Crush
- ID:
crush - Store:
<data_dir>/crush.db, one SQLite store per project, listed in${XDG_DATA_HOME:-~/.local/share}/crush/projects.json;data_diris<project>/.crushby default, and an entry that moved it is followed. - Read override:
DEJA_CRUSH_ROOT(the data home holding that registry) - Format: SQLite;
sessionsjoined tomessages, incremental bysessions.updated_at.
Crush is the only harness here that keeps its history beside the work rather
than in one place. Nothing under the data home holds a transcript: the registry
does, as {"projects":[{"path","data_dir","last_accessed"}]}, and `crush
projects` prints the same pairs. A registry entry whose project has since been
deleted is ordinary and is skipped rather than reported.
A message's parts column is a JSON array of {type, data}. text carries
data.text; tool_call carries the tool's name and a JSON *string* of its
arguments, so the shell command and the edited path are one decode further in.
edit and multiedit (an edits array) carry old_string and new_string,
and write carries content; those become the replaced span and the written
lines that deja restore and deja blame read (#4377). lsp_replace_symbol
writes its replacement in place of a symbol, before it or after it, and is
read as written lines unless its action is delete; the replaced symbol is
only in the result's metadata, {file_path, old_content, new_content, action}.
It is read from there: the lines the result names (lines N-M) of the old
content, or the lines the two contents do not share (#4533, #595).
tool_result carries what the tool printed with a <cwd>…</cwd> tag appended,
which is the same directory on every line in the store and is stripped rather
than indexed — left in, a search for the project name would match every tool
output there is. finish is bookkeeping. A result names its call by
tool_call_id (the call's id), on a later row: a bash result whose last line
is crush's "Exit code N" puts → exit N on the command, and an edit,
multiedit or write whose result has is_error changed nothing, so it keeps
its file record and no edit or written lines (#4532).
Both stamp columns are commented "Unix timestamp in milliseconds" in Crush's own
schema and hold whole seconds in what v0.92.0 writes — its update trigger sets
strftime('%s','now'). Both units are read. The project is the directory the
store sits under; Crush records no working directory on the session row.
parent_session_id is the only place the subagent edge exists, so a subagent's
turns are filed under their parent rather than as separate work.
Shape verified against crush v0.92.0 by running one against a recording
endpoint: the store landed beside the project, the registry gained its path and
data_dir, and deja indexed the result.
- MCP:
deja install crushadds the server undermcpin
~/.config/crush/crush.json — Crush keys it mcp, not the common
mcpServers. Crush's own crush_info reports `deja = connected (1 tools, 0
resources), and the tool arrives as mcp_deja_deja`. Verified against the
recorder: a scripted call came back with the seeded decision in the bytes
Crush sent next.
- Skill: the same install writes the shared
~/.agents/skills/deja-history/SKILL.md, which Crush scans by default
alongside ~/.claude/skills and its own directory. It appears in the system
prompt's <available_skills> block and in crush_info as `deja-history =
user`.
- Command:
~/.config/crush/commands/deja.md, which Crush lists as
/user:deja. The palette is a TUI surface, so the entry is written but its
expansion is unverified here.
- Auto-recall:
deja install crush-autoadds aPreToolUsehook —
deja hook-tool --crush, matcher ^(bash|edit|write|multiedit)$. PreToolUse
is the only event Crush fires, so that one hook carries the rest: the
session's digest on its first call, the recall for the newest message the
person sent, the fix pair when the previous command failed, and the packet
after a summary. The message, the failure and the summary come out of
crush.db by the payload's session id, each once, one tool call late; a turn
with no matched tool call gets none of it. A hook answers with a
flat {"version":1,"context":…} rather than Claude's nested envelope, which
is what the --crush flag writes; no decision field, because "allow"
there is affirmative pre-approval and would skip the permission prompt on
every call the hook fires on. Measured: with a scripted go build ./...
against a store holding that command's history, the recall block reached the
model appended to the tool result, in the request Crush sent next.
- Resume:
crush --session <uuid>, run in the project directory — Crush
looks for the session in the store under the current directory and nowhere
else, so the same id resolves to nothing from elsewhere. That store sits in
the project, so a project that is gone is refused with deja show (#4459).
- Handoff: exec,
crush run.
Costs on that version, measured from the recorded requests: deja's tool schema
is 3,138 bytes of the 29,754-byte tool block in every request, the skill's
catalogue entry adds 384 bytes to the 22,487-byte system prompt, and a recall
that answers adds 269 bytes to the turn that asked. With the binary moved aside
and the wiring left in place, the turn still completed: 28 tools instead of 29,
the hook's failure logged and ignored, the tool call through.
Last verified: 2026-09-07
deja reads this format and 42 others, and turns what it finds into memory your agents can search. See the harness matrix for what is wired where, or install it and search your own history.
Found this useful? Star deja-vu on GitHub.