Skip to content

CLI Reference

GiulioDER edited this page Jul 23, 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

Four flags apply to every command:

Flag Why you'd use it
--dsn Point at a different Postgres. Falls back to RECALL_DSN.
--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 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.

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.

--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.

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.

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