Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 34 additions & 7 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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-<name>`.

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-<name>-v<version>` tag, builds binaries, and regenerates the
catalog. One-time setup: `git config core.hooksPath .githooks` enables the DCO
signoff hook.

## Architecture
Expand All @@ -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 <name>` runs the managed
`recall-<name>` 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

Expand All @@ -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.
36 changes: 36 additions & 0 deletions extensions/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# extensions/

Official Recall extensions: standalone workspace binary crates named
`recall-<name>`, dispatched via `recall <name>`. `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-<name> --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
Comment thread
samzong marked this conversation as resolved.
exit means failure.
- Do not add `capabilities` or `permissions` manifest fields — unenforceable
for native binaries.

## Adding an extension

1. Create `extensions/recall-<name>/` and add it to `workspace.members` in the
root `Cargo.toml` (keep `default-members` as `["."]`).
2. Binary name must be `recall-<name>`; the dispatch name is `<name>`.
3. `make check` covers the whole workspace; build alone with
`cargo build -p recall-<name>`.

## Releasing

Bumping the extension's package version in a PR is the release intent. After
merge, CI creates the `recall-<name>-v<version>` 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.
42 changes: 42 additions & 0 deletions src/adapters/AGENTS.md
Original file line number Diff line number Diff line change
@@ -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 <id>
```
25 changes: 25 additions & 0 deletions src/db/AGENTS.md
Original file line number Diff line number Diff line change
@@ -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.
28 changes: 28 additions & 0 deletions src/tui/AGENTS.md
Original file line number Diff line number Diff line change
@@ -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.
18 changes: 18 additions & 0 deletions website/AGENTS.md
Original file line number Diff line number Diff line change
@@ -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.