---
title: "CLI Reference"
image: "https://recall.edheltzel.codes/og.png"
---

> Documentation Index
> Fetch the complete documentation index at: https://recall.edheltzel.codes/llms.txt
> Use this file to discover all available pages before exploring further.

# CLI Reference

The `recall` command is the primary interface for Recall. Subcommands operate on the SQLite database at `~/.agents/Recall/recall.db`, except `recall jev`, which scores one candidate and does not open the database.

New to Recall? Start with the [Getting Started](https://recall.edheltzel.codes/docs/) first-run tutorial.

---

## Search

```bash
recall "query"                          # Hybrid search (keyword + semantic, default)
recall "query" -k                       # Keyword only (FTS5)
recall "query" -v                       # Semantic search (requires Ollama)
recall search "query"                   # FTS5 search with options
recall search "query" -t decisions      # Hard-filter to decisions only
recall search "query" --bias-type decisions # Prefer decisions, still show other matching tables
recall search "query" -p myproject      # Filter by project
recall search "query" --show-provenance # Show provenance for every result
recall search "query" --include-duplicates # Include records marked by recall dedup
recall semantic "query"                 # Semantic search (explicit)
recall hybrid "query"                   # Hybrid search (explicit)
```

The default search mode is hybrid: it combines FTS5 keyword matching with vector similarity, then merges results by relevance score. Use `-k` when you want exact phrase or operator matching without semantic expansion. Use `-v` when you want conceptually related results even if the exact words differ. `-k`, `-v`, and `recall search` stay local even when `~/.agents/Recall/config.json` names a query harness. `recall hybrid` and an unqualified `recall "query"` use that harness when the `query` key is present. A missing key stays hybrid. See [architecture](https://recall.edheltzel.codes/docs/architecture/#harness-steps).

Local semantic and hybrid search skip stored vectors whose length differs from the query embedding and print one warning with the skipped count. Compatible vectors still contribute to ranking. If every stored vector is incompatible, semantic search falls back to keyword search, while hybrid search returns its keyword matches. The warning recommends [`recall embed rebackfill`](#embeddings) to regenerate incompatible embeddings.

For keyword search, `-t/--table` and `--bias-type` solve different problems:

- `-t decisions` is a **hard filter**: only decisions can appear.
- `--bias-type decisions` is a **soft boost**: matching decisions rank higher, but matching LoA entries, learnings, breadcrumbs, and messages can still appear.

Allowed values: `messages`, `loa`, `decisions`, `learnings`, `breadcrumbs`.

```bash
recall search "database choice" -t decisions          # only decisions
recall search "database choice" --bias-type decisions # decisions first, broader context preserved
recall "database choice" -k --bias-type decisions     # default command, keyword-only with soft bias
```

FTS5 supports boolean operators and prefix matching:

- `auth AND token` — both terms must appear
- `config OR setup` — either term
- `config NOT docker` — exclude a term
- `auth*` — prefix match (authz, authentication, etc.)
- `"vpn config"` — exact phrase

By default, search output stays quiet about [Record Provenance](#record-provenance) when a record carries a known value, and visibly flags records whose provenance is unknown (legacy rows that predate the provenance column). Pass `--show-provenance` to display the provenance of every result.

Records marked as duplicates by [`recall dedup`](#dedup) are hidden from every search path (keyword, semantic, hybrid) by default — the records and their lineage remain in the database. Pass `--include-duplicates` to show them.

---

## Capture

### Ambient capture

Public door for harness adapters. Send raw text; core ranks and stores. No SDK. Guide: [Capture adapter](https://recall.edheltzel.codes/docs/reference/capture-adapter/). Hidden `recall host-hook` is deprecated for ambient writes. It remains only for Codex `SessionStart` injection.

```bash
recall capture --contract 1 --harness acme-agent --event turn_end --text "raw turn"
recall capture --contract 1 --harness omp --event session_end --text-file turn.txt
printf '%s\n' '{"contract":1,"harness":"acme-agent","event":"turn_end","text":"raw turn"}' | recall capture
```

`--contract` defaults to `1`. `--harness` is `^[a-z][a-z0-9-]{0,63}$`; see [built-in and reserved harness IDs](https://recall.edheltzel.codes/docs/reference/capture-adapter/#harness-ids). `--event` is `turn_end` or `session_end`. Optional: `--session-id`, `--cwd`, `--project`. Text is `--text`, `--text-file`, or stdin when those flags are absent and another flag selected the flag form. With no flag fields, stdin is the JSON twin (`contract`, `harness`, `event`, `text`, `session_id`, `cwd`, `project`). Invalid input exits non-zero and writes nothing.

Prove a write with `recall search "<unique phrase>" -t messages`.


### Session Dump

```bash
recall dump "Session Title"             # Import session + capture LoA entry
```

Combines `recall import` and Curated LoA capture in one step. Run this at the end of every session to persist the conversation and run the `fabric` Extractor. The title should describe what was accomplished in the session. `--skip-fabric` writes a basic summary instead.

Without `--stdin`, Recall discovers native transcripts in Claude Code, OpenCode, then Pi order, followed by registered session sources. For omp's live-session route and discovery restriction, see the [`recall_dump` contract](https://recall.edheltzel.codes/docs/integrations/omp/#explicit-dump-recall_dump-tool).

`--stdin` reads up to 25 MiB of contract 1 JSON for host extensions that hold the live transcript: `contract: 1`, `harness`, `session_id`, optional `cwd`/`project`, and `messages` with user/assistant `role`, nonblank text `content`, and optional `timestamp`. Malformed input is rejected before writes. `-p` overrides the payload's project; otherwise Recall uses its `project`, then the origin repository name or directory basename from `cwd`, then `harness` if `cwd` is absent.

Both CLI dumps and MCP `memory_dump` scrub each message's content with the shared write-safety guard before snapshot matching, storage, and summary or extraction. Known credential patterns become visible `[REDACTED:<kind>]` markers, invisible/bidi controls are removed, and incoming messages left blank are dropped. This does not detect every secret; follow the [credential-handling rule](https://recall.edheltzel.codes/docs/reference/mcp-tools/#memory_add) before dumping sensitive conversations.

### Library of Alexandria (LoA)

```bash
recall loa write "Session Title"              # Capture messages since last LoA entry
recall loa write "VPN Config" -p infra        # Tag with project
recall loa write "VPN Part 2" -c 1            # Continue from previous entry (chain)
recall loa write "Auth System" -t "auth"      # Add tags
recall loa list                               # List recent entries
recall loa show 1                             # View extract body for entry #1
recall loa quote 1                            # View raw source messages for entry #1
```

LoA is the primary knowledge capture mechanism. Raw transcripts are high-noise; Curated LoA entries contain `fabric` Extractor output, message lineage, continuation chains, and project context. Chaining (`-c <id>`) links a new entry to a prior one, forming a thread of related sessions. Automatic-capture LoA is written by session extract, not this command. Extractor config: [architecture](https://recall.edheltzel.codes/docs/architecture/#extractor-config).

The omp extension invokes `recall loa omp-end <sessionId>` for [background curation](https://recall.edheltzel.codes/docs/integrations/omp/#capture-contract).

### Decisions

```bash
recall add decision "Use TypeScript over Python" --why "Type safety, team preference" -p myproject
recall add decision "Use SQLite for storage" -w "Lightweight, zero-config" --confidence high
```

Records an architectural or strategic decision with rationale. Decisions carry a status field (`active`, `superseded`, `reverted`) so you can track when a decision is overturned and why.

### Decision Lifecycle

Manage decision status transitions.

```bash
# List all decisions (all statuses)
recall decision list
recall decision list --status active
recall decision list --status superseded
recall decision list --project my-project --limit 10

# Mark a decision as superseded (replaced by newer)
recall decision supersede 42

# Mark a decision as reverted (was wrong, rolled back)
recall decision revert 42
```

### Learnings

```bash
recall add learning "Port conflict on 4000" "Kill process or change port" --prevention "Use dynamic port allocation"
```

Captures a problem-solution pair. The first argument is the problem, the second is the solution. The optional `--prevention` flag records how to avoid the issue in the future.

For learning-tag scrubbing and secret-handling requirements shared with `memory_add`, see the [explicit-write reference](https://recall.edheltzel.codes/docs/reference/mcp-tools/#memory_add).

### Breadcrumbs

```bash
recall add breadcrumb "User prefers dark mode in all UIs" -p myproject -i 8
```

Stores a freeform observation or preference. The `-i` flag sets importance on a scale of 1-10 (default: 5). Higher importance ranks the breadcrumb earlier in session-start L1, not in search.

---

### Jev score

```bash
recall jev "Use SQLite, not a second database"   # Score one candidate
recall jev --kind decision -p recall "Use SQLite" # Optional kind and project
printf '%s\n' "hi" | recall jev                   # Read the candidate from stdin
```

Prints one JSON object with `choice` (`keep`, `demote`, or `drop`), `probabilities`, and `confidence`. Does not open the database and does not write a row. A non-blank `JEV_RECALL_KEY` in the process wins; otherwise Recall reads it from `$HOME/.env`. If neither provides a key, a terminal prints [https://console.typesafe.ai/keys](https://console.typesafe.ai/keys) and prompts for one run. The paste is not saved or printed. A non-interactive run prints that URL and exits 1. Ingest uses the same resolution without prompting; no resolved key skips Jev and still writes parsed rows. Setup is in [Score one memory item with Jev](https://recall.edheltzel.codes/docs/score-with-jev/).

## View

```bash
recall loa list                         # Recent LoA entries
recall loa show 1                       # Extract body for entry #1
recall loa quote 1                      # Raw source messages for entry #1
recall recent                           # Recent records across all tables
recall recent decisions                 # Recent decisions only
recall show decisions 5                 # Full record for decision #5
recall stats                            # Database statistics
recall doctor                           # Health check all subsystems
```

`recall recent` accepts any table name: `decisions`, `learnings`, `breadcrumbs`, `loa`, `sessions`, `docs`, `telos`.

`recall show` displays the full record for a single row. The first argument is the table name, the second is the row ID.

---

## Import

```bash
recall import --dry-run                 # Preview session imports without writing
recall import --yes -v                  # Import all sessions from ~/.claude/projects/
recall import-legacy --yes              # Import DISTILLED.md extracts as LoA entries
recall docs import --dry-run            # Preview document imports
recall docs import --yes                # Import standalone markdown documents
recall telos import --dry-run           # Preview TELOS imports
recall telos import --yes -u            # Import TELOS framework (update existing)
recall telos list -t goal               # List TELOS entries of type: goal
recall telos show GOALS                 # Show the GOALS entry by code
recall telos search "purpose"           # Search TELOS entries
recall telos search "purpose" -t goal   # Same search, one type
recall docs list                        # List imported documents
recall docs search "vpn"                # Search imported documents
recall docs show 3                      # Show document #3
recall import-conversations ./export.json --dry-run
recall scrub-archive --dry-run          # Preview legacy archive redaction
```

`recall import` scans `~/.claude/projects/` for JSONL session files and imports any not yet recorded. Use `--dry-run` first to confirm scope. The `-v` flag enables verbose output.

`recall import-legacy` converts entries from `DISTILLED.md` and `HOT_RECALL.md` (the previous manual memory format) into LoA entries. `-s` / `--source` limits that to `distilled` or `hot_recall`; the default is `all`.

`recall docs` manages standalone reference documents (markdown files) that should be searchable alongside session memory.

`recall telos` manages TELOS framework entries (`problem`, `mission`, `goal`, `challenge`, `strategy`). The `-u` flag updates existing entries rather than skipping them on reimport. `recall telos search` takes a query, optional `-t` type, and `-l` (default 10).

`recall docs list` lists imported documents. `recall docs search` searches them (`-l`, default 10). `recall docs show <id>` prints one document.

`recall import-conversations <path>` imports a Claude.ai, ChatGPT, or Slack JSON export. `--format` is `auto` (default), `claude-ai`, `chatgpt`, or `slack`. `--dry-run` previews without writing. `--no-extract` stores raw messages and skips the curated Extractor. `-p` overrides the project name.

`recall scrub-archive` rewrites secrets and invisible or bidi characters in `~/.claude/MEMORY` and per-session `transcript.md` files under `~/.claude/sessions`. It writes unless `--dry-run` is set, and backs up each changed file first. `-v` also lists clean and missing surfaces.

---

## Embeddings

```bash
recall embed stats                      # Show stored embedding counts and storage size
recall embed backfill -t loa            # Generate missing embeddings for LoA entries
recall embed backfill -t decisions      # Generate missing embeddings for decisions
recall embed rebackfill                 # Regenerate existing embeddings with the current model
recall embed reindex                    # Rebuild the sqlite-vec index from stored embeddings
```

Embeddings are generated via Ollama and stored in the database for vector similarity search. `recall embed stats` groups stored embedding counts by source table and model and reports their total storage size. Run `recall embed backfill` after importing a large batch of records to generate missing embeddings.

Supported tables for backfill: `loa`, `decisions`, `learnings`, `messages` (assistant messages only). Each run processes up to `--limit` records (default: 100).

`recall embed rebackfill` regenerates existing embeddings with the current model before replacing them in one transaction, then rebuilds the vector index when available. If embedding generation fails, the stored embeddings remain unchanged. Embeddings whose source is no longer available or whose text is too short are removed during replacement.

`recall embed reindex` rebuilds the sqlite-vec index from stored embeddings. If sqlite-vec is unavailable, it prints that and leaves search on FTS5 plus brute-force cosine.

---

## Identity & Onboarding

```bash
recall start                            # Render L0/L1 session-start memory (markdown)
recall start --format cursor            # Cursor sessionStart wrapper: { "additional_context": "..." }
recall onboard                          # Interactive L0 identity interview (writes identity.md)
recall onboard --print --yes            # Preview rendered markdown without writing
recall onboard --project                # Write project-local (./.atlas-recall/identity.md)
recall onboard --out /path/identity.md  # Write to an explicit path
```

`recall start` is the shared L0/L1 inject renderer. Claude SessionStart and Codex
SessionStart call the same assembler (published caps: L0 1200 chars, L1 6000,
total 8000). Empty DB + missing `identity.md` exits 0 with a short degrade line.
Cursor inject uses `--format cursor` (`{ additional_context }`); Codex keeps
`hookSpecificOutput.additionalContext`. The `templates/cursor/` sessionStart
command stays unqualified `recall start --format cursor`. Cursor.app GUI PATH
typically lacks `~/.bun/bin`, so that hook is a no-op until `recall` is on the
app PATH. CLI Cursor / a shell where `recall` resolves is fine. Durable GUI
PATH / `recall start --format cursor` accuracy is pending FM-321/327; this
section describes the as-built unqualified command, not a wrapper or
machine-specific prefix.

`recall onboard` creates the L0 tier that `recall start` injects at the top of every
session. Precedence for the output path: `--out` > `RECALL_IDENTITY_PATH` env var >
`--project` > the global identity. The global resolver preserves an existing
user-owned `~/.claude/MEMORY/identity.md`; otherwise it selects the canonical
file under the Recall install root, `~/.agents/Recall/MEMORY/identity.md`. If a
file already exists, the command asks for confirmation and writes a `.bak` copy
before overwriting.

The renderer warns when output exceeds `MAX_L0_CHARS=1200` — `recall start`
silently truncates beyond that threshold.

## Importance

The `importance` column (1-10) on `messages`, `decisions`, `learnings`, and
`loa_entries` controls L1 tier ranking at session start.

```bash
# Backfill importance scores from confidence signals (dry-run by default)
recall importance backfill
recall importance backfill --execute
recall importance backfill --execute --table decisions
recall importance backfill --execute --force            # overwrite non-defaults too

# Pin / unpin individual records
recall pin decisions 42                 # Pin decision #42 to importance 10 (default)
recall pin learnings 7 8                # Pin learning #7 to importance 8
recall unpin decisions 42               # Reset to table default (5, or 8 for LoA)
```

LoA entries have a write-time floor of 5; `recall pin` will not drop them below that.

## Record Provenance

The `provenance` column on `messages`, `decisions`, `learnings`, `breadcrumbs`,
and `loa_entries` declares how each record was created: `verbatim` (exact source
text), `user_authored` (directly authored via a user or agent command),
`extracted` (generated from source material, possibly lossy), or `derived`
(mechanically produced from existing memory records). Provenance is **automatic
write-path metadata** — every write path stamps it; there is no flag or MCP
parameter to set it (see `docs/adr/0001-record-provenance-automatic-write-path-metadata.md`).

Legacy rows that predate the column have no declared provenance (`NULL`,
reported as `unknown`). The backfill classifies them conservatively — only
where the source table or a write-path marker gives deterministic evidence;
everything else stays unknown rather than being guessed:

```bash
recall provenance backfill                      # Dry-run report (default)
recall provenance backfill --execute            # Apply the classification
recall provenance backfill --execute -t loa_entries  # Limit to one table
```

Allowed `-t/--table` values: `messages`, `decisions`, `learnings`,
`breadcrumbs`, `loa_entries`, `all` (default).

## Benchmarks

Phase 2 benchmark harness for measuring context efficiency.

```bash
recall benchmark list                   # Show available suites + build status
recall benchmark run                    # Run all available suites
recall benchmark run B                  # Run a specific suite (A-F)
recall benchmark run B -p my-project    # Scope to a specific project
recall benchmark report                 # Show the latest report
```

Suite B (token efficiency) compares the v2 wake-up bundle against v1 and the
CLAUDE.md baseline. Results are written to `docs/benchmarks/results/` as JSONL plus
a human-readable `.md` alongside. See `docs/benchmarks/README.md` for methodology.

## Export & Backup

Portable and disaster-recovery exports of the memory database.

```bash
recall export                                   # JSON export to stdout (summary on stderr)
recall export --format markdown                 # Human-readable Markdown export
recall export --format sql --output dump.sql    # Textual SQL dump (schema + INSERTs)
recall export --format sqlite --output copy.db  # Full database backup (VACUUM INTO)
recall export --output exports/                 # Directory mode: artifact + manifest.json
recall export --backup                          # Timestamped SQL dump to ~/.agents/Recall/backups/
```

Formats:

- **json / markdown** — app-level export of the durable memory tables
  (`sessions`, `messages`, `decisions`, `learnings`, `breadcrumbs`,
  `loa_entries`, `dedup_lineage`, `host_ingest_generations`,
  `host_ingest_generation_messages`, `host_ingest_embedding_invalidations`,
  `host_ingest_state`,
  `host_ingest_messages`, `loa_message_sources`). Every row of a
  provenance-bearing table carries an explicit
  `provenance` field; legacy `NULL` provenance is exported as the literal
  `unknown` — never omitted, never guessed (see Record Provenance above).
  Embeddings are excluded.
- **sql** — textual SQL dump (CREATE TABLE + INSERT statements) of the same
  durable tables. Restorable into an empty database; one-command restore is
  intentionally not provided.
- **sqlite** — binary database backup via `VACUUM INTO`: the full DB including
  embeddings and internal tables. Always requires `--output`.

Output contract:

- No `--output`: export data goes to stdout, the manifest summary to stderr —
  stdout stays clean for piping.
- `--output <file>`: writes a single export file and prints the manifest
  summary to stdout.
- `--output <dir>`: writes the export artifact plus `manifest.json` into the
  directory. Directory exports always write `manifest.json`. A path that does
  not exist yet is treated as a file — add a trailing slash (`exports/`) to
  request a new directory.

The manifest records the Recall version, timestamp, schema version
(`PRAGMA user_version`), format, included tables, row counts per table,
provenance counts per table (including `unknown`), and whether embeddings were
included.

`recall export --backup` writes a timestamped SQL dump to
`~/.agents/Recall/backups/` (creating the directory if needed), never
overwrites an existing file (a `-N` suffix is added on collision), and prints
the output path.

## Dedup

Detect and mark duplicate memory records without erasing evidence or lineage.

```bash
recall dedup                            # Dry-run report (default — writes nothing)
recall dedup --execute                  # Mark duplicates (non-destructive)
recall dedup --execute --delete         # Destructive opt-in: hard-delete duplicates
recall dedup -t breadcrumbs             # Scope to one table
recall dedup -p myproject               # Scope to one project
recall dedup --threshold 0.98           # Stricter semantic matching (default 0.95)
recall dedup --no-semantic              # Exact/normalized text pass only
```

Safety model:

- **Dry-run by default.** Mutations require `--execute`.
- **Non-destructive by default.** `--execute` writes lineage rows to the
  `dedup_lineage` table (survivor, duplicate, reason, similarity, status,
  timestamp); the duplicate records themselves stay intact and are merely
  hidden from search. `--delete` is the destructive opt-in and requires
  `--execute` — run `recall export --backup` first.
- **Within-table only.** Dedup never merges across tables (or across
  projects). Cross-table duplicate candidates are report-only.
- **Survivor priority** is `user_authored > verbatim > extracted > derived >
  unknown` ([Record Provenance](#record-provenance)); ties break by richness
  (longer normalized text), importance, recency, then lowest id.
- **Detection** combines exact/normalized text matching with semantic
  matching over stored embeddings (no embedding service call needed). The
  semantic pass is skipped — and reported as skipped — when no embeddings
  exist; records are never merged below the configured `--threshold`
  (conservative default: 0.95 cosine similarity). Records with fewer than 20
  significant characters are never candidates.
- **Survivors are sticky.** A record recorded as a survivor in
  `dedup_lineage` is never re-marked as a duplicate by a later run, so every
  hidden or deleted record keeps a visible survivor across runs. The
  tradeoff is order-dependence: an early survivor is never consolidated
  under a later, higher-priority record. As defense-in-depth, `--delete`
  refuses outright (no changes written) if a plan would delete a recorded
  survivor. A consequence of stickiness is an intentional asymmetry: because a
  sticky survivor is excluded from the *duplicate* side of later scans, it
  never acquires **new** semantic duplicates after the run that established it.
  The exact/normalized pass is unaffected — exact-duplicate chains share
  normalized text, so a survivor that gathers further exact matches stays safe
  by construction.
- **Marking is terminal until acted on.** Once `--execute` marks a record a
  duplicate, a later `--execute --delete` run does **not** auto-escalate that
  prior mark to deletion — `--delete` acts only on duplicates identified within
  its own run. Marking is the conservative, reversible state; silently
  escalating it to irreversible deletion would violate non-destructive-by-default
  ([ADR-0003](https://github.com/edheltzel/Recall/blob/main/docs/adr/0003-dedup-mark-delete-non-escalating.md)). A user who wants
  prior marks deleted must act on them explicitly.
- **Prune respects survivors.** [`recall prune`](#prune) never deletes a record
  that is a recorded survivor in active `dedup_lineage` (status `marked` or
  `deleted`) — doing so would orphan the duplicates marked under it (they would
  stay hidden from search while their visible representative is gone). Withheld
  rows are reported in the prune summary, never silently skipped — the same
  conservative stance as `--delete`'s survivor guard above.
- **Lifecycle-aware.** Only `active` decisions participate; superseded and
  reverted decisions are managed by the decision lifecycle, not dedup.

Marked duplicates are hidden from all search paths by default; see
`recall search --include-duplicates`. Lineage is included in
`recall export`, so the audit trail is portable.

## Repair

Explicit data/index maintenance is deliberately separate from
`recall doctor --fix`, which repairs install and configuration drift but never
repairs memory data or indexes.

```bash
recall repair                           # Dry-run report (default — writes nothing)
recall repair --execute                 # Apply the planned repairs
recall repair -t messages               # Scope to one table
recall repair --no-embed                # Skip the embedding pass
```

What repair covers:

- **FTS5 index rebuild.** Each search index (`messages`, `decisions`,
  `learnings`, `breadcrumbs`, `loa_entries`, `telos`, `documents`) is checked
  against its source table — indexed row count (via the docsize shadow
  table), sync-trigger presence, and the FTS5 `integrity-check` command. A
  drifted index is rebuilt from its source table; a missing index or missing
  sync triggers (the classic symptom: search silently returns nothing on an
  un-migrated database) are recreated from the canonical schema DDL and then
  rebuilt.
- **Lifecycle message publication.** Pending lifecycle generations are checked
  separately because their searchable rows are activated by a publication
  pointer. With `--execute`, repair advances their FTS publication and semantic
  invalidations in bounded batches. If work remains after that bounded pass,
  repair exits nonzero so it can be retried safely.
- **Re-embedding.** Rows expected to carry embeddings (`loa_entries`,
  `decisions`, `learnings`, assistant `messages`) that have none are
  re-embedded when the Ollama embedding service is available and the row has
  enough source text. If the service is unavailable, repair reports the
  missing embeddings and still exits successfully — unless another requested
  repair failed. Partial results are never hidden: embedded, skipped
  (too short), and failed counts are all reported.
- **Orphan/invariant recovery.** With `--execute`, embeddings whose published
  source record no longer exists are removed and the vector index is rebuilt
  when sqlite-vec is available (otherwise it remains durably marked for the
  next rebuild). Dedup lineage pointing at missing rows, messages without a
  session, broken LoA message ranges and parent links, and pending schema
  migrations remain **report-only**. Pending migrations are fixed by
  `recall init`.

Safety model:

- **Dry-run by default.** Mutations require `--execute`. Run
  `recall export --backup` before applying repairs.
- **Repair never hard-deletes source records.** Only derived orphan embedding
  rows are removed.
- **Repair never changes [Record Provenance](#record-provenance).** FTS
  rebuild regenerates index shadow tables; re-embedding only inserts into
  the `embeddings` table. No source-table column is written.
- `recall doctor` may recommend `recall repair`, but `doctor --fix` never
  runs data repair implicitly.

## Admin

```bash
recall init                             # Initialize the database (safe to re-run)
recall doctor                           # Health check all subsystems
recall doctor --fix                     # Repair install and configuration drift
recall stats                            # Database statistics
recall path                             # Print resolved paths (DB, install root, symlinks)
recall path --json                      # Same, as JSON
recall migrate --to /new/path/recall.db   # Relocate the database (details below)
recall migrate --to ... --dry-run       # Preview the migration plan
recall onboard                          # Interactive L0 identity interview (see Onboard)
recall start                            # Render L0/L1 session-start memory (see Identity & Onboarding)
```

`recall init` creates the database schema if it does not exist, and applies any pending migrations. It is safe to run on an existing database.

`recall init` and `recall migrate` preserve literal `..` segments and repeated spaces in database paths. The filesystem resolves these paths, including `link/..` through a symlink's target, when creating directories or opening and moving files.

`recall doctor` checks the database connection, schema integrity, FTS5 index health, MCP server registration, Ollama availability, and the per-platform symlinks under `~/.agents/Recall/`. Run this first when troubleshooting. Pass `--fix` to repair drift: missing symlinks are re-created, stale `recall-memory` database paths are updated, and user-modified files at symlink targets are backed up under `~/.agents/Recall/backups/<TIMESTAMP>/doctor-fix/` before being replaced. Data and index maintenance is the explicit job of [`recall repair`](#repair), which doctor recommends when an FTS index is out of sync.

Claude configuration files accept JSONC, but when `recall install`, `recall update`, `recall uninstall`, or `recall doctor --fix` writes one back, it is serialized as strict JSON and loses comments and trailing commas. Adding the first Recall MCP or hook entry during install or update also normalizes the file. `recall migrate` applies the same normalization to every detected host configuration it rewrites.

Recall's direct config editors reject malformed configuration, duplicate keys, and invalid Recall entry shapes instead of partially interpreting or silently skipping them.

`recall stats` reports row counts per table and total database size.

`recall path` prints the resolved DB path, the install root, the active env var (`RECALL_DB_PATH` / `MEM_DB_PATH` / default), and the per-platform symlink targets with their current state (OK / drift / missing). Pass `--json` for machine-readable output.

`recall migrate` moves the database to a new path and rewrites MCP configs across all detected platforms (`~/.claude.json`, `~/.claude/settings.json`, `~/.config/opencode/opencode.json`, `~/.pi/agent/mcp.json`) so the spawned `recall-mcp` process keeps reading from the right file. When the [quarterly aging wrapper](https://recall.edheltzel.codes/docs/installation/#quarterly-aging-cron) exists, migration also rewrites its exported database assignment through the same staged-write and rollback path. It does not create a wrapper or change the crontab.

It expands `~` and the `~/` prefix in `--to` and makes relative source and destination paths absolute by prefixing the current working directory. Rewritten MCP configs retain that destination spelling. If source and destination have the same filename and their existing parent directories resolve to the same physical directory, migration exits successfully without snapshots, file moves, or config changes.

For a different destination, migration refuses to overwrite a non-empty file or proceed when any destination `-wal`, `-shm`, or `-journal` sidecar already exists. If `lsof` detects an open source database, migration also refuses to proceed; this check is best-effort when `lsof` is unavailable. It snapshots the source DB + WAL/SHM sidecars + configs, including their original comments, to `~/.agents/Recall/backups/<TIMESTAMP>/pre-migrate/` before any mutation, then moves the database and those sidecars. If an existing target config or aging wrapper cannot be read, safely parsed, staged for an atomic write, or replaced, migration stops before moving the database. If a later database sidecar move or staged commit fails, migration attempts to move the database and any moved sidecars back and restore the bytes and permissions of files already committed. Restart Claude Code / OpenCode / Pi after running so their MCP servers reload.

### Onboard

```bash
recall onboard                          # Interactive 7-question identity interview
recall onboard --yes                    # Non-interactive (accept all defaults)
recall onboard --dry-run                # Show the proposed identity.md, write nothing
```

`recall onboard` runs a short interview that writes the same resolved identity path `RecallStart` reads — the L0 tier of tiered RecallStart. L0 is the always-loaded slice on hosts with supported session-start injection: your role, projects, tools, and working preferences. Without it, those sessions have to re-learn the basics from search.

Run it once after installing. Re-run it whenever your role, active projects, or working preferences change. The path can be overridden with `RECALL_IDENTITY_PATH` — honored by both `recall onboard` (write) and the RecallStart hook (read).

Inputs are separated with `|` (not `,` or `;`) to avoid silent data loss when a value itself contains a comma: e.g. `no force-push, ever | always use worktrees`.

### Prune

Clean up old and expired records. **Dry-run by default** — must pass `--execute` to actually delete.

```bash
# Preview what would be pruned (safe, no deletions)
recall prune

# Actually prune with default 180-day retention
recall prune --execute

# Custom retention period
recall prune --execute --older-than 90d

# Skip decision pruning
recall prune --execute --keep-decisions
```

**What gets pruned:**
| Table | Strategy |
|-------|----------|
| messages | Consolidated sessions (with LoA) older than N days |
| sessions | Orphaned (no messages, no LoA, no host-ingest state) older than N days |
| breadcrumbs | Expired (`expires_at` in the past) |
| decisions | Superseded/reverted older than min(N, 90) days |
| extraction_tracker | Older than N days |
| extraction_sessions | Rows beyond the 500-row cap |

**Never pruned:** loa_entries, learnings, extraction_errors

**Dedup-aware.** Prune never deletes a record that is a recorded survivor in
active `dedup_lineage` (status `marked`/`deleted`), even when it otherwise
matches the retention rule above — deleting a survivor would orphan the
duplicates marked under it. Withheld rows are counted and reported in the
summary (`N kept as dedup survivors`, or `N kept as dedup survivors/FK references` for messages). See the dedup [safety model](#dedup).

Prune also fails closed before mutation when the lifecycle schema is not ready.
If it reports `RETRYABLE`, run `recall init` and retry the same command.

### Age

Type-aware aging. Dry-run by default. Pass `--execute` to apply.

```bash
recall age
recall age --execute
```

With the defaults, aging expires old breadcrumbs, demotes old decisions and learnings below importance 5, and deletes messages older than 180 days only from sessions consolidated into LoA entries. Existing guards still apply: recorded dedup survivors, marked duplicates, and foreign-key references are withheld. LoA entries are not demoted at the default threshold, and pinned importance 10 is never touched.

For managed cron scheduling, opt-out, paths, and lifecycle behavior, see [Quarterly aging](https://recall.edheltzel.codes/docs/installation/#quarterly-aging-cron).

### Consolidate

Dry-run by default. Clusters old low-importance decisions and learnings by project and time window, writes a derived LoA summary per cluster, and demotes the sources.

```bash
recall consolidate
recall consolidate --execute
recall consolidate -t decisions
```

Defaults: tables `all` (`decisions`, `learnings`), age cutoff `180d`, importance below 5, window 30 days, minimum cluster size 3. Pinned importance 10 is not a candidate.

### Cluster

Dry-run by default. Groups learnings that already have embeddings by cosine similarity and, with `--execute`, writes procedure records.

```bash
recall cluster
recall cluster --execute --threshold 0.9
```

`--threshold` is 0.85 by default (0-1).

Source: https://recall.edheltzel.codes/docs/reference/cli/index.mdx
