diff --git a/AGENTS.md b/AGENTS.md index 81d1579..c06d097 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,14 +1,18 @@ # AGENTS.md This file provides guidance to AI coding agents (Claude Code, Codex, OpenCode, -etc.) when working with code in this repository. +etc.) when working with code in this repository. Module-level rules live in +nested AGENTS.md files: `src/adapters/`, `src/db/`, `src/tui/`, `extensions/`, +`skills/`, and `website/` — read the one for the directory you are changing. ## Overview Recall is a Rust CLI/TUI application that indexes AI coding sessions from local tools (Claude Code, Codex, OpenCode, Cursor, ...) into one SQLite database for full-text/semantic search, usage tracking, JSONL export/import, session -sharing, and resume. It is an application crate, not a reusable Rust library. +sharing, and resume. The repo is a Cargo workspace: the core application crate +at the root plus official extension crates under `extensions/`. Nothing is +published to crates.io — these are application binaries, not library crates. ## Commands @@ -24,11 +28,15 @@ cargo test integration::eval_harness # eval harness ``` `make check` must pass before push. CI runs exactly the same command — there is -no CI-only logic. +no CI-only logic. The gate uses `--workspace`, so it covers extension crates +too. Build a single extension with `cargo build -p recall-`. -Releases use cargo-release: `make release-patch` is a dry run; add `EXECUTE=1` -to bump, commit, tag, and push. The `v*` tag triggers the GitHub Actions binary -build. One-time setup: `git config core.hooksPath .githooks` enables the DCO +Core releases use cargo-release: `make release-patch` is a dry run; add +`EXECUTE=1` to bump, commit, tag, and push. The `v*` tag triggers the GitHub +Actions binary build. Extensions release independently: bumping an extension's +package version in a PR is the release intent — after merge, a workflow creates +the `recall--v` tag, builds binaries, and regenerates the +catalog. One-time setup: `git config core.hooksPath .githooks` enables the DCO signoff hook. ## Architecture @@ -51,8 +59,21 @@ Data flow: source adapters → sync → SQLite → search → CLI/TUI. - `src/tui/` — ratatui app: app state, event handling, layout, background search worker, share/usage/viewing state, and `ui/` rendering modules. - `src/share/` — renders sessions to HTML and publishes share assets. -- `src/cli.rs` — clap subcommands dispatching to the modules above. +- `src/cli.rs` — clap subcommands dispatching to the modules above; unknown + subcommands fall through to extension dispatch. +- `src/extension.rs` — extension host: `recall ` runs the managed + `recall-` binary; `recall ext install/list/remove/upgrade` manages + official extensions from the GitHub Pages catalog. +- `extensions/` — official extension crates (workspace members, independently + versioned). Extensions consume core only through the stable CLI JSON/JSONL + protocol (`recall ... --format json`, `recall export`), never the SQLite file + or Rust internals. `docs/extensions.md` is the design and contract reference. +- `skills/` — agent skill bundles. `skills/recall/` is embedded into the core + binary via `include_bytes!` and installed by `recall skill install` — editing + it changes the binary. `skills/reflect/` pairs with the reflect extension. - `website/` — Next.js docs site (pnpm), independent of the Rust crate. + `website/public/extensions/catalog.json` is generated release state — never + hand-edit version entries. ## Boundaries @@ -69,5 +90,11 @@ Data flow: source adapters → sync → SQLite → search → CLI/TUI. - Adapter rules (see DEVELOPMENT.md): return `Ok(vec![])` when a tool is not installed; open external databases read-only; extract text content only; `tracing::warn!` and skip on recoverable parse errors. +- Core/extension boundary: anything that writes the Recall index, data plane, + or schema migrations belongs in core; extensions only consume the stable CLI + protocol. For that protocol, stdout carries only the requested JSON/JSONL, + progress and warnings go to stderr, published fields must not be removed or + renamed, and breaking changes bump `protocol_version`. The SQLite schema is + explicitly not a public contract. - `.local/` is local scratch and external comparisons, not an architecture source. diff --git a/extensions/AGENTS.md b/extensions/AGENTS.md new file mode 100644 index 0000000..bf58491 --- /dev/null +++ b/extensions/AGENTS.md @@ -0,0 +1,36 @@ +# extensions/ + +Official Recall extensions: standalone workspace binary crates named +`recall-`, dispatched via `recall `. `docs/extensions.md` is the +full design and contract reference; `recall-probe` is the minimal reference +implementation. + +## Contract + +- Extensions consume core only through the stable CLI JSON/JSONL protocol + (`recall info --format json`, `recall session list/show --format json`, + `recall search --format json`, `recall export`). Never read `recall.db` + directly and never depend on the `recall` crate — Rust internals and the + SQLite schema are explicitly unstable. +- Every extension must answer `recall- --recall-extension-manifest` with + JSON containing `name`, `version`, `protocol`, and `min_recall`. +- stdout is machine output only; progress and warnings go to stderr. Non-zero + exit means failure. +- Do not add `capabilities` or `permissions` manifest fields — unenforceable + for native binaries. + +## Adding an extension + +1. Create `extensions/recall-/` and add it to `workspace.members` in the + root `Cargo.toml` (keep `default-members` as `["."]`). +2. Binary name must be `recall-`; the dispatch name is ``. +3. `make check` covers the whole workspace; build alone with + `cargo build -p recall-`. + +## Releasing + +Bumping the extension's package version in a PR is the release intent. After +merge, CI creates the `recall--v` tag, builds target archives, +and regenerates `website/public/extensions/catalog.json`. Extension versions +are independent of Recall core versions — never couple a core release to an +extension release unless a protocol change forces it. diff --git a/src/adapters/AGENTS.md b/src/adapters/AGENTS.md new file mode 100644 index 0000000..334f16c --- /dev/null +++ b/src/adapters/AGENTS.md @@ -0,0 +1,42 @@ +# src/adapters/ + +One adapter per AI coding tool. This is the most-extended surface in Recall — +DEVELOPMENT.md walks through adding an adapter; this file states the rules that +apply when working here. + +## Contract + +- The `SourceAdapter` trait in `mod.rs` is the authoritative contract, not the + DEVELOPMENT.md example. `id()`, `label()`, `scan()`, and `resume_command()` + are required; `scan_summary()`, `scan_for_sync()`, `prune()`, + `app_command()`, and `usage_parser_version()` are optional overrides. +- Register new adapters in `all_adapters()` in `mod.rs`. Registration alone + wires the adapter into sync, search, the TUI source filter, and the CLI + `--source` flag. No schema change is needed — `sessions.source` is a value, + not a column per tool. +- `events.rs`, `file_scan.rs`, and `sync_state.rs` are shared helpers, not + adapters. Prefer `file_scan::run_file_scan_with_options` over hand-rolled + mtime tracking for file-based sources. +- Usage and session events are extensions on the same `RawSession` + (`with_usage`, `with_events`), each with its own parser version. Bump the + parser version when parsing changes — that is what triggers backfill for + sessions whose files did not change. +- `source_supports_event_backfill()` in `mod.rs` is a hardcoded source list. + An adapter that starts emitting events must be added there, or usage + dashboards will not pick it up. + +## Rules + +- Tool not installed → return `Ok(vec![])`, never an error. +- Open external tool databases read-only (`SQLITE_OPEN_READ_ONLY`). +- Extract text content only; skip tool calls, images, and internal metadata. +- Recoverable parse error → `tracing::warn!`, skip the session, continue. +- Timestamps are Unix milliseconds. + +## Verify + +```bash +make check +cargo run -- sync -v # should log the new source with a session count +cargo run -- search "query" --source +``` diff --git a/src/db/AGENTS.md b/src/db/AGENTS.md new file mode 100644 index 0000000..54da7f4 --- /dev/null +++ b/src/db/AGENTS.md @@ -0,0 +1,25 @@ +# src/db/ + +rusqlite storage for `recall.db`, split by domain: one store module per record +family (`session_store`, `event_store`, `usage_store`, `project_store`, +`semantic_store`, `skill_audit_store`), `search.rs` for FTS5 + sqlite-vec +queries, `schema.rs` for migrations, `store.rs` for the `Store` handle. + +## Migrations + +- Schema changes are append-only: add a `migrate_vN` function in `schema.rs`, + chain it in `init()`, and bump `SCHEMA_VERSION`. Versioning uses + `PRAGMA user_version`. +- Never edit a shipped `migrate_vN` — existing databases have already applied + it. Fix mistakes in a new migration. +- The SQLite schema is not a public contract. Extensions and third parties + consume the CLI JSON protocol; schema may change in any release without a + `protocol_version` bump. + +## Rules + +- Full-text search is FTS5; vector search is sqlite-vec (registered via + `register_sqlite_vec()` before any connection opens). +- Tests use `Store::open_in_memory()` — no fixture database files. +- Keep store modules single-domain. Cross-domain reads belong in `search.rs` + or the caller, not in a store that owns another store's tables. diff --git a/src/tui/AGENTS.md b/src/tui/AGENTS.md new file mode 100644 index 0000000..80f192e --- /dev/null +++ b/src/tui/AGENTS.md @@ -0,0 +1,28 @@ +# src/tui/ + +ratatui application. Module roles: + +- `runner.rs` — terminal lifecycle and the event loop. +- `app.rs` — the `App` state struct; all state transitions happen here or in + `event.rs`. This is the state machine; define transitions before editing. +- `event.rs` — key/input handling that mutates `App`. +- `search_state.rs`, `share_state.rs`, `usage_state.rs`, `viewing_state.rs` — + per-concern state, kept out of `app.rs` when self-contained. +- `search_worker.rs` — background search thread connected by mpsc channels. +- `layout.rs`, `text_layout.rs` — geometry and text wrapping. +- `ui/` — rendering only. Draw functions read state; they must not mutate it. + +## Search worker protocol + +Searches run off the UI thread in two phases (`Text`, then `Hybrid` when the +semantic index is ready). Every `SearchRequest` carries an id; `App` keeps +`active_search_id` and drops any response whose id or query no longer matches +(`app.rs`). Preserve this discipline when adding async work: tag requests, +compare on receipt, never block the event loop on a channel. + +## Rules + +- New popups, panes, or key handling follow the existing split: state in a + `*_state.rs` module or `App`, input in `event.rs`, drawing in `ui/`. +- Long-running work goes through a worker thread and messages, never inline in + the event loop. diff --git a/website/AGENTS.md b/website/AGENTS.md new file mode 100644 index 0000000..10a30fd --- /dev/null +++ b/website/AGENTS.md @@ -0,0 +1,18 @@ +# website/ + +Next.js + Fumadocs documentation site, statically exported to GitHub Pages. +Independent of the Rust workspace — use pnpm, never cargo or npm. + +```bash +pnpm dev # development server +pnpm build # static export +pnpm lint # eslint +pnpm types:check # fumadocs-mdx + next typegen + tsc --noEmit +``` + +## Rules + +- `public/extensions/catalog.json` is generated release state written by the + extension release workflow. Never hand-edit version entries — it must + contain real asset URLs and SHA-256 checksums. +- Docs content lives in `content/`; keep it aligned with actual CLI behavior.