ccFamily
[ MENU ]
SECT. 01 // PEER · MEMORY LAYER · CLI · v0.5.6 LIVE

ccRecall/
Claude Code's
local working memory layer

Week one you picked SQLite over MongoDB, to avoid running one more service. Week two you fixed an ImageMagick edge case. Week three you settled on a file naming convention.

Then you open a new session — and Claude remembers none of it.

All of it is still sitting in ~/.claude/projects/ as JSONL — nothing reads it back. ccRecall does: after a session ends it distills the conversation into structured memories, and the next session starts with up to 5 of them. Local SQLite, no cloud, Apache-2.0.

▲ NOW · CMD · INSTALL

npm install -g @tznthou/ccrecall

SessionStart · top of a new conversation actual format

[ccRecall memory recall]

- On macOS use cwebp -q 85 for PNG→webp, not sips — sips fails silently on CJK filenames, prints success but writes nothing…

- Tailwind CLI projects: after editing src/input.css you must run npm run build, because dist/output.css is committed…

- Deploy platforms detect runtime from version files, so .node-version and friends must be committed with an upper bound…

(608 memories available — use recall_query to search more)

DESIGN NOTE // BUDGET RATIONALE

Why only 5, instead of everything?

A memory store is long-tail. Load all of it and the first thing a session does is eat your context window — mostly with things irrelevant to today's work.

So it ships in two parts: SessionStart injects at most 5 entries within <300 tokens; everything else stays in the store until Claude decides mid-conversation that it needs more and calls recall_query itself.

// Corrected the "auto-injects" overstatement in 2026-05, and again in 2026-07 — injection is real, it just has a budget.

SECT. 02 // COMPONENT SPECIFICATION

Three technical choices, each one auditable.

// INDEX(daemon watches .jsonl) → DISTILL(Haiku, optional) → INJECT(SessionStart ≤300 tokens) → RECALL(MCP on-demand)

01 COMPONENT

Local-first

Everything in ~/.ccrecall/ is local SQLite. No cloud, no telemetry, no outbound HTTP.

DEPS: SQLite (system)

SIZE: ~570 KB

NETWORK: none

02 COMPONENT

Hooks + MCP

Native integration with Claude Code lifecycle hooks (SessionStart / SessionEnd) plus the MCP server standard protocol.

HOOKS: SessionStart + End

MCP: v1 protocol

SETUP: install-hooks

03 COMPONENT

Apache-2.0

Source code public and auditable. A simple SQLite + FTS5 + Node stack — no black-box vector DB dependencies.

LICENSE: Apache-2.0

REPO: tznthou/ccRecall

AUDIT: public

SECT. 03 // TRUST CONTRACT

Three principles — written in the README, and in the code.

ASSERT #01

🔒 Local-first

All ~/.ccrecall/ SQLite stays on your machine. The daemon makes zero outbound calls — no version check, no telemetry, no account. The one thing that touches the network is the optional post-session distillation, and it goes through your own Claude CLI. Leave it off and ccRecall never opens a socket beyond localhost. For compliance-sensitive teams (FinTech / Healthcare / Gov): drop it into your existing backup policy — no InfoSec review needed.

ASSERT #02

📜 Open source

GitHub: tznthou/ccRecall — code public and auditable. Community issues and PRs welcome. The license won't quietly switch to BSL/SSPL (explicit commitment).

ASSERT #03

🧰 Claude-Code-native

Native integration with Claude Code lifecycle hooks + MCP server. No IDE dependency, no vendor lock-in, no cloud account required. Auto-compatible with Claude Code — the hooks API is treated as a first-class integration target.

SECT. 04 // WORKFLOW · INDEX → DISTILL → INJECT → RECALL

4 steps to run it, a 5th to make it grow.

npm install / install-daemon / mcp add / install-hooks gets you indexing, startup injection, and manual recall_save. Memories still won't accumulate on their own — that's step 5: an optional wrapper that lets Haiku read the transcript after a session ends and distill 0–5 entries into the store, for about $0.001 a run.

→ 01 INSTALL

Install

One npm command installs ccmem (daemon) and ccmem-mcp (MCP server). Three more setup commands follow — daemon, MCP, hooks — plus one optional distillation wrapper. Full tutorial →

CMD: npm i -g @tznthou/ccrecall

BIN: ccmem + ccmem-mcp

NEXT: 3 steps + 1 optional

→ 02 EXTRACT

Extract

A chokidar watcher tails ~/.claude/projects/, so a new session lands in the index within seconds; the SessionEnd hook exists to confirm nothing slipped through. What actually turns conversations into memories is the optional distillation wrapper — after a session ends, Haiku reads the transcript and distills 0–5 entries (~$0.001/session). Cross-project recall since v0.4.1: when two projects share a topic, high-confidence memories surface across the boundary.

HOOK: SessionEnd (index check)

DISTILL: Haiku via wrapper (optional)

STORE: ~/.ccrecall/ccrecall.db

INDEX: FTS5 full-text

→ 03 RECALL

Recall

On the next session, the SessionStart hook injects up to 5 memories within a 300-token budget. Selection runs in three tiers: cold memories get a rotating slot first (since v0.5.5 anything injected recently is excluded, so the same few entries stop hogging the budget), then recent high-confidence ones, then FTS to fill any gap. Mid-conversation, Claude calls MCP recall_query / recall_context on its own judgement.

HOOK: SessionStart

MCP: recall_query / recall_context / recall_save

BUDGET: ≤5 rows · ≤300 tokens

SELECT: cold-rotate → confidence → FTS

// INVARIANTS: LOCAL-FIRST · NO-CLOUD · NO-TELEMETRY · USER-OWNS-DATA

DESIGN NOTE // WHAT WE CUT AND WHY

We built a trust gate. Then we took it out.

The v0.3.0 design had two tiers: anything the machine captured landed in a low-trust session_journal, and only entries you reviewed and promoted by hand graduated to the memories table the AI can actually search. The reasoning held up on paper — a rule-based scorer mis-captures, and noise in a memory store is worse than no memory at all.

0

PROMOTIONS · EVER

177

STUCK IN QUEUE

Two months of real use returned those two numbers. Not one entry ever graduated. That is not a gate — that is a dead-letter queue.

So v0.5.0 cut the whole thing: the journal, the scorer feeding it, eight endpoints nobody called, and a bookkeeping table eating 65% of the database. The path that was actually doing the work turned out to be the other one — Haiku distills the session after it ends, writes keyed memories, and the next session starts with them.

// A design doc can argue for a gate. Only the data can tell you nobody walks through it.

SECT. 05 // STORAGE FORMAT · WHAT YOU ACTUALLY GET

SQLite is the drawer. Markdown is the note.

The first question people ask is what a memory is actually stored as. Two layers: the container is a single .db file, the content is plain text. The drawer has no idea what is written on the note — it's just a TEXT column.

SAMPLE ROW memories.content
**Rule**: On macOS use cwebp -q 85 for
PNG→webp, not sips.

**Why**: sips fails silently on CJK filenames
— stdout reports success, nothing is written,
no stderr.

**How to apply**:
1. brew install webp
2. cwebp -q 85 input.png -o output.webp

// Bold, lists, code blocks are all markdown —
// SQLite stores them as ordinary text.

VS. OTHER STORES

Obsidian

One .md file per memory, scattered across folders

Notion

Cloud database plus a block structure

ccRecall

One SQLite file, one row per memory

The cost: you can't open memories in Obsidian. What you get back is instant full-text search, confidence and access counts queryable in SQL, and a backup that's one file copy.

v0.5.3 · CJK

CJK conversations get the same topic index

Topic extraction used to recognise Latin word boundaries only, which meant an entire Chinese, Japanese, or Korean session produced exactly zero topics — CJK users silently lost cross-project surfacing and metacognition queries. v0.5.3 brought CJK into the tokeniser (\p{Script=Han} plus 32 stopwords and particle splitting).

measured on production data CJK topics 0 772 total index grew just 1.09×
⚠ NOT JUST NPM INSTALL

Three more commands to wire / full 4 + 1 step tutorial

npm install is just step one — daemon, MCP, and hooks each need a command before it runs at all, plus one optional wrapper before memories start accumulating.

[ → READ FULL TUTORIAL ]
SECT. 06 · FINAL

STEP 1 · INSTALL
Five seconds for this line. Three more steps in the tutorial.

npm install -g @tznthou/ccrecall

[ ⭐ STAR ON GITHUB ]