Skip to content

CLI Reference

giulio d'erme edited this page Aug 10, 2026 · 2 revisions

CLI Reference

python -m recall.cli <command> [options]

Flag values and defaults are not repeated here — run python -m recall.cli <command> --help for those. This page covers what each command is for, and the workflows they compose into.

Global options

The main global flags:

Flag Why you'd use it
--serving-dsn Runtime Postgres credential. Falls back to RECALL_SERVING_DSN, then deprecated RECALL_DSN.
--migration-dsn Schema-owner credential for schema apply. Falls back to RECALL_MIGRATION_DSN.
--dsn Deprecated serving DSN alias kept for older local workflows.
--embedder fastembed (local, default) or hashing (fully offline, deterministic — for tests and offline work, not for retrieval quality).
--table Operate on a different table. Use a throwaway name to keep an experiment out of your real memory index.
--tenant Scope every operation to one tenant namespace.

⚠️ --tenant matters most for forget. Every command is tenant-scoped, and forget deletes nothing outside the tenant it is pointed at — so an erasure request against another tenant needs this flag explicitly. Silently deleting from the wrong tenant and silently deleting nothing are both bad; the flag makes the choice visible.

The CLI also prints a loud warning to stderr if your serving DSN carries the published default credentials against a non-local host. → Tenancy-and-Auth

The commands

index — put a corpus into memory

Walks a path, chunks what the glob matches, embeds it and writes it to the store. Markdown by default; passing a code glob switches to code-aware chunking automatically.

Re-running is cheap and expected: unchanged files are skipped by content hash, and files that have vanished from disk are pruned from the index.

The prune guard. If a re-index would drop a large fraction of the sources under a root, it raises and deletes nothing. That is how a missing corpus — wrong path, unmounted volume, half-finished checkout — stops being indistinguishable from a deleted one. Once you have confirmed the files really are gone, re-run with --allow-prune.

In production mode, local filesystem indexing is refused. Build and promote immutable generations instead; the production control plane is covered in docs/ENTERPRISE_RETRIEVAL.md.

If this goes wrong: an index run that reports far fewer files than you expect is usually a glob problem, not a corpus problem — the default glob is markdown-only. A run that refuses with a prune error is the guard doing its job; check the path before overriding it.

search — query the index

Runs the full pipeline and prints each hit with its verdict, calibrated confidence, cosine and provenance — or an explicit abstention with a reason.

Strict trust is the default. A production search refuses when the active generation or its certified calibration cannot be trusted. For ad hoc local folders, set RECALL_TRUST_MODE=development; the output is degraded context, not a production trust claim.

--entail adds the opt-in near-miss judge, which demotes hits that are semantically close but do not actually answer the query. It needs the entail extra and downloads a judge model on first use. It is off by default because it degrades far-gap detection while improving near-miss detection; the two stages stack rather than substitute. → The-Trust-Layer

If this goes wrong: results that look plausible but stale usually mean supersession edges are not resolving — run lint. Results marked uncalibrated mean no usable calibration file was found for this embedder; see calibrate below.

demo — see it work in one command

Indexes the shipped corpus/ and runs sample queries, including the case the whole project turns on: a stale memory with the highest cosine in the result, correctly demoted below its successor, and an unanswerable query that returns an explicit abstention.

The fastest way to confirm your setup works, and the fastest way to show someone what this does.

code — index RE-call's own source

Indexes the library's own code and runs sample code queries. A quick demonstration that the chunking and retrieval work on code as well as prose.

schema — inspect and apply ordered migrations

schema status and schema plan are read-only. schema apply runs the ordered SQL migrations, records checksums in recall_schema_migrations, and must be run with --migration-dsn or RECALL_MIGRATION_DSN.

python -m recall.cli --serving-dsn "$RECALL_SERVING_DSN" schema --dim 384 status
python -m recall.cli --migration-dsn "$RECALL_MIGRATION_DSN" schema --dim 384 apply

schema grants --role recall_server prints the runtime grants for the serving role and executes nothing. Use --enterprise when the generation control plane is enabled. Generate this SQL rather than copying a list from the wiki; it is tied to the actual schema objects the package ships.

lint — check the supersession graph

Pure filesystem check: no database, no embedder, fast enough for CI. Catches the errors that break the trust layer's correctness:

  • an edge naming a file that does not exist in the corpus;
  • a document claiming to supersede itself;
  • a cycle in the chain;
  • an edge naming a basename that more than one document carries (which fails closed at read time, so it is worth catching earlier);
  • malformed validity dates (the indexer would refuse these anyway).

--semantic adds the opposite check — the missing edge. The static lint verifies edges that exist and is structurally blind to the one that hurts most: a new memo that is really about a prior settled decision but never declares it. That relation is not in the frontmatter, it is in the meaning, so no syntactic check can see it. This one queries the index with each memo's text and flags high-similarity closed decisions it does not reference. Needs the database and an embedder, hence opt-in.

--fix proposes frontmatter edges for prose closure markers whose target is provable. It is a dry run by default — it prints the plan and changes nothing. --apply writes them.

Read The-Trust-Layer before relying on --fix: on the reference corpus it proposes almost nothing, deliberately, and that is the finding rather than a bug. Narrating versus declaring, part versus whole, augmenting versus replacing are invisible to a pattern and obvious to the author. Treat it as a reviewing aid.

check — the write-time gate

The complement to lint --fix, and the more useful of the two. Run it on the memo you are writing, and it surfaces candidate supersession edges while the person who knows the answer is still in the room.

--strict exits non-zero when a memo needs an edge, which is what makes it usable as a pre-commit hook.

Note the deliberate inversion: --fix refuses everything it cannot prove, because it writes unattended. check surfaces every candidate it can find, because a human is right there to pick one. A false candidate costs a glance; a missing one costs the edge.

forget — permanent erasure

Permanently deletes the indexed chunks for the named sources. This is the right-to-erasure path.

It previews by default and changes nothing without --yes. It is irreversible, and it is also invoked from scripts, so a typo or an unattended run must not silently wipe a corpus. Sources are named exactly as stored — the source field in search output.

Deletion is tenant-scoped; see the --tenant note above.

calibrate — fit the abstention threshold

Fits the abstention threshold for this embedder against a labelled set of queries (each marked answerable or not) and writes it to a calibration file.

Worth doing because a fixed cosine threshold does not transfer across embedders — each model places its scores in a different regime. An uncalibrated system falls back to a default and flags every result as uncalibrated, which works but leaves accuracy on the table.

⚠️ Re-run it after switching embedders (a calibration fitted for another model is refused outright, by design) and after a re-index, since HNSW index builds are nondeterministic enough to move the operating point. Calibrate against a few hundred labelled queries if the threshold matters — a handful of samples buys margin, not stability. → The-Trust-Layer

Workflows these compose into

Daily memory maintenance

python -m recall.cli lint ./notes          # graph health, no DB
python -m recall.cli index ./notes         # incremental; unchanged files skipped

Pre-commit hook on a memory corpus

python -m recall.cli check ./notes/new-memo.md --strict

Catches the missing supersession edge at the only moment it can be answered cheaply.

After changing embedder

python -m recall.cli --embedder <new> index ./notes    # re-embed
python -m recall.cli --embedder <new> calibrate <labelled-queries.json>

Both are required. The old vectors are not reused (the cache key includes the embedder), and the old calibration will be refused, not silently applied.

Production note: v1 serving resolves calibration from PostgreSQL by tenant and active generation. Legacy calibration files are import-only evidence; they are not automatically selected by the MCP server.

setup — guided local setup

Runs the interactive local setup wizard and writes the selected development settings to .env. It reuses the same environment variables documented in Configuration-Reference rather than creating a second configuration surface.

Trying something without touching your real index

python -m recall.cli --table scratch index ./experiment

See also: Configuration-Reference for the environment variables behind these defaults · Python-API-and-MCP for the same operations from code · The-Trust-Layer for what the verdicts in search output mean.

Clone this wiki locally