Cassandra
Record failed tool calls and flag unchanged retries across sessions, compactions and subagents.
Failed calls can drop out of context after compaction, between sessions or in subagents. Persistent records make those failures available for later retries.
- TypeScript
- Bun
- Claude Code
- hooks
GitHub (opens in a new tab)README (opens in a new tab)Changelog (opens in a new tab)
On this pageRecord failures and detect retries
Record failures and detect retries
Cassandra is a Claude Code plugin that hooks into the tool-call lifecycle. Failed Bash and mcp__* calls are recorded. If the same call runs again without a detected project change, the model receives one line describing the earlier attempt and its result.
Matching uses a fingerprint of tool_name and tool_input, both structured JSON. For Bash, commands are trimmed and consecutive whitespace is collapsed. Paths are not canonicalised, flags are not reordered and trailing pipes are not removed, because those transformations could merge distinct commands.
Edit and Write are excluded. Even functionally equivalent edits do not repeat byte for byte, so indexing these payloads would not provide matching records.
Use across context boundaries
Within an intact context, the failed call is usually still available in the transcript and an extra warning adds little. Cassandra addresses three situations where that context is absent:
- After compaction, a failed call can drop out of context while its record remains.
- A new session does not include the previous session's failed attempts.
- A subagent's context does not include the main agent's earlier attempts.
Warnings are suppressed outside these boundaries.
Quick start
claude plugin marketplace add https://github.com/AraneaDev/aranea-marketplace
claude plugin install cassandra@araneaHooks bind at session start. The hook binary is not included in the repository: the first session after installation builds it in the background and displays a notice. The plugin is active from the second session onward.
The binary is around 79 MB because bun build --compile embeds the Bun runtime. This is why it is built locally rather than shipped. If Bun is missing from your PATH, the plugin remains inactive and displays one notice at session start.
Warning checks
On PreToolUse, the plugin fingerprints the call and looks up the hash. Most lookups have no match and exit without output. A match triggers a freshness check. If the workspace has changed since the failure, no warning is added. If no change is detected, the model receives the warning.
In a git repository, the check hashes HEAD and git status --porcelain. Outside git, it uses a bounded scan of modification times in the working directory. An inconclusive check produces no warning, reducing the risk of false positives.
The check has two limits. Outside git, a file rewritten with different content of the same length and its modification time restored can evade detection. Changes outside the project, globally installed packages and environment-variable changes also leave the project stamp unchanged. The warning therefore states the scope of the check: "nothing in this repository has changed".
Overhead and warning statistics
Measured overhead is roughly 12 ms per hook invocation on an idle machine and 17 ms under load, against a design budget of 20 ms. A successful tool call invokes both PreToolUse and PostToolUse, starting the binary twice for about 25 ms in total. The measurements fit the budget per invocation, but exceed it per successful tool call.
PostToolUse resolves the pending marker, allowing the plugin to record when a warned call subsequently succeeds. Without this hook, cassandra stats cannot calculate the false-positive rate needed to assess the freshness check.
cassandra stats reports the false-positive rate and the share of warnings issued without a context boundary. The first indicates whether the check needs improvement. The second shows how often warnings repeat information already present in context; a high share is a reason to remove the plugin rather than adjust the check.
Data handling and limits
Cassandra does not block or deny calls, or rewrite them. The only addition is one line of additionalContext. The plugin does not read your prompts or the model's prose.
The stored record includes a 240-character excerpt from the tool's error message or denial reason, so the warning can explain the earlier failure. This is command output, not user or model prose, and is treated as untrusted. Control characters are removed before storage; the warning fences and labels the excerpt as tool output rather than an instruction.
All data stays on your machine, within the plugin's data directory. There is no network traffic, telemetry or API key.