Skip to main content

ccrecall

Conversation history and semantic search for Claude Code.

ccrecall stores your Claude Code sessions in a local SQLite database so you can recall past conversations, search across them by keyword and meaning, and get automatic context on session start. By default, storage, sync, deterministic summaries, and semantic search all run on your machine. Optional LLM summary enrichment is separate: if you explicitly opt in and pass its capability check, ccrecall sends a branch-scoped packet through your installed Claude CLI using your Claude auth.

ccrecall is an independent, community project for Claude Code. It is not affiliated with, endorsed by, or sponsored by Anthropic.

What it does

Every time a Claude Code session ends, the conversation is synced to ~/.ccrecall/conversations.db. On your next session start, Claude automatically gets a summary of what you were last working on. You can also search past sessions by keyword or pull recent ones at any time.

Install

ccrecall has two parts: a Python package (the ccrecall CLI plus the hook binaries) and a Claude Code plugin (the /ccr-* skills and the hook wiring). Install both.

1. Install the package — puts ccrecall and the hook commands on your PATH:

uv tool install ccrecall

(pipx install ccrecall or pip install ccrecall work too.)

2. Enable the plugin — ccrecall ships as a Claude Code plugin. The repo doubles as a single-plugin marketplace, so from inside Claude Code:

/plugin marketplace add NodeJSmith/claude-code-recall
/plugin install ccrecall@claude-code-recall

That registers the skills and wires the SessionStart / Stop / SessionEnd hooks (hooks/hooks.json) — both are auto-discovered from the plugin's directory layout. Reload with /reload-plugins if they don't appear immediately.

Plugin skills are namespaced under the plugin name, so the skills below are invoked as /ccrecall:ccr-recall and /ccrecall:ccr-resume. The hook commands degrade gracefully — each is guarded by command -v … || true, so if the package isn't installed (or isn't yet on PATH) the hook is a silent no-op rather than a broken session.

First-run setup

No manual setup is needed. ccrecall uses built-in defaults unless you create ~/.ccrecall/config.json with overrides.

Semantic search

Search results are fused from two signals: keyword full-text search (FTS5 → FTS4 → LIKE fallback) and vector similarity from a locally-running embedding model. The two ranked lists are merged with Reciprocal Rank Fusion (RRF), so results that rank well in both signals appear first.

The embedding model is jina-embeddings-v2-small-en (512-dim), running entirely on your machine via fastembed. No data leaves your machine on this path.

Optional LLM summary enrichment

LLM enrichment adds a Branch Resume Brief above the deterministic summary when a branch has a valid cached enrichment. It is off by default and never replaces the deterministic baseline.

When you enable it, ccrecall sends only the selected branch packet through your installed claude CLI using your Claude Code auth:

  • selected branch-scoped transcript content
  • branch/session metadata
  • source-path provenance

ccrecall does not enable this automatically. You must opt in, and the claude CLI must first pass a --no-session-persistence capability check so the enrichment call does not create importable Claude transcripts of its own.

Capability check and manual backfill

Run the capability check first:

ccrecall backfill llm-summaries --check-capability

Expected outcome: a pass/fail result that updates ccrecall's local capability sidecar without reading your real conversation transcripts.

Then run the canonical manual backfill command:

ccrecall backfill llm-summaries

Common selectors:

ccrecall backfill llm-summaries --days 14
ccrecall backfill llm-summaries --limit 50
ccrecall backfill llm-summaries --session <session-uuid>
ccrecall backfill llm-summaries --force

Use --force only when you want to rerun the selected branches even after an earlier enrichment attempt or when you intentionally want a manual run to bypass the llm_summary_min_exchanges filter.

Expected outcome: progress/completion output only. ccrecall does not print transcript text, raw prompts, or raw model responses.

Automatic current-session enrichment

llm_summaries_enabled controls only the automatic post-sync worker. When it is true, ccrecall sync-current may spawn a detached enrichment worker after a successful Stop-hook sync when:

  • the current session produced new messages

The worker checks the capability sidecar and branch eligibility itself. If the capability check is missing or invalid, it exits without calling Claude. If the branch does not meet the minimum-exchange threshold, it remains eligible for a later manual backfill.

If any of those gates fail, or if Claude is unavailable, unauthenticated, rate-limited, over budget, times out, or returns invalid output, ccrecall keeps the deterministic summary and injected context exactly as before.

Budget behavior

llm_summary_max_budget_usd defaults to $1.00. ccrecall passes that value through to the Claude CLI unchanged as an upstream stop threshold. It is useful spend control, but it is not a guaranteed maximum charge: Claude can cross the threshold before stopping.

Embedding coverage and backfill

Coverage

New sessions are embedded automatically as they sync (embed-on-write), so coverage builds forward on its own. Only active-leaf branches are embedded — at most one active leaf per session (maintained by sync/import), not its abandoned forks/retries. The flag isn't DB-enforced, but sync/import marks exactly one branch is_active=1 per session. The search path only ever returns active leaves, so embedding inactive forks would just produce vectors that can never surface.

Optional: seed historical conversations

Embedding runs on CPU via fastembed. jina-v2-small-en is light — a few milliseconds for a short summary, up to ~400ms for a long one — but seeding a large history (~2k active leaves) is still a bounded chunk of work, and a parallel run can thrash a small or shared box. It is therefore opt-in — it is not auto-spawned on SessionStart — so it never fires unbidden. Run it yourself when you want to seed:

ccrecall backfill embeddings              # all active leaves, all history
ccrecall backfill embeddings --days 14    # only the last 14 days
ccrecall backfill embeddings --limit 500  # cap this run at 500 branches
ccrecall backfill embeddings --threads 4  # use 4 inference threads (idle machine)

It runs at low scheduling priority (nice) and a single inference thread by default so it yields to interactive work. Tune the thread count with --threads (e.g. --threads 4 on an idle workstation to finish faster). Progress prints to stderr (one line per batch); the run is resumable — re-running skips already-embedded branches.

Flags

Flag Effect
--keyword-only Skip the embedding step entirely, use keyword search only
--status Print diagnostic info (vec extension loaded, model name, embedded vs. total summarized (embeddable) branch count) and exit 0

Runtime deps

The semantic search path requires three extra packages beyond the base install:

  • sqlite-vec — SQLite extension for vector KNN queries
  • fastembed — downloads and runs the embedding model (manages onnxruntime + tokenization)
  • numpy — vector math (normalization)

These are included in the package dependencies. If fastembed fails to import (e.g. ABI mismatch on an unusual platform), search falls back silently to keyword-only mode.

Degradation

Semantic fusion is automatically disabled when:

  • The embedding model can't be loaded (e.g. a first-run download failed and no cached copy exists)
  • fastembed cannot be imported
  • sqlite-vec cannot be loaded on the connection (e.g. Python built without loadable extensions)

In all cases, search falls back to keyword-only and returns results normally. When this happens (or when embedding coverage is still catching up below ~95%), a recall appends a one-line caveat to its own results so you know they may be partial — surfaced only when you actually run a recall, never as a standalone nag. Use ccrecall search --status to check which path is active.

When ccrecall speaks up

ccrecall is meant to be invisible — it runs in the background and Claude consumes its output. It will interrupt you, once, only when something is actually broken and only you can fix it:

  • It can't save your history — the data directory or database is unwritable (disk full, permissions, corruption). Left unsaid, a working install would silently stop recording sessions.
  • Embeddings are persistently failing — the vector extension or embedding model is unavailable, so semantic search is degraded until you fix the environment.

When either condition holds, the next session injects a short alert for Claude to relay to you in plain language. It's told once, then goes quiet for ~24h (tunable via alert_snooze_hours in the config file) even if still broken, and clears itself the moment the condition resolves. Coverage that's merely catching up is never surfaced this way — only genuine, unrecoverable failures earn the interruption.

Skills

Skill Trigger What it does
/ccr-recall "what did we discuss", "continue where we left off", "search my conversations" Lets Claude search or browse your past sessions on demand
/ccr-resume "pick up where we left off after /clear", a stop, or an unanswered question Reconstructs the prior session's intent from its transcript tail and surfaces any unresolved decision

Entry points

Hooks (run automatically — don't call these manually)

These are wired by the plugin's hooks/hooks.json and fire on their respective Claude Code events.

Entry point Event What it does
ccrecall-setup SessionStart Creates ~/.ccrecall/ if needed, opens the DB to apply any pending migrations, then spawns ccrecall import and ccrecall backfill summaries as background processes
ccrecall-context SessionStart (startup + clear) Injects a summary of your most recent session into Claude's context so it knows what you were working on. On /clear, reads a handoff file to link directly to the session you just cleared from
ccrecall-clear-handoff SessionEnd (clear only) Writes a small handoff file so the next session start knows which session to link to after a /clear. Without this, context injection falls back to a "most recent session" heuristic
ccrecall-sync Stop Syncs the current session to the DB in a detached background process. Runs on every session end

These are kept as separate console scripts (rather than ccrecall hook … subcommands) on purpose: hooks fire on every session boundary, and a direct entry point avoids eagerly importing the full CLI command surface on the hot path.

Internal helpers (spawned by hooks — don't call these manually)

Entry point What it does
ccrecall sync-current Syncs a single session file to the DB. Called by ccrecall-sync with the session ID from stdin
ccrecall import Full import of all JSONL files in ~/.claude/projects/. Skips files that haven't changed since last import (file hash check). Run on first install and whenever new sessions need backfilling
ccrecall backfill summaries Generates context summaries for any DB branches that don't have one yet. Runs in the background after ccrecall-setup
ccrecall-llm-summaries Internal detached worker that builds branch packets and asks the installed Claude CLI for opt-in Branch Resume Briefs. Spawned after sync when llm_summaries_enabled is true; it exits without calling Claude when the capability gate is not valid.

Skill CLIs (called from skill files — can also be used directly)

These are the ccrecall subcommands the /ccr-* skills invoke. You can run them from the terminal too.

Entry point What it does
ccrecall recent Prints recent sessions from the DB in markdown (default) or JSON. Used by /ccr-recall
ccrecall search Searches sessions by keyword fused with vector similarity (FTS5 → FTS4 → LIKE fallback, RRF-fused with jina embeddings when available). Used by /ccr-recall
ccrecall tail Reads the tail of a prior session's transcript to recover the last instruction and any unanswered question. Used by /ccr-resume
ccrecall backfill embeddings Opt-in seeding of embeddings for historical active-leaf branches (jina-v2-small-en via fastembed). Not auto-spawned. Supports --days N / --limit N / --threads N; throttled via nice + a single inference thread by default. Resumable
ccrecall backfill llm-summaries Opt-in Branch Resume Brief backfill through your installed Claude CLI. Run --check-capability first. Supports --days N / --limit N / --session UUID / --force. Manual runs work even when automatic enrichment is disabled.
ccrecall backfill tool-content Opt-in re-parse of already-synced sessions' JSONL files to populate messages.tool_content for rows synced before tool-content extraction existed. Not auto-spawned. Supports --days N / --limit N / --status; resets embedding_version on touched branches so backfill embeddings re-embeds them. Resumable

Data flow

Session ends
  └─ ccrecall-sync (Stop hook)
       └─ ccrecall sync-current (background)
             └─ writes to ~/.ccrecall/conversations.db
             └─ embeds the active leaf via jina if model available (drops silently on failure)
             └─ optionally spawns ccrecall-llm-summaries after sync
                  (only when explicitly enabled; worker checks the capability gate)

/clear (SessionEnd)
  └─ ccrecall-clear-handoff
       └─ writes a handoff file naming the session being cleared
            (so the next SessionStart links to it instead of guessing)

Session starts
  └─ ccrecall-setup (SessionStart)
  │    └─ ccrecall import (background, first run / new files)
  │         └─ embeds each new active leaf via jina if model available
  │    └─ ccrecall backfill summaries (background, if summaries missing)
  │    └─ (embedding backfill is NOT auto-spawned — opt-in via ccrecall backfill embeddings)
  └─ ccrecall-context (SessionStart, startup + clear)
       └─ injects deterministic context, plus a Branch Resume Brief above it when a current valid enrichment exists

Config file

~/.ccrecall/config.json is an optional user-created override file. ccrecall uses these defaults when a key is absent:

Key Type Default Effect
auto_inject_context bool true Inject a summary of your previous session at session start.
max_context_sessions int 2 How many recent sessions to include in that injected context.
exclude_projects list[str] [] Project names to skip when storing conversations — excluded projects are not imported or synced. Matched against the project's directory name. This is write-side only: it prevents new data from being indexed; it does not remove or hide conversations already stored before the project was excluded.
logging_enabled bool true Write hook diagnostics (including swallowed hook exceptions) to per-process files such as ~/.ccrecall/ccrecall-sync.log. Set to false to suppress logging.
log_level str "INFO" Logging verbosity when logging_enabled is true. Accepts standard Python level names: DEBUG, INFO, WARNING, ERROR.
alert_snooze_hours int 24 After surfacing an alert (unwritable DB, embedding failure), suppress the same alert for this many hours.
llm_summaries_enabled bool false Enable automatic post-sync LLM enrichment for eligible current sessions. Manual ccrecall backfill llm-summaries runs still work when this is false.
llm_summary_model str "sonnet" Claude model name to pass through unchanged for enrichment. sonnet is the default because this is a factual summarization/resume task; you can choose a cheaper model such as haiku yourself.
llm_summary_effort str "medium" Claude effort setting for enrichment requests.
llm_summary_timeout_seconds int 180 Per-branch timeout for the Claude enrichment subprocess.
llm_summary_max_budget_usd float 1.00 Claude budget stop threshold to pass through unchanged. This is upstream spend control, not a guaranteed maximum charge.
llm_summary_min_exchanges int 9 Minimum exchange count for automatic enrichment and ordinary manual backfill selection. ccrecall backfill llm-summaries --force bypasses this filter.

Example config:

{
  "llm_summaries_enabled": true,
  "llm_summary_model": "sonnet",
  "llm_summary_effort": "medium",
  "llm_summary_timeout_seconds": 180,
  "llm_summary_max_budget_usd": 1.0,
  "llm_summary_min_exchanges": 9
}

The deterministic baseline remains local-only. Opt-in LLM enrichment is the only path in this release that sends selected transcript-derived data through Claude Code auth.

Database

~/.ccrecall/conversations.db — SQLite, WAL mode. Tables:

  • sessions — one row per conversation session
  • branches — one row per conversation branch (rewinding creates new branches)
  • messages — all messages, stored once per session regardless of branch
  • branch_messages — join table linking messages to branches
  • import_log — tracks which JSONL files have been imported and their hashes
  • chunks — one row per exchange (user turn + following assistant turns), carrying bounded display text and embedding bookkeeping
  • chunk_vec — sqlite-vec virtual table storing 512-dim jina embeddings keyed by chunk rowid, used for KNN search

Development

uv sync                       # install package + dev dependencies
uv run pytest                 # run the test suite
uvx prek run --all-files      # run the lint/format/type hooks

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ccrecall-0.22.0.tar.gz (335.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ccrecall-0.22.0-py3-none-any.whl (182.9 kB view details)

Uploaded Python 3

File details

Details for the file ccrecall-0.22.0.tar.gz.

File metadata

  • Download URL: ccrecall-0.22.0.tar.gz
  • Upload date:
  • Size: 335.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for ccrecall-0.22.0.tar.gz
Algorithm Hash digest
SHA256 22d7d3e1817d510ec6310f6bd4ce5314cb13c1b797c763df54fe1703c9ff13e4
MD5 d42575cef36dc663bb798403f3c56376
BLAKE2b-256 26dae4c02df31265ecfeab0d3743709f865bdff699fbcfdc75585688e25b7644

See more details on using hashes here.

File details

Details for the file ccrecall-0.22.0-py3-none-any.whl.

File metadata

  • Download URL: ccrecall-0.22.0-py3-none-any.whl
  • Upload date:
  • Size: 182.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for ccrecall-0.22.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3466f8333776a6ce6e3b855f3e24a8dc1fddbe87e7fda5116791f4d236188f04
MD5 43e9be3d77808bd23fe9e49e291acbaa
BLAKE2b-256 cfddefe70285b17accb9cc07088c47c3b30ae3e985588e5706345d8ec773919b

See more details on using hashes here.

Release history Release notifications | RSS feed

0.23.0

2 files

0.22.1

2 files

This release

0.22.0 This release

2 files

0.21.1

2 files

0.21.0

2 files

0.20.0

2 files

0.19.4

2 files

0.19.3

2 files

0.19.2

2 files

0.19.1

2 files

0.19.0

2 files

0.18.1

2 files

0.18.0

2 files

0.17.1

2 files

0.17.0

2 files

0.16.0

2 files

0.15.0

2 files

0.14.1

2 files

0.14.0

2 files

0.13.4

2 files

0.13.3

2 files

0.13.2

2 files

0.13.1

2 files

0.13.0

2 files

0.12.0

2 files

0.11.1

2 files

0.11.0

2 files

0.10.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page