Skip to main content

Conversation history and semantic search for Claude Code

Project description

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. Everything runs on your machine — no data leaves it.

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. On the first session after installing, ccrecall silently creates ~/.ccrecall/config.json with recommended defaults. You can edit that file directly at any time to change settings.

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.

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

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

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)

/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 last session summary into Claude's context

Config file

~/.ccrecall/config.json — created automatically on first run with these defaults:

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 ~/.ccrecall/ccrecall.log. Set to false to suppress the log.
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.

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

Project details


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.13.4.tar.gz (207.3 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.13.4-py3-none-any.whl (122.3 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: ccrecall-0.13.4.tar.gz
  • Upload date:
  • Size: 207.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","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.13.4.tar.gz
Algorithm Hash digest
SHA256 5d54bfdf58df6bd9f30d19acc1fe1cb4510e72d8d3804cbf2ef5ff99bb81e2ff
MD5 a4ba021dc3b7eee30867364ba9d4f974
BLAKE2b-256 30836e2ba90f5b9f695482df36197934596311adcec9bf64ea470371a6ae1436

See more details on using hashes here.

File details

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

File metadata

  • Download URL: ccrecall-0.13.4-py3-none-any.whl
  • Upload date:
  • Size: 122.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","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.13.4-py3-none-any.whl
Algorithm Hash digest
SHA256 9145e53283fe0bbf433d9d4e83e001e92a2d35664563ba55ab1f74458268b489
MD5 65234241f044a7635e6d4bccc0c7e2aa
BLAKE2b-256 e5b37cf15fbdcfc7a3bddea0a71d997e11e5ca40493e9be037039471e85f55e2

See more details on using hashes here.

Supported by

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