agf discovers sessions by reading the files an agent already writes locally — no
plugin binaries, no config. Adding a new agent (harness) is a self-contained
change: implement one scanner and register it in a handful of match arms. The
compiler enforces most of the wiring — every match self { Agent::… } becomes
non-exhaustive until you add the new arm — but two registrations live in plain
arrays/Vecs the compiler can't check, so they're called out below.
Community integrations are welcome even if the agent isn't a "main" harness; keep them scoped like the existing ones (read-only scan, deletion limited to the validated session).
Say the new agent is Foo, CLI foo, sessions under ~/.foo/sessions/.
-
src/model.rs— theAgentenum. AddFooand fill in every arm the compiler now flags:Display,color(),all()(plain array — add it),cli_name(),resume_cmd(),new_session_cmd(). Addresume_mode_options()only iffoohas permission/approval flags.resume_cmd()must quote the id via the passedshell(shell.quote(session_id)), never raw'{session_id}'— session ids come from parsed files and may contain shell metacharacters.
-
src/config.rs— add afoo_sessions_dir()(or_dir()) helper returning the on-disk location. Usedirs::for platform-correct paths, andAgfError::NoDataDir(notNoHomeDir) when adirs::data_dir()lookup fails. Then add thedata_sources()arm: the paths whose mtime decides cache freshness.- It must cover every file your scanner reads, not just its primary index —
a source you omit can change without invalidating the cache, so
agfkeeps serving a stale payload. Within that constraint keep it narrow: each path is stat-walked on every launch.
- It must cover every file your scanner reads, not just its primary index —
a source you omit can change without invalidating the cache, so
-
src/scanner/foo.rs— implementpub fn scan() -> Result<Vec<Session>, AgfError>.- Return a
Sessionper resumable session. Settimestamp(Unix ms) to the last-activity time (file mtime or the newest in-file event) — not creation time — so time sort is consistent with the other agents. - Bound reads on large transcripts with the shared
read_head_tail/ bounded-read helpers inscanner/mod.rs; never slurp multi-MB logs whole. - Skip malformed lines, don't panic on bad input.
- Register the module in
src/scanner/mod.rs: addpub mod foo;and anAgent::Foo => foo::scan()arm inscan_agent(). Do not erase errors into empty success; stale cache rows are retained when a scanner fails.
- Return a
-
src/cache.rs— no agent-specific dispatch is required; workers call the commonscanner::scan_agent(). EnsureSessioncache fields round-trip. BumpCACHE_VERSIONonly if the cached payload shape can change within a single released package version (a new agent key alone doesn't require it — theagf_versionstamp forces a rescan on upgrade). -
src/delete.rs— add theAgent::Fooarm indelete_agent_sessions(). Default toUnsupportedand exclude Foo fromAgent::supports_delete()when the provider coordinates databases, sidecars or active sessions. Antigravity and Gemini are examples. Only implementdelete_foo_sessions(ids: &HashSet<&str>)after validating the complete upstream deletion contract.- It receives a batch: bulk delete does one pass per agent, so do the walk
or open the database once and act on every id in
ids. - Scope deletion to validated sessions — match by id in file content or by a
validated directory name.
is_safe_session_idalready rejects traversal, but if you join an id onto a path, re-check it against your own id format first (seedelete_yolop_session_from). - Bound what you read: the id lives in a header, so use
read_first_line/read_head_linesrather than slurping transcripts. - Add a test proving a sibling session survives.
- Never turn database or file-removal errors into successful deletion.
- It receives a batch: bulk delete does one pass per agent, so do the walk
or open the database once and act on every id in
-
Tests + docs — unit-test the scanner against a fixture session, add a
resume_cmdtest, add a row to the Supported agents and storage tables inREADME.md, and add the CLI name to the Requirements list.
cargo test --locked
cargo clippy --locked --all-targets --all-features -- -D warnings
cargo fmt --all -- --check
# Real-data smoke test (scans all agents regardless of install):
AGF_DEBUG=1 cargo run -- list --agent foo --format jsonAn explicit --agent foo scans only Foo when its cache entry is stale, so you
can verify it against a fixture $HOME without installing the other CLIs:
HOME=/tmp/agf-fixture cargo run -- list --agent foo --format json