How the hook works

The plugin at v0.9.2 runs mneme-hook on three Claude Code events:

  • PreToolUse on Edit, Write and MultiEdit. The hook works out the file content the change would produce, then checks the content the change introduces against your recorded decisions. Unrelated lines already in the file don’t trigger a block.
  • PreToolUse on Bash. A single command of the form cat > file <<'EOF' or cat >> file <<'EOF', with a quoted delimiter and nothing else in the command, is checked the same way before it runs. Other shell writes, such as echo >, tee, sed -i, a chained mkdir -p dir && cat > …, or a script, can’t be reconstructed in advance and pass through.
  • SessionStart and Stop. At session start the hook records the state of the repository. Each time the agent finishes a turn, it checks every changed text file that git tracks or would track, which catches the shell writes the first check let through. This needs a git work tree.

The exit code semantics are deterministic: exit 0 allows the edit; exit 2 blocks it and returns the violated decision id and rule to the agent as feedback, so it can correct the change without you stepping in. At the end of a turn, a violation keeps the agent working with the same feedback; it doesn’t undo a file that was already written.

on Edit / Write / MultiEdit (and a single cat-heredoc write):
  introduced = content the change adds to <path>
  result     = mneme check --input <introduced content> \
                 --target-path <path> --query "edit to <path>"
  if mode is strict and result is FAIL or WARN:
    exit 2   # block, decision id returned to the agent
  else:
    exit 0   # allow (also when no verdict could be produced)

The how-to guide replays each of these events against mneme-hq 0.9.2, with the recorded output.

Fail-open by design. The hook never blocks on Mneme malfunction. If mneme is not found, an edited file can't be read, or a mneme check call takes longer than 10 seconds, the hook exits 0 and allows the edit. Its notes go to Claude Code’s debug log only, so neither you nor the agent sees them in the session. If mneme-hook itself isn’t on PATH, Claude Code shows a hook error instead.

Install

The Python runtime and the Claude Code plugin are separate artifacts: installing one does not install the other, and no auto-install links them. The plugin is not yet listed in the Claude Code marketplace, so loading it currently requires a checkout of the Mneme repository.

  1. Install the runtime
    pipx install "mneme-hq>=0.9.2"

    mneme-hq>=0.9.2 keeps the runtime aligned with the current hook contract, including its machine-readable --json verdict.

  2. Scaffold project memory (if you don't have one yet)
    mneme init

    mneme init creates .mneme/project_memory.json with an empty valid decision corpus — deliberately zero seeded decisions, since every recorded decision is enforceable. It refuses to overwrite an existing corpus unless --force is supplied. Populate it with mneme add_decision, or bring your existing ADRs in with mneme adr import.

  3. Load the plugin
    git clone --branch v0.9.2 --depth 1 https://github.com/MnemeHQ/mneme
    claude --plugin-dir ./mneme/integrations/claude-code-plugin

    The plugin bundles the enforcement hook, the mneme skill, and the namespaced slash commands (/mneme:check, /mneme:context, /mneme:record, /mneme:review). Reload in-session with /reload-plugins after changes.

    A legacy flat installer (python scripts/install_claude_code.py, with hyphenated /mneme-check commands) still works from a git clone, but scripts/ is not shipped in the PyPI wheel.

  4. Verify

    Open Claude Code in your project, make an edit that violates a recorded decision, and confirm the hook blocks it and surfaces the decision id.

Slash commands

After installing, four slash commands are available in Claude Code:

Command Purpose
/mneme:context Retrieve decisions relevant to your current task
/mneme:check Check a file or draft against project memory
/mneme:record Record a new architectural decision
/mneme:review Audit all pending diff changes against decisions

Modes

Mode Behaviour Set via
strict (default) Block on any non-zero verdict from mneme check export MNEME_HOOK_MODE=strict
warn Never block. Claude Code ignores the per-edit reason, so violations reach the agent only from the end-of-turn check export MNEME_HOOK_MODE=warn

Warn mode turns blocking off, but it doesn’t show you per-edit findings. To collect findings while you tune new decisions, run mneme check --mode warn yourself or in CI, and keep the hook in strict mode.

Retrieval: what the hook checks and what it misses

Typed FORBID_LITERAL rules (on the paths they apply to), single-term anti-patterns and single-term “no X” constraints are checked on every edit, whatever the file is called. Retrieval only decides which decisions have their multi-term anti-patterns and multi-term “no X” constraints checked. The hook query is "edit to <file_path>", and decisions are scored by overlap between its words (four letters or more, no stemming) and their scope, decision text, constraints, anti-patterns and rationale.

What this means in practice:

  • A decision with scope: storage is retrieved for an edit to app/storage/layer.py, because “storage” appears in the query.
  • The same decision isn’t retrieved for an edit to models.py, so its multi-term rules aren’t checked there. Its typed rules still are.
  • A generic file name like utils.py rarely matches a decision. Retrieval then depends on the words in its directory path and the word “edit” that every hook query contains.

Mitigations:

  1. Choose scope keywords when recording decisions that match file names in your project. Use /mneme:record and follow the scope tip in the command.
  2. Run /mneme:context before non-trivial edits with a descriptive phrase describing the domain (e.g. "storage layer", "auth middleware"). This uses a richer query than the hook and surfaces decisions the hook might miss.
  3. Run /mneme:review after a batch of edits to catch violations the per-edit hook missed due to retrieval gaps.

Prefer FORBID_LITERAL rules for decisions that must hold on every file. Use the slash commands for coverage of multi-term rules where file names don’t describe the domain.

Fail-open guarantees

The hook never blocks on execution errors. It exits 0 (allow) when:

  • mneme is not found on $PATH
  • The file an Edit targets can’t be read (a Write to a new file is checked in full)
  • A mneme check call times out (limit: 10 s)
  • Any other OS-level error occurs

Only a real verdict returned by mneme check can cause a block.

Troubleshooting

Hook is not firing

  • Confirm mneme-hook is on $PATH: which mneme-hook (or where mneme-hook on Windows).
  • If not, the pip scripts directory may not be on $PATH. Add it, or install with pipx.
  • Confirm Claude Code was started with --plugin-dir pointing at the plugin. With the legacy installer, confirm .claude/settings.json contains the PreToolUse entry.

Hook fires but nothing is blocked

  • Confirm .mneme/project_memory.json exists in the session’s working directory or a parent directory, or set MNEME_MEMORY. With no memory file the hook allows every change silently.
  • Check your decision scope keywords against the file names being edited — see the retrieval section above.
  • Confirm neither MNEME_HOOK_MODE nor the plugin’s mode option is set to warn, which never blocks.
  • Check Claude Code’s debug log for notes from mneme-hook, such as “Failing open”.

Too many false positives / blocks

  • Narrow the rule with include_paths, or check candidate rules with mneme check --mode warn before you import them.
  • Review the triggered anti-patterns with /mneme:context to see if they're too broad.

FAQ

How is the Mneme hook different from CLAUDE.md?
CLAUDE.md is static context: text injected at session start that the model is asked to respect. The Mneme hook runs as a PreToolUse hook — it intercepts Edit and Write calls before they touch the filesystem and can return exit code 2 to block the operation outright. CLAUDE.md is advisory; the hook is enforcement. The two are complementary: CLAUDE.md gives the model context, Mneme blocks violations regardless of whether the model attended to that context. See why prompt memory fails at scale.
Does the hook slow down Claude Code sessions?
Each checked edit, and each changed file at the end of a turn, starts a local mneme check. In our test on Windows with mneme-hq 0.9.2, a hook call took about a second. There is no network round trip, embedding model or vector store; the check is deterministic keyword matching.
What happens if Mneme itself fails?
The hook is designed fail-open: if the Mneme process errors, returns invalid output, or times out, Claude Code is allowed to proceed rather than being blocked. This is a deliberate safety choice — a broken governance layer should not lock the team out of editing their codebase. Strict mode (mneme check --mode strict) in CI is where hard-blocking lives; the editor-time hook prioritizes availability.
Can I use the hook without committing project_memory.json?
Technically yes, but you lose the value. The decision corpus is the source of truth; committing it to the repo means every team member sees the same enforcement, and changes to architectural rules show up in PR review like any other change. Local-only memory recreates the per-engineer drift problem the layer exists to solve.