Memoir
Memory server
First committed by zhangfengcdt
Published in zhangfengcdt/memoir. Publisher page
629 GitHub stars
What it is
Hierarchical Agent Memory with Git-Like Version Control
AutoGineer overview, from the brain’s own files
Memoir is a memory system designed to replace opaque vector databases with a transparent, versioned, and cryptographically secure storage layer. By utilizing ProllyTrees, it enables hierarchical semantic paths instead of UUIDs, allowing for efficient O(log n) lookups. It addresses common AI memory issues like context contamination and token waste by providing branch, commit, merge, and rollback capabilities. Memoir integrates with various agents through plugins and an MCP server, supporting both keyword-based and LLM-powered semantic search.
How it works
Memory model: Not stated
AutoGineer overview, from the brain’s own files
Memoir organizes memory into a hierarchical structure using semantic paths, such as profile.professional.skills.python. It uses a ProllyTree-based store to manage data, allowing for Git-like operations such as branching and versioning. The system includes a classifier to categorize memories and a search engine for retrieval. It provides a CLI for manual interaction, hooks for automatic memory capture in supported agents, and an MCP server for integration with various AI development tools.
| Folder | Role | Files |
|---|---|---|
| .github | agent-config | 10 |
| docs | docs | 41 |
| scripts | scripts | 9 |
| src | source-code | 216 |
| tests | tests | 53 |
| .agents | folder | 1 |
| .claude-plugin | folder | 1 |
| benchmarks | folder | 10 |
| packaging | folder | 3 |
| plugins | folder | 187 |
| static | folder | 2 |
The agent writes
- create_if_missing evidence: CLAUDE.md
Requirements
Runtimes: Python >=3.10
API keys (by name): ANTHROPIC_API_KEY, OPENAI_API_KEY
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Google Antigravity, Kiro, OpenCode, Amp, Roo Code, Cline, JetBrains Junie, Devin (ex-Windsurf)
Set it up
This brain runs code automatically: 7 hooks (claude-code, codex), 2 scripts. Review Makefile, README.md, plugins/claude-code/hooks/hooks.json, plugins/codex/hooks/hooks.json before opening the folder in Claude Code. No step below turns a hook on for you.
Makefile:pip install -e .README.md:curl -LsSf https://astral.sh/uv/install.sh | shplugins/claude-code/hooks/hooks.json:bash ${CLAUDE_PLUGIN_ROOT}/hooks/session-start.shplugins/claude-code/hooks/hooks.json:bash ${CLAUDE_PLUGIN_ROOT}/hooks/user-prompt-submit.shplugins/claude-code/hooks/hooks.json:bash ${CLAUDE_PLUGIN_ROOT}/hooks/stop.shplugins/claude-code/hooks/hooks.json:bash ${CLAUDE_PLUGIN_ROOT}/hooks/session-end.shplugins/codex/hooks/hooks.json:bash "${PLUGIN_ROOT}/hooks/session-start.sh"plugins/codex/hooks/hooks.json:bash "${PLUGIN_ROOT}/hooks/user-prompt-submit.sh"plugins/codex/hooks/hooks.json:bash "${PLUGIN_ROOT}/hooks/stop.sh"
- Not scanned by AutoGineerThe author’s README also describes installing it as a Claude Code plugin marketplace. Read its install section: AutoGineer did not scan that path. https://github.com/zhangfengcdt/memoir/blob/7fd1b3ef704fc554596571d5b9c8b73d7f2b0c5d/README.md#install-a-plugin-for-your-agent
- Not scanned by AutoGineerIts dependencies are installed by pip or uv when you run it and were not scanned by AutoGineer (7 declared). https://pypi.org/project/memoir-ai/
First committed by zhangfengcdt. Published in zhangfengcdt/memoir. Licence: Apache License 2.0.
Usage
AutoGineer overview, from the brain’s own files
- CLI-based memory management
- Automatic context injection for coding agents
- Version-controlled memory branching
- Semantic search across stored facts
- Cross-machine memory synchronization
- “memoir remember "I prefer using Python for data analysis" -p preferences.coding.languages”
- Stores the fact under the specified semantic path for later retrieval.
- “memoir recall "what are my coding preferences?"”
- Performs a semantic search to retrieve the relevant stored memory.
Documentation by zhangfengcdt
Memoir
<div align="center"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://www.memoir-ai.dev/images/memoir.png"> <source media="(prefers-color-scheme: light)" srcset="https://www.memoir-ai.dev/images/memoir-light.png"> <img src="https://www.memoir-ai.dev/images/memoir.png" alt="Memoir Logo" width="200" height="200"> </picture>
Git for AI Memory
Hierarchical Memory with Git-Like Version Control
</div>
Image: Python Image: License Image: Status Image: Website Image: Docs
<p align="center"> <img src="docs/_static/memoir-demo.gif" alt="Memoir demo" width="900"> </p>
Memoir is a high-performance semantic memory system for AI agents that brings Git-like version control to AI memory management. It replaces opaque vector databases with transparent, versioned, cryptographically secure memory storage using hierarchical semantic paths.
Project page: memoir-ai.dev — full overview, demos, and roadmap.
Why agents need versioned memory
AI memory is a Global Variable anti-pattern. Every production agent hits the same three walls: context contamination, token rent, and memory drift. Memoir brings version control to your agent's mind.
Your agent doesn't respect your git state.
Context contamination happens every time you git checkout. Without branch-aware memory, your agent tries to apply experimental refactor patterns to stable production hotfixes.
You're paying "token rent" on a flat file.
Using CLAUDE.md or MEMORY.md as a global store is a cache-killer. Every minor memory update invalidates your entire prefix cache, forcing you to pay full price to re-process your entire conversation.
Your agent's memory is code without version control.
Today's AI memory — CLAUDE.md, vector stores, scratchpads — is treated like an append-only blob. One bad session poisons every future retrieval. Without memoir blame or memoir checkout, there's no way to audit who taught the agent a rule or revert a hallucination without wiping the whole store.
Key Features
Git-like Versioning — Branch, commit, merge, and rollback memories with cryptographic integrity.
Semantic Paths — Replace UUID keys with meaningful paths like
profile.professional.skills.python.O(log n) Lookups — Fast hierarchical search instead of expensive vector operations.
Memory Aggregation — Automatic consolidation of related memories at semantic locations.
Clean Architecture — Proper separation of storage, classification, and search layers.
Multiple Search Engines — Choose between fast keyword-based or intelligent LLM-powered search.
<!-- ## Multi-agent sessions <p align="center"> <img src="docs/_static/memoir-multi-agent.gif" alt="Multiple agent sessions working with Memoir" width="900"> <br> <em>Multiple agent sessions working with Memoir.</em> </p> -->
Install from PyPI
pip install memoir-aiThe distribution name on PyPI is
memoir-ai. The Python import isimport memoirand the CLI ismemoir.
Install a plugin for your agent
Memoir ships as a plugin for several coding and personal-assistant agents. Expand the one you use:
<details> <summary><b>Claude Code</b></summary>
Inside a Claude Code session, run:
/plugin marketplace add zhangfengcdt/memoir
/plugin install memoir@memoirNo manual pip install needed if you have uv on PATH — the plugin auto-resolves to uvx --from memoir-ai memoir when the bare memoir binary isn't installed. Don't have uv? Install it once with the standard one-liner:
curl -LsSf https://astral.sh/uv/install.sh | shThat's enough — the plugin handles the rest. It registers hooks for session start, user-prompt-submit, and stop, so your project gets automatic context injection and auto-captured memories. See the Claude Code plugin guide for the full slash-command and hook reference.
</details>
<details> <summary><b>Codex</b></summary>
Memoir's Codex plugin is distributed from this repository's Codex marketplace. In Codex, run /plugins, add the memoir marketplace from zhangfengcdt/memoir, restart Codex if prompted, then choose Memoir Plugins and install memoir.
You can also register the marketplace from the CLI:
codex plugin marketplace add zhangfengcdt/memoirWhile developing from a local checkout, use codex plugin marketplace add /absolute/path/to/memoir instead. The repo marketplace lives at .agents/plugins/marketplace.json and points Codex at ./plugins/codex, relative to the repository root.
No manual pip install needed if you have uv on PATH — the Codex plugin resolves the Memoir CLI the same way the Claude Code plugin does: memoir on PATH first, then uvx --from memoir-ai==<pinned> memoir, then uv tool run --from memoir-ai==<pinned> memoir.
Enable Codex hooks with [features].hooks = true in ~/.codex/config.toml or pass --enable hooks for a smoke run. The Codex plugin ships lifecycle hooks; memory-recall, memoir-onboard, memoir-remember, memoir-status, and memoir-ui skills; Codex-specific transcript parsing; and local marketplace metadata. See the Codex plugin guide for setup, limitations, and the real Codex smoke-test flow.
</details>
<details> <summary><b>Hermes</b></summary>
Memoir is a memory provider for Hermes, the Nous Research personal-assistant agent. Install the plugin into your Hermes home and activate it:
hermes plugins install zhangfengcdt/memoir/plugins/hermes # or: cp -r plugins/hermes ~/.hermes/plugins/memoir
hermes memory setup # choose "memoir"Hermes then auto-captures durable facts (people, schedule, preferences, standing instructions) each turn and exposes memoir_recall / memoir_remember / memoir_forget / memoir_sync / memoir_status tools. Capture/classification run on your host-selected model via direct provider APIs (never the claude CLI). See the Hermes plugin guide for install, configuration, model selection, and proxy routing.
</details>
<details> <summary><b>OpenClaw</b> (community)</summary>
Memoir is a memory plugin for OpenClaw, the personal-assistant gateway. It lives in its own repo, zhangfengcdt/openclaw-memoir. Install it into the memory slot, allow conversation access, and restart the gateway:
openclaw plugins install https://github.com/zhangfengcdt/openclaw-memoir # or: --link /path/to/openclaw-memoir
openclaw config set plugins.entries.memory-memoir.hooks.allowConversationAccess true
systemctl --user restart openclaw-gatewayOpenClaw then auto-captures durable facts each turn (remembered across your sessions by default) and exposes memoir_recall / memoir_remember / memoir_forget / memoir_status tools plus an in-chat /memoir command. Set a capture model + provider key in config — plugins.entries.memory-memoir.config.{model,apiKey} (OpenClaw keeps its own keys in a secret store; use a temperature=0-capable model like anthropic/claude-haiku-4-5). See the OpenClaw plugin guide for configuration, scoping, model selection, and tool-profile notes.
</details>
<details> <summary><b>OpenCode</b> (community)</summary>
opencode-memoir is a community-maintained plugin that brings Memoir's long-term memory workflows to OpenCode through its native plugin system. Follow the install instructions in that repository.
</details>
<details> <summary><b>Any MCP host</b> (Claude Desktop, Cursor, Cline, Windsurf, VS Code, Zed, …)</summary>
Memoir ships an MCP server (memoir-mcp) on the official Model Context Protocol SDK, so any MCP-compatible host gets git-versioned memory — including the memoir_branches / memoir_checkout / memoir_commits versioning tools no other memory server has. No install needed if you have uv; add to your host's mcpServers config:
{
"mcpServers": {
"memoir": {
"command": "uvx",
"args": ["--from", "memoir-ai[mcp]", "memoir-mcp"],
"env": { "MEMOIR_STORE": "~/.memoir/mcp" }
}
}
}Recall is LLM-free by default (keyless), with opt-in single-call or multi-level tiered LLM modes; the store auto-creates on first use. See the MCP server guide for per-host config (VS Code/Zed/Continue/LibreChat differ slightly), recall modes + LLM setup, remote HTTP for ChatGPT/Claude.ai connectors, and the one-click Claude Desktop .mcpb.
</details>
Quick look
Memoir's CLI defaults to Anthropic claude-haiku-4-5 as of v0.1.6 — set your key first:
export ANTHROPIC_API_KEY="sk-..."Note that if you do not set any model api keys, it will try to use the claude -p command to authenticate via claude code's subscription.
Then create a store and round-trip a memory:
# 1. Create a memoir store
memoir new my-memoir-store
cd my-memoir-store
# 2. Store with an explicit path (offline, no LLM call)
memoir remember "Sarah prefers tabs and 2-space indents" -p preferences.coding.style
# 3. Store with auto-classification (LLM picks the path; needs API key)
memoir remember "I work in Pacific time"
# 4. Read back by path (offline)
memoir get preferences.coding.style
# 5. Semantic search (LLM-backed)
memoir recall "what does Sarah prefer?"
# 6. Watch a file by indexing it on the fly
memoir watch add ~/papers/transformer.pdf -n research
# 7. Search the indexed content
memoir search "transformer attention mechanism"
# 8. Open the visual explorer (auto-opens in your browser)
memoir uiCurious what the UI looks like before installing? Browse the UI Gallery.
Prefer a different model? memoir recall "..." --model gpt-4o-mini (needs OPENAI_API_KEY), or set MEMOIR_LLM_MODEL in your shell. Resolution order: --model flag → MEMOIR_LLM_MODEL → claude-haiku-4-5.
See the full Quickstart for the Python API and version-control workflow.
Documentation
Full docs live at zhangfengcdt.github.io/memoir:
Quickstart — five-minute tour of the core loop.
CLI Reference — every command, flag, and exit code.
UI — the visual explorer (Tree / Graph / Timeline / Places +
/stats).Claude Code — plugin install, slash commands, hooks, lifecycle.
Codex — plugin install, hooks, skills, transcript parsing, smoke-test evidence.
Architecture — taxonomy, classifier, store, search.
API Reference — Python SDK.
Examples — context branching, memory debugging, reproducible testing.
Contributing
Memoir is alpha and contributions are very welcome — especially from people building coding agents, since that's the audience we're optimizing for. Good first paths in:
Pick an issue from the issue tracker or open one describing a gap.
Fork the repo, branch off
main, and runmake cibefore opening a PR (lint, tests, docs build must be green).Bug reports with a minimal reproducer and benchmark / taxonomy proposals for coding-agent use cases are particularly appreciated.
License
Apache License 2.0 — see LICENSE.
docs/claude_code.md
Claude Code Plugin
Memoir ships a first-class plugin for Claude Code. Drop it in and memoir becomes native to your coding sessions: context injected on session start, durable facts auto-captured at turn end, and a full suite of slash commands for everything in between.
The plugin lives in the repo at plugins/claude-code/.
Install
Inside a Claude Code session, run:
/plugin marketplace add zhangfengcdt/memoir
/plugin install memoir@memoirThe first command registers the memoir GitHub repo as a plugin marketplace; the second installs the memoir plugin from that marketplace. Hooks take effect on the next session start.
Each project gets its own memoir store under ~/.memoir/<slug>/, derived from your cwd. Override by exporting MEMOIR_STORE=/path/to/store. If you use git worktree, all linked worktrees of one repo share a single store keyed on the main worktree's path — set MEMOIR_STORE per worktree to opt out.
What ships
| Component | Count | Role |
|---|---|---|
| Slash commands | 7 | Manual memory ops, branch sync, admin, UI launch, capture feed pane |
| Skills | 2 | Auto-invoked: recall + codebase onboarding |
| Lifecycle hooks | 4 | Context injection + auto-capture |
| Helper scripts | 5 | Store path, branch sync, UI control, status line, pane text fallback |
| Mod (Claude Code 2.1.287+) | 1 | In-process hooks module: /memoir:pane capture feed + hint-line status |
Slash commands
| Command | Purpose |
|---|---|
/memoir:onboard [--force] | Populate or refresh the codebase:onboard snapshot. |
/memoir:remember <fact> | Capture a memory. -p <path> skips classification. Prompts you to resolve conflicts when the key already exists (see Interactive conflict resolution). |
/memoir:recall <query> | Recall from prior sessions (delegates to the memory-recall skill). |
/memoir:status | Branch, commit count, memory count, namespaces. |
/memoir:sync [branch ...] | Guided promotion of unmerged memoir branches into main (select UI: merge all / choose / ignore / snooze / delete branches). With arguments, merges those branches directly. See Branch sync. |
/memoir:ui | Launch or re-open the web UI (readonly, LLM off by default). |
/memoir:push [--force] | Push every local memoir branch of this repo to memoir cloud: logs in and links the store when needed (offering an agent account when the machine has no login), skips branches already merged into main, parks a branch the cloud has moved ahead of under <branch>-conflict-<stamp> (or overwrites it with --force, keeping the cloud's tip under refs/memoir/backup/). See Cloud push. |
/memoir:pane | Open the memoir pane (Memories, Recent, Metrics tabs). Needs Claude Code 2.1.287+; older versions print a text summary. See Capture feed pane. |
Admin operations (forget, taxonomy) are available via the memoir CLI directly — they were dropped from the slash-command surface to keep the in-session UX focused on the everyday actions.
Skills
| Skill | Namespace | Role |
|---|---|---|
memory-recall | default | User-captured facts. Picks taxonomy paths with summarize, batches get, never invokes nested LLMs. Runs in a forked context. Configured default on with aggressive triggering so remembered preferences are never silently skipped. |
memoir-onboard | codebase:onboard | Maintains a high-level repo snapshot that seeds future sessions via SessionStart injection. |
The split is deliberate: recall owns user-captured facts; onboard owns codebase structure.
Read/write asymmetry
By design:
Reads are auto-triggered via skills. The agent pulls context when it thinks it might need to, without the user asking.
Writes are an explicit slash command.
/memoir:rememberstays as a command — not a skill — because theStophook already handles auto-capture; this command is the manual escape hatch. Deletion (memoir forget) lives on the CLI rather than as a slash command, kept explicit for safety.
Interactive conflict resolution
/memoir:remember resolves write conflicts interactively by default. When the target key is already occupied, the command does not silently overwrite or append — it surfaces the existing value and lets you decide.
Mechanically, the command issues its write with --merge-policy reject: fresh keys are written immediately, but an occupied key is refused and returned as a conflict (nothing is clobbered). The agent then shows you the comparison and the options, and waits:
● Conflict at knowledge.coding.style
existing: use spaces
incoming: use tabs
How should I save this? [replace / append / merge / skip]replace — overwrite with the new value (keep only the latest)
append — keep both (the new value becomes another entry)
merge — LLM-consolidate old and new into one statement (slower; needs a model)
skip — leave the existing memory unchanged
Your choice is applied as a second, explicit write. Notes:
Conflict-free captures are unaffected — a write to a new key just succeeds.
To capture without prompting, pass an explicit policy, e.g.
/memoir:remember "…" -p <path> --merge-policy replace; the command forwards it and skips the prompt.This applies only to the manual command. The
Stop-hook auto-capture stays non-interactive (it can't prompt mid-turn) and resolves conflicts by the per-type default — see How auto-capture decides what to remember.
Lifecycle hooks
Configured in plugins/claude-code/hooks/hooks.json:
| Event | Script | Timeout | Async | Purpose |
|---|---|---|---|---|
SessionStart | session-start.sh | 15s | — | Inject store status, branch/commit state, onboard snapshot, and "memory available" hints. Auto-promotes memoir branches whose code branch merged into main; surfaces unmerged branches with a gated /memoir:sync offer. |
UserPromptSubmit | user-prompt-submit.sh | 10s | — | Surface matching memory hints for the current prompt. |
Stop | stop.sh | 180s | yes | Parse the transcript and auto-capture durable facts into the taxonomy. |
SessionEnd | session-end.sh | 5s | yes | Cleanup. |
Shared helpers: hooks/common.sh, hooks/parse-transcript.sh.
Helper scripts
| Script | Role |
|---|---|
derive-store-path.sh | Maps the current cwd to ~/.memoir/<slug> (linked worktrees collapse onto the main worktree's slug). Respects $MEMOIR_STORE. |
push-cmd.sh | Backing script for /memoir:push: plan / login-start / login-wait / login-key / login-agent / link / push / push-conflict / push-force / conflict-name, each one line of JSON. |
sync-cmd.sh | Backing script for /memoir:sync and the session-start offer: list / dry-run / merge / ignore / snooze / decline / prune (branch deletion; refuses main and the current branch). |
memoir-ui-ctl.sh | start / stop / status for the web UI, with pidfile bookkeeping so repeated /memoir:ui calls reuse the same server. |
statusline.sh | Renders memoir state into Claude Code's status line, e.g. memoir: feature/foo · 14 memories. Only needed on Claude Code < 2.1.287 — newer versions get the same line from the mod with no settings edit. |
pane-cmd.sh | Text fallback for /memoir:pane on Claude Code < 2.1.287: status line plus the last captures, from git metadata alone. |
Capture feed pane and built-in status line (/memoir:pane)
Auto-capture is deferred to the async Stop hook, so a turn's writes land in the store seconds after the turn ends and nothing in the session shows them. On Claude Code 2.1.287 or newer the plugin loads a mod — an in-process hooks module listed under modules in hooks/hooks.json, source in hooks/mod/ — that surfaces them. It is read-only and purely additive: the four shell hooks, the skills and every other command are unchanged.
Hint line. The line under the prompt gains a suffix, memoir: main · 18 memories, and +N this turn once the turn's non-metric captures have landed (cleared at the next turn). No store → nothing is shown. If statusLine.command in your settings already references statusline.sh, the mod skips the suffix so the state is not shown twice; statusline.sh stays for older Claude Code versions.
/memoir:pane. Three tabs:
| Tab | Shows | Keys |
|---|---|---|
| Memories (default) | Off main, the keys that differ from main, sorted, each with one line of its value and its mark: + new on this branch, ~ changed here (main holds another value), - on main only (forgotten here); the header counts every key and the differing ones. On main, every key. metrics.* keys are left to the Metrics tab. the all branches button lists every other branch's differences from main under the keys — the ten most recently committed branches, the latest on top, each with its count and the age of its last commit; their keys open read-only. A filter: field narrows the list (every section) to keys containing the text; the taxonomy button shows the taxonomy above the keys (paths grouped by first segment with counts, Enter on a group lists its leaves). Enter or a click on a key opens its detail: the value, or for ~ and - main's value (- lines) beside the branch's (+ lines), and the actions, each behind a confirmation: d forget key for any key on this branch (+, ~, unchanged), u restore main's value for ~ and restore from main for -. On main the detail shows the value and offers forget key. the merge to main memory button merges the branch into main and asks what to do with keys changed on both sides: ours keeps main's value, theirs takes the branch's (the CLI's merge -S strategies), both merges with ours and then appends the branch's value to main's with remember --merge-policy append — then checks the branch out again (merge --into would otherwise leave the store on main). The pull from main memory button, shown when main holds a key this branch lacks or has changed, merges main into the branch instead (memoir merge main on the branch, no checkout): what was captured on main since the branch left comes over, and a key changed on both sides takes the same choice from the branch's side — ours keeps the branch's value, theirs takes main's, both keeps the branch's and appends main's. The comparison is memoir diff main HEAD, which reads both commits from the tree without a checkout; a memoir older than that answer falls back to git log main..HEAD marks without main's values (no restore). | 1 tab · type in filter: · click or Tab+Enter: taxonomy, all branches, merge to main memory, pull from main memory · Enter/click a key · b back · d forget / u restore, then y/n · merge or pull then o/t/b · r refresh |
| Recent | On a branch, its commits that are not on main (after a merge of the branch into main, those dated after it); on main, the latest 10 commits. Newest first, each by its title (Store <key> in default, Delete …, Merge branch …); ● for this turn's, ○ older; age and two lines of a stored value. metrics.* writes hidden by default. Enter or a click on a commit opens it: sha, time, and what it changed — each key with the value before (- lines) and after (+ lines), from memoir diff <sha>~1 <sha>; a memoir without key-level diffs shows the key's current value instead. | 2 tab · Enter/click a commit · b back · m toggle metrics · r refresh |
| Metrics | One bar chart per metric across branches, from the metrics.turn.<branch> accumulators: turns, tool calls, tool errors, average latency (s), output chars, tool result chars. The current branch leads every chart; a branch with no sample for a metric is left out of that chart rather than drawn as zero. The same figures as the web UI's Statistics → Metrics tab. | 3 tab · r refresh |
The header carries the memoir mark in box drawing, the title with the store's path beside it (dimmed, home shown as ~, cut from the left when the pane is narrow), the tabs, and the status. The status (<branch> · N memories · N commits) is three shortcuts: the branch opens Metrics, the memory count Memories, the commit count Recent. On a branch the commit count is what sits on top of main, written +N changes (after a merge of the branch into main, only commits dated after it); on main it is every commit. While the Recent tab hides metrics.* writes (its default) the count leaves them out too, so it matches the rows; m on that tab shows and counts them. The pane docks beside the transcript in a wide fullscreen terminal and seats inline above the prompt otherwise; ctrl+x tab focuses it, ctrl+x x closes it. Where nothing can draw a pane (claude -p, the Agent SDK) the command answers with a plain-text summary.
How it watches the store. A 3 s timer stats <store>/.git/HEAD and the current branch's ref — no subprocess. Only when either moved does it run git log --format=%H%x09%ct%x09%s (the latest 10 on main, up to 20 of main..HEAD on a branch), parse the Store <key> in <namespace> subjects, read the memory count the shell hooks cache in plugin-statusline-cache, and fetch the new keys' contents in one batched memoir --json get through scripts/memoir-cli.sh (the same resolver chain as the hooks — never bare memoir, since hook environments lack the venv PATH). Every default-namespace key the summarize call lists is then read in one more batched get (the Memories tab's values, and among them the metrics.turn.* accumulators for the Metrics tab, which change every turn); off main one memoir --json diff main HEAD lists every key that differs with both values (read from the tree, so the store's checked-out branch never moves — get --branch would check main out and back, and a concurrent Stop-hook write would land on main); with a memoir that predates key-level diff output, two git log subject listings tell which keys the branch wrote and which main ever stored instead (after a merge of the branch into main, whose merge commit has one parent, only the branch's writes dated after it count). turn.start records the head commit as the baseline; everything committed after it is "this turn". This catches every write path: the Stop hook, /memoir:remember, the CLI, memoir pull. The pane itself writes only what the Memories tab asks for, through the same CLI: the detail's confirmed action (memoir forget --force, or memoir remember -p <key> --replace with main's value) and p's merge (memoir merge <branch> --into main -S ours|theirs, for both followed by one remember --merge-policy append per both-sides key, then memoir checkout <branch>; polling pauses meanwhile), then re-reads. Drawn state lives in $.state, so it survives a hot reload and a /clear only resets the turn baseline.
Cloud push (/memoir:push)
/memoir:push [--force] sends this repo's memoir store to memoir cloud in one go. The agent drives scripts/push-cmd.sh, which prints one line of JSON per step; the agent never composes cloud plumbing itself.
Plan. The store, the gateway (
MEMOIR_CLOUD_URL, else the linked remote's, else the saved default login), the login state, theoriginlink, and the branches to push:mainfirst, then the rest by last commit, newest first. Branches already merged intomain(an ancestor of it, or carrying a/memoir:syncmarker newer than their tip) and the cloud-ownedcloud/*branches are left out and listed as skipped.Login. No saved login for the gateway and no
originyet: the command first asks whether to create an agent account for this repo (memoir login --agent: a throwaway cloud account owning one store, its token kept with the store, capped and expiring untilmemoir claimmoves it into your account) — that question is where your permission is captured — or to sign in. Signing in starts a browser approval (memoir login --no-browser), shows the link and code, and waits up to four minutes for the click. If that cannot work (headless), it offers to take an API key and saves it withmemoir login --with-key. The key is never echoed.Link. No
origin:memoir remote add <handle>/<repo name>when that cloud store exists, elsememoir push --create, which creates and links it. A remote that points elsewhere is reported, never relinked.Push.
memoir push --branch <b>per branch. The first push uploads every chunk the cloud lacks; the rest are git pushes.Conflicts. The CLI exits 6 when the cloud branch has commits this machine lacks. Without
--forcethe branch is pushed again as<branch>-conflict-<YYYYMMDD-HHMM>(memoir push --to), to pull and merge by hand on the other side. With--force, only such branches are overwritten (memoir push --force), and the cloud's previous tip is kept locally underrefs/memoir/backup/<branch>first.Summary.
[mode=pushed|partial|error], one line per branch, and the counts.
Why the command is still /memoir:pane. Mods register commands without a plugin prefix, so the slash command is the thin commands/pane.md, and the mod answers its command.run before the prompt runs. On a Claude Code that cannot load mods the markdown runs instead and prints scripts/pane-cmd.sh's text summary — the one visible difference on old versions.
Tests.
claude plugin validate plugins/claude-code # lists the hooks, $ calls and state keys the module uses
claude plugin test plugins/claude-code # hooks/mod/*.test.tsx — no store, no memoir, no LLMThe tests answer the host (git, the CLI, the filesystem, settings) from memory and cover capture parsing, metrics.* filtering, the +N baseline, the no-store path, the statusline.sh coexistence skip, the plain-text fallback, and the pane's tabs and toggles. Claude Code lays this build's API declarations in plugins/claude-code/.claude-plugin/types/ whenever it loads the mod from the folder; that directory is gitignored.
Lifecycle
A session flows through four hook events. Steps 2–4 loop once per user prompt; step 5 runs once at the end.
sequenceDiagram
actor You
participant Claude as Claude Code
participant Plugin as memoir plugin hooks
participant Store as memoir store
rect rgb(30, 50, 70)
Note over Claude,Store: 1. SessionStart — session-start.sh (sync)
Plugin->>Store: read status, branch/commit, codebase:onboard
Store-->>Claude: inject snapshot + "memory available" hints
end
rect rgb(30, 55, 45)
Note over You,Store: 2–4. Per user prompt (loops)
You->>Claude: prompt
Plugin->>Store: 2. UserPromptSubmit — recall determine (sync)
Store-->>Claude: "[memoir] memory available" hints
Claude->>Store: 3. memory-recall skill (forked, on-demand)
Store-->>Claude: summarize → pick prefixes → get → facts
Claude-->>You: response
Plugin->>Store: 4. Stop — classify + auto-capture (async)
end
rect rgb(60, 45, 30)
Note over Claude,Store: 5. SessionEnd — session-end.sh (async)
Plugin->>Store: cleanup
endTwo properties to notice:
Reads happen eagerly, writes happen lazily. Every prompt passes through
UserPromptSubmit(step 2) and potentially firesmemory-recall(step 3) — the agent pulls context without the user asking. Auto-capture is deferred toStop(step 4), which is async so it never blocks the turn.Namespaces split along read/write paths.
memory-recallworks againstdefault(user-captured facts, written by theStophook or/memoir:remember).memoir-onboardworks againstcodebase:onboard(repo snapshot, written by/memoir:onboard, replayed by theSessionStarthook). Two namespaces, two lifecycles, no overlap.
The admin surface — /memoir:ui, /memoir:status, /memoir:sync (slash commands), plus memoir taxonomy and memoir forget (CLI) — sits outside this lifecycle: it's explicit user invocation, not hook-driven. (The exception is /memoir:sync's session-start integration — auto-promotion and the gated offer — described in Branch sync.)
Session context injection
SessionStart writes a single JSON object to stdout that Claude Code reads as the session preamble: {"systemMessage": ..., "hookSpecificOutput": {"hookEventName": "SessionStart", "additionalContext": ...}}. The additionalContext block is assembled in order from up to four parts, each conditional on having something to say. Every part comes from raw memoir get/summarize calls — no LLM is invoked at session start.
| Block | Source | Always on? |
|---|---|---|
| Store summary | memoir status + memoir summarize taxonomy | Yes — branch, user-memory count, namespace counts. |
| Default-namespace keys | memoir summarize --keys "*" -n default | Yes (when default has keys). Capped at 200, grouped by L1 prefix. |
| Auto-promotion report | auto_promote_merged_branches (runs before the unmerged scan) | Only when a memoir branch's code branch merged into main since the last session. See Branch sync. |
| Unmerged-branch detector | git rev-list + sync markers over the store's branches | Only when the code branch is main. Mid-flight on a feature branch, other branches' unmerged work is noise. Appends a gated /memoir:sync auto-offer at ≥5 unmerged commits. |
| Codebase snapshot | memoir summarize --keys "*" -n codebase:onboard + batched get | Default on (MEMOIR_ONBOARD_INJECT=1). See Codebase snapshot below. |
The status line itself follows the same conditional shape: [memoir] <branch> · <N> memories [· capture disabled] [· N branches unmerged] [· auto-promoted N merged branches] [· concurrent session warning].
Shape of the default-keys block:
# default namespace keys
(12 keys, grouped by L1 prefix)
knowledge (7):
- knowledge.technical.branching
- knowledge.technical.merge
...
preferences (1):
- preferences.communication.tone
metrics (1):
- metrics.turn.mainThis is just the index — agents memoir get <key> the ones they care about, paying for content only on demand.
Branch sync (/memoir:sync)
Memory branches auto-track code branches, so unmerged captures accumulate on feature/* memoir branches until they're promoted to main. /memoir:sync is the guided promotion flow, and three lifecycle mechanisms keep the branch population from overwhelming users over time.
Turning off auto-matching. memoir branch-match off disables the SessionStart/UserPromptSubmit/Stop hooks' branch-following behavior for the current store (memoir branch-match on re-enables it, memoir branch-match alone reports status). Useful when deliberately working a memoir branch that doesn't match the code branch. The web UI exposes the same toggle as a button. State is a marker file, plugin-auto-match-disabled, alongside the other per-store files below.
The command. /memoir:sync drives the whole flow through Claude Code's select UI (AskUserQuestion) — this is why it's a slash command rather than a hook or forked skill: the select UI is a model tool available only in the main conversation.
┌─ Memoir sync ──────────────────────────────────────────────┐
│ You have 3 memoir branches with 12 unmerged commits: │
│ memoir/feature/a — 7 commits, last capture 2d ago │
│ memoir/feature/b — 4 commits, last capture 5d ago │
│ ... What would you like to do? │
│ ❯ 1. Merge all (Recommended) │
│ 2. Choose branches (multiSelect picker) │
│ 3. Ignore, snooze, or clean up │
│ 4. Not now │
└────────────────────────────────────────────────────────────┘Merging shows a dry-run preview (+N new keys, M updated) and then applies — no second confirmation, because the promote is additive-only (never deletes keys, no conflicts) and lands as one revertable commit that restores the prior branch. /memoir:sync feature/x skips all questions and merges directly. All state reads/writes go through one backing script, scripts/sync-cmd.sh (list / dry-run / merge / ignore / snooze / decline / prune), so the agent never composes raw CLI plumbing or hand-edits state files.
Auto-promotion on code-branch merge. When a code branch's work lands in main via a merge commit, the work unit is complete — so the next SessionStart on main promotes its memoir branch automatically and reports it in the status line. The heuristic requires the branch tip to be reachable from main but not on main's first-parent chain: a branch with no unique code commits (its tip is just an old mainline commit) is never swept up, so in-progress branches stay isolated. Squash- and rebase-merged branches aren't detectable this way; they fall through to /memoir:sync or stale cleanup. Disable with MEMOIR_AUTO_PROMOTE_MERGED=0.
Gated auto-offer. When total unmerged work reaches 5 commits and no snooze is active, the SessionStart context asks the agent to offer the sync once that session — immediately if the first prompt is low-stakes, otherwise at the end of the turn. Declining backs off automatically: 1 day, then 7, then 30 on consecutive declines; an explicit snooze resets the escalation. Snooze suppresses only the offer — the status-line count and the informational branch list always render.
Branch deletion. /memoir:sync can delete any memoir branch except main and the currently-checked-out one (the backing script refuses both) — always behind an explicit multiSelect pick, never automatically. The picker flags risk per branch: stale entries — inactive ≥60 days (MEMOIR_STALE_BRANCH_DAYS to override) and either already synced or their code branch gone (work abandoned) — lead the list as safe to remove; unsynced branches carry a DISCARDED warning with their unmerged-commit count. Pruning also removes the branch's sync marker and ignore-list entry, so the store's branch population stays bounded over time.
Why a branch stops being flagged. Memoir's promote rewrites patches on main rather than creating a two-parent merge commit, so git-graph checks can't detect "already merged". Instead, every successful promote writes a timestamp marker to <store>/.git/plugin-synced-branches/<branch>; a branch is considered synced while the marker is newer than its last capture, and resurfaces automatically if new captures land afterwards.
State files (all local-only, under the store's .git/):
| File | Contents |
|---|---|
plugin-synced-branches/<branch> | Epoch of the last successful promote (written by the CLI). |
plugin-ignored-branches | One branch name per line; delete a line to unignore. |
plugin-merge-prompt-cooldown | Line 1: snoozed-until epoch. Line 2: consecutive-decline count. |
plugin-auto-match-disabled | Presence disables branch auto-matching (memoir branch-match off / UI toggle); absence (default) means enabled. |
Codebase snapshot (codebase:onboard)
A persistent, high-level repo overview written by /memoir:onboard and replayed by SessionStart so fresh sessions start warm with a structured map of the codebase.
Layout. Keys live in the codebase:onboard namespace, grouped by L1 root:
| Root | What it captures |
|---|---|
goal.primary / goal.non_goals | What the project is and isn't. |
structure.entrypoints | CLIs, servers, main functions. |
structure.modules.<fs_path> | One key per major module (src_memoir_cli, plugins_claude_code, …). 1–3 line role summary. |
test.strategy | Test layout + how to run. |
debug.common | How to reproduce common failure modes. |
deploy.targets | How the code ships (CI workflows, packaging). |
document.sources | Where canonical docs live. |
rules.* | Project rules beyond CLAUDE.md, one key per rule. |
lessons.* | Hard-won facts from prior incidents, one key per lesson. |
references.* | External links / upstream conventions. |
_meta.last_onboard.{commit,date,memoir_commit,mode} | Staleness anchors. |
Each value is ≤ ~500 chars; the SessionStart compact view takes the first sentence and caps a root's joined children at 140 chars.
Refresh paths. /memoir:onboard probes _meta.last_onboard.commit and picks one of three:
cold — no prior snapshot. Full scan:
ls -d */, skimCLAUDE.md/README*/pyproject.toml/Makefile/.github/workflows/*.yml,git log --oneline -20. Write everygoal.*/structure.modules.*/rules.*/lessons.*/references.*key withmemoir remember -p <path> -n codebase:onboard(the-pflag bypasses the LLM classifier — fast and deterministic).warm — code HEAD has moved.
git log --stat <last_sha>..HEADenumerates changed paths; only the affectedstructure.modules.*keys and any new lessons are rewritten. Typically 1–5 keys per pass.meta-only — code HEAD unchanged. Bumps
_meta.last_onboard.dateso the staleness indicator renders fresh; no narrative keys touched.
--force always uses the cold path.
Branch behavior. The snapshot lives in the codebase:onboard namespace, not default — BranchService.promote_branch only carries the default namespace, so codebase:onboard stays per-branch. This is intentional: a feature branch can carry its own structural notes without leaking them to main until the user explicitly chooses to.
Staleness. SessionStart flags the snapshot stale="true" when _meta.last_onboard.date is more than 30 days old, and appends a (snapshot is stale — run /memoir:onboard to refresh) hint. memoir sync-branch calls update_onboard_meta_after_sync on the merged branch so the meta keys stay truthful even when the user hasn't re-run /memoir:onboard.
Non-git folders (project:onboard)
The plugin treats non-git folders as a first-class case rather than a degraded git mode. This is the home for running Claude Code over non-code projects — writing (drafts, manuscripts, research notes), video editing (clips, transcripts, project files), bookkeeping (statements, receipts, spreadsheets), and similar mixed-media folders.
Contract.
| Surface | Git folder | Non-git folder |
|---|---|---|
| Branches | Auto-tracks code branch | Locked to main |
| Status line | [memoir] <branch> · N memories | [memoir] main · N memories |
| Stop auto-capture | Captures to current memoir branch | Captures to main |
/memoir:sync, memoir sync-branch (CLI) | Operate normally | Nothing to do — only main exists, so the unmerged/stale lists are empty by construction |
/memoir:onboard | codebase:onboard cold/warm based on code SHA | project:onboard cold/warm based on filesystem snapshot hash |
SessionStart injection | Renders codebase:onboard block | Renders project:onboard block |
Stats / memoir log, graph, tree | Identical | Identical |
project:onboard namespace layout.
| Key | Contents |
|---|---|
summary.overview | 2–4 sentences auto-composed by a deterministic shape detector (writing / bookkeeping / video-editing / mixed). |
structure.shape | One of writing-shape, bookkeeping-shape, video-editing-shape, mixed. |
structure.tree | Pruned directory tree (depth ≤ 3). |
structure.totals | JSON: {file_count, dir_count, total_bytes, kind_histogram}. |
files.<sanitized_path>.meta | {size, mtime, ext, kind} per file (/ and . → _ for the path segment). |
files.<sanitized_path>.summary | Structured key=value blob from a per-kind extractor. Always begins with kind=…. |
_meta.last_onboard.{date,mode,snapshot_hash,memoir_commit,file_count} | Refresh anchors. |
Deterministic extractors — no LLM at index time. The skill runs plugins/claude-code/skills/memoir-onboard/extractors.py (stdlib-only Python) once per file. One function per kind, all bounded:
prose / markdown — frontmatter
title:→ first H1 → first non-empty line; first 50 / last 20 words; word count; top non-stopword terms.csv / tsv — sniffed delimiter, columns, 16-row sample, streamed row count, numeric columns. Adds
shape=ledgerwhen columns include date+amount+category-like patterns.office-zip (
.docx,.pptx,.xlsx,.epub) — stdlibzipfile+xml.etreereadsdocProps/core.xml, sheet names, slide count, paragraph count, EPUB manifest.pdf — metadata-only at v1 (file size, magic bytes, version). Real text extraction is a tool entry.
video-project (
.fcpxml,.kdenlive,.prproj,.aep) — XML parse for project name, clip count, duration; binary.aepis metadata-only.json / yaml — top-level keys, max depth, item count.
srt / vtt — first cue, last cue, cue count, total duration.
image / audio / video — extension-derived
kindplus stdlib-cheap header parses (PNG dimensions from IHDR, WAV duration, etc.). Anything that needs a real codec stays metadata-only.
Files larger than 50 MB get metadata-only treatment regardless of kind, so raw video and audio never enter the snapshot.
Cold / warm / meta-only paths. Same shape as codebase:onboard, but keyed off a filesystem snapshot hash (sha256 over sorted (path, size, mtime_ns) tuples) instead of a code SHA:
cold — no prior snapshot. Walk → run every extractor → write
files.*keys,summary.overview,structure.tree,structure.totals,structure.shape→ stamp_meta.*.warm — snapshot hash differs. Diff path-by-path: added → run extractor + write; deleted →
memoir forget; modified → re-run extractor + write; unchanged → skip. Refresh aggregate keys and re-stamp meta. Falls through to a full cold rewrite when more than ~30% of files changed.meta-only — snapshot hash unchanged. Bump
_meta.last_onboard.dateonly.
Pluggable tool registry. v1 ships with zero tool entries — every cold and warm pass is free, offline, and stdlib-only. To add an external tool (e.g. Whisper for audio transcription, ExifTool for images, a vision LLM for image captioning), drop a YAML or JSON config:
# ~/.memoir/onboard-tools.yaml (user-global)
# <project>/.memoir/onboard-tools.yaml (project-local; merged after global)
audio:
- name: whisper
command: "whisper {path} --output-format json"
timeout_s: 60
image:
- name: claude-vision
command: "vision-caption.sh {path}"
timeout_s: 30Per kind, the stdlib extractor always runs first; configured tools then run and merge their JSON output under extractor.<tool_name>.<field> keys, so the consumer LLM can tell deterministic fields apart from tool-derived ones via the extractor.stdlib.fields=[…] provenance line. Results are cached at <store>/.git/plugin-extractor-cache/<sha256-of-file-content>.<tool>.json so warm-mode reuses tool output when file content is unchanged. Failures are silent — tool errors and timeouts are logged to /tmp/memoir-hook.log; the blob is emitted with stdlib fields only.
Excludes. Default exclusion globs cover OS / editor cruft (.DS_Store, ~$*), code build artifacts (node_modules, __pycache__, dist, .venv), and video / audio editor caches (Adobe Premiere Pro Auto-Save/, *.fcpcache/, Render Files/). Add project-local entries via .memoir/onboard-excludes.txt (gitignore syntax, one glob per line).
Store-mode drift guardrail (warning-only). A folder that flips between non-git and git states (running git init on an existing project, or rm -rf .git-ing a tracked folder) keeps the same store path — they share ~/.memoir/<slug>. The plugin records the mode at first store creation in <store>/.git/plugin-store-mode. Subsequent SessionStarts compare the marker to the current state; on mismatch, it surfaces a one-block warning alongside the normal status line:
[memoir] note: store mode drift
This store was created in `non-git` mode; the project directory is now `git`.
Captures continue, but branch auto-matching and the SessionStart onboard
injection now use the new mode — earlier non-git-mode data may be on a
different memoir branch (run `memoir branch list` to inspect).
To suppress: `memoir checkout main` and update the marker with
`echo git > <store>/.git/plugin-store-mode`.Captures keep working through the warning — it's informational, not enforced. The marker is auto-backfilled the first time an old (pre-guardrail) store is observed, so no warning fires for stores that pre-date this feature.
How auto-capture decides what to remember
The Stop hook's capture stage (stop.sh:69-end, gated by MEMOIR_NO_CAPTURE=1) makes one haiku call per turn that does both extraction and classification in one shot — emitting taxonomy-classified durable facts directly, bypassing memoir's internal LLM classifier chain. Net effect: ONE haiku call per turn instead of 1 (extract) + N×4-5 (classify + decide + metadata for each fact) — typically 25–30× faster end-to-end.
Pipeline.
Pre-flight guards. Skip silently if the transcript has fewer than 3 lines, or if
parse-transcript.shreturns(empty transcript),(no user message found), or(empty turn). Anchors on the most recent user message — only the trailing turn is sent to haiku.Taxonomy resolution. Read the cached taxonomy block populated at
SessionStart. Falls back to a hardcoded hint sheet (top-level + common second-level paths) if the store has no taxonomy loaded — so first-run sessions still classify correctly.System prompt. Load
plugins/claude-code/hooks/prompts/stop_capture.tmpl(single source of truth, testable via the prompt-harness) and substitute${TAXONOMY_BLOCK}in bash. TheCATEGORIES+EXAMPLESblocks come from the store's persisted taxonomy (taxonomy:v1:*), so auto-capture classifies against the same taxonomy as explicit/memoir:remember "fact"(without-p).Worthiness gate — the silent default. The system prompt instructs haiku that the default answer is nothing. A line is emitted only when a fact passes four hard gates:
Durable — still relevant in weeks or months.
A future session would genuinely benefit from knowing it.
Not already discoverable from the code, git log,
CLAUDE.md, orREADME.A senior engineer would write it down in onboarding notes.
Always-capture triggers override the silent default: standing rules ("from now on…", "always X", "never X"), stated preferences ("I prefer X over Y"), architectural / tooling decisions just resolved this turn (with rationale), project facts not yet in the repo, and non-obvious technical invariants. Most turns produce zero lines — that is the expected, high-quality result.
One-shot LLM call.
claude -p --model haiku --no-session-persistence --no-chrome --system-prompt "$STOP_SYSTEM_PROMPT"with the parsed transcript on stdin.MEMOIR_NO_CAPTURE=1 CLAUDECODE=is set on the subprocess to prevent recursion into the host's plugin hooks.Output format.
<path>[,<path>...]<TAB><fact>per line. Comma-separated paths in column 1 mean "write the same fact to multiple paths in one call" — each blob'srelated_keysfield records the siblings (handled memoir-side).Line-format validation. A line is rejected if any of: empty path or fact, fact under 8 characters, or paths don't match
^[a-z][a-z0-9_]*(\.[a-z0-9_]+){1,3}(,…)*$. Guards against haiku going rogue with preamble text or malformed taxonomy paths.Write. For each surviving line, build
-p p1 -p p2 …argv from the comma list and callmemoir remember "$fact" -p …— bypassing memoir's internal classifier entirely.Statusline refresh. After captures land, recompute the user-memory count and rewrite the statusline cache so the displayed count ticks up immediately.
Toggles & failure mode. MEMOIR_NO_CAPTURE=1 disables only the capture stage; the metrics stage still runs. The whole pipeline is fail-silent — every subprocess uses 2>/dev/null || true, malformed haiku output is filtered out by the line-format check, and the hook always exits 0. A failed turn is silent, not loud.
Testing. The system prompt is a first-class artifact — extracted to hooks/prompts/stop_capture.tmpl so the prompt-harness can test it in isolation:
# Full LLM suite (~60s, costs LLM tokens)
plugins/claude-code/tests/prompt-harness/runner.py run --prompt stop_capture --model haiku
# One case
plugins/claude-code/tests/prompt-harness/runner.py case stop_capture/<case>.yaml --model haikuThe harness drops system.txt, input.txt, output.txt, result.json, and a replayable command.sh per case under /tmp/memoir-prompt-tests/<UTC-timestamp>/. Read summary.md for pass/fail. Assertion DSL: plugins/claude-code/tests/prompt-harness/README.md.
Code-change audit log (metrics.code.<branch>)
After the auto-capture stage, the Stop hook runs a third pass that detects file edits this turn and appends a one-line summary to a per-branch audit log. The goal is a chronological, human-readable record of what changed in the code, browseable later via memoir get metrics.code.<branch> (or the UI).
Pipeline.
Toggle gate. Skip if
MEMOIR_NO_CODE_SUMMARY=1.Transcript scan.
hooks/collect-edits.shwalks the most recent turn'stool_useblocks forEdit,Write,MultiEdit, andNotebookEdit. Returns a JSON object{user_prompt, edits: [{tool, file_path, snippet}, …]}with each snippet truncated to 300 chars and the user prompt truncated to 2000 chars. Empty stdout = no file edits → skip the LLM call.Build prompt input. Prepend an optional
[User prompt]\n<text>\n---header (the why — gives haiku the user's stated intent), then concatenate edits as<tool> <file_path>\n<snippet>\n---, capped at ~8 KB so multi-file refactors stay well under haiku's context window.One haiku call.
claude -p --model haiku --no-session-persistence --no-chrome --system-prompt "$CC_SUMMARY_PROMPT"with the assembled block on stdin. Recursion-prevention env:MEMOIR_NO_CAPTURE=1 MEMOIR_NO_METRICS=1 MEMOIR_NO_CODE_SUMMARY=1 CLAUDECODE=. Prompt template:hooks/prompts/code_change_summary.tmpl. Haiku uses the user prompt as the highest-signal source for why; the snippets confirm what.Output validation & cleanup. Strip surrounding quotes / preambles (
Here is,Summary:, leading bullets), collapse to the first non-empty line, truncate to 1000 chars (haiku is asked for ≤100 words, so the cap is a safety belt for run-on output). If the result is exactlytrivial(case-insensitive), skip the write — trivial-edit suppression is decided by haiku in-prompt.Branch lookup.
memoir_json status→ current memoir branch (fallbackunknown).Read-merge-write append.
memoir remember -preplaces by path, so the hook owns the append (mirrors themetrics.turn.<branch>flow). Reads the existing JSON value, parsesentries[], appends{timestamp, summary}, writes the merged accumulator back.
Key shape: metrics.code.<branch> — sibling to metrics.turn.<branch>. Branch names with / (e.g. feature/x) are kept literal.
Value shape:
{
"schema_version": 1,
"entries": [
{"timestamp": 1714800000.0, "summary": "Refactored auth middleware to use JWT; added unit tests for token expiry."},
{"timestamp": 1714801200.0, "summary": "Renamed getUser → getCurrentUser across 7 callers; updated docstrings."}
]
}Entries are append-forever — no decay. Bash-induced edits (sed -i, mv, refactor scripts) are not detected by design; the log reflects what the agent itself did via Edit/Write/MultiEdit/NotebookEdit.
Toggles & failure mode. MEMOIR_NO_CODE_SUMMARY=1 disables only this stage; capture and metrics still run. Every subprocess uses 2>/dev/null || true and the hook always exits 0 — same fail-silent design as the other stages. A failed turn is silent, not loud.
Branch identity & merge. Source-branch identity lives in the key fragment, not the value. BranchService.promote_branch carries metrics.code.feature/x to main automatically when the user runs memoir sync-branch feature/x, preserving the per-source-branch view.
Testing. Unit + integration + prompt-harness cover the three layers separately:
# Transcript-scan unit tests (no memoir, no haiku)
bash plugins/claude-code/tests/test_collect_edits.sh
# Stop-hook integration (read-merge-write append, gating; no haiku)
bash plugins/claude-code/tests/test_stop_code_summary.sh
# Prompt cases (haiku-driven, costs LLM tokens)
plugins/claude-code/tests/prompt-harness/runner.py run --prompt code_change_summary --model haikuPer-branch turn metrics (metrics.turn.<branch>)
The Stop hook accumulates per-turn statistics into one key per branch in the default namespace, alongside auto-captured memories.
Key shape: metrics.turn.<branch> — for example metrics.turn.main, metrics.turn.feature/x. Branch names with / are kept literal (memoir's remember -p accepts arbitrary path strings).
Value shape:
{
"schema_version": 1,
"tokens": null,
"llms": null,
"turns_count": 42,
"total_output_chars": 198432,
"total_tool_input_chars": 24561,
"total_tool_result_chars": 884201,
"total_tool_calls": 187,
"total_tool_errors": 6,
"total_repeated_tool_calls": 12,
"total_latency_ms": 1845300,
"latency_samples": 31
}Each turn the hook reads the existing accumulator, folds in deltas computed by collect-metrics.sh, and writes it back via merge-metrics.py. tokens and llms are reserved null until Claude Code exposes per-turn usage to hooks; today the proxies are char-count and tool-count fields. latency_samples only ticks for turns whose transcript line carries a user-message timestamp.
Toggles & failure mode. MEMOIR_NO_METRICS=1 disables the metrics path independently of MEMOIR_NO_CAPTURE — either can fail without affecting the other. The whole block is wrapped in 2>/dev/null || true, matching the rest of the Stop hook's fail-silent design.
Branch identity & merge. Source-branch identity lives in the key fragment, not the value — so BranchService.promote_branch (default-namespace only) carries metrics.turn.feature/x to main automatically when the user runs memoir sync-branch feature/x. After promotion, main retains its own metrics.turn.main untouched; metrics.turn.feature/x rides along, preserving the per-source-branch view.
UI surface. The /memoir:ui Statistics modal grows two conditional tabs after Overview:
Codebase — renders
codebase:onboardkeys grouped by L1 prefix, first sentence per child, with a header showinglast_onboard <commit> · <date> · <mode>. Same compact rendering shape as the SessionStart inject.Metrics — table view with rows = branches and columns = accumulator fields (
Turns,Calls,Errors,Avg latency (ms),Output chars, …). Three bar charts below show avg-latency / output-chars / tool-result-chars distributions across branches.
Both tabs only appear when their data exists. Both fetch via raw GET /api/onboard and GET /api/metrics — no LLM.
Environment variables
All optional.
| Variable | Default | Effect |
|---|---|---|
MEMOIR_STORE | ~/.memoir/memoir_<hash> | Override the per-project store path. |
MEMOIR_NO_CAPTURE | unset | 1 disables Stop-hook auto-capture (haiku classification + memory writes). Metrics still record. |
MEMOIR_NO_METRICS | unset | 1 disables the per-branch turn-metrics accumulator. Auto-capture still runs. |
MEMOIR_NO_CODE_SUMMARY | unset | 1 disables the per-branch code-change audit log (metrics.code.<branch>). Capture and metrics still run. |
MEMOIR_ONBOARD_INJECT | 1 | 0 suppresses the codebase:onboard block in SessionStart's additionalContext. |
MEMOIR_AUTO_PROMOTE_MERGED | 1 | 0 disables SessionStart auto-promotion of memoir branches whose code branch merged into main. |
MEMOIR_STALE_BRANCH_DAYS | 60 | Inactivity threshold for /memoir:sync's stale-branch cleanup. |
MEMOIR_LLM_MODEL | haiku | Model used for the Stop hook's fact extractor. Override only if you've validated alignment with the prompt-test harness. |
MEMOIR_MAX_RESULT_CHARS | 1000 | Per-tool-result truncation in parse-transcript.sh. |
Where to set them
Pick the scope you want — Claude Code merges settings from all three layers, with project-local winning over user-global winning over the shell environment.
1. User-global (every Claude Code session, every project) — ~/.claude/settings.json:
{
"env": {
"MEMOIR_NO_CAPTURE": "1",
"MEMOIR_LLM_MODEL": "claude-sonnet-4-6"
}
}2. Per-project, committed (every collaborator on this repo) — <repo>/.claude/settings.json:
{
"env": {
"MEMOIR_NO_CODE_SUMMARY": "1"
}
}3. Per-project, local only (just you, gitignored) — <repo>/.claude/settings.local.json. Same shape as above. Use this for personal toggles you don't want to commit (e.g. MEMOIR_STORE pointing at a custom path).
4. One-off / shell session — export before launching claude:
MEMOIR_NO_CAPTURE=1 claude
# or
export MEMOIR_LLM_MODEL=claude-sonnet-4-6
claudePersist by adding the export line to ~/.zshrc / ~/.bashrc, or use direnv with a per-project .envrc.
After editing a settings.json, restart the Claude Code session for the change to take effect (the plugin reads env at hook fire time, but Claude Code reads settings at session start).
Manifest
plugins/claude-code/.claude-plugin/plugin.json:
{
"name": "memoir",
"version": "0.1.0",
"description": "Git-versioned, taxonomy-structured memory for Claude Code — recall by path, branch to isolate, time-travel to audit."
}See also
CLI — the underlying
memoircommands the plugin wraps.API — the Python library for programmatic use.
Architecture — how memoir is structured under the hood.
512 files · 489 not served here
- .agents/plugins/marketplace.json (not served here)
- .claude-plugin/marketplace.json (not served here)
- .dockerignore (not served here)
- .github/ISSUE_TEMPLATE/bug_report.md (not served here)
- .github/ISSUE_TEMPLATE/feature_request.md (not served here)
- .github/ISSUE_TEMPLATE/performance_issue.md (not served here)
- .github/pull_request_template.md (not served here)
- .github/workflows/cc-smoke.yml (not served here)
- .github/workflows/ci.yml (not served here)
- .github/workflows/docs.yml (not served here)
- .github/workflows/mcp-release.yml (not served here)
- .github/workflows/pypi-smoke.yml (not served here)
- .github/workflows/release.yml (not served here)
- .gitignore (not served here)
- .pre-commit-config.yaml (not served here)
- ATTRIBUTION.md
- AUTOGINEER-RUNS.md
- CLAUDE.md
- CONTRIBUTING.md
- LICENSE
- Makefile (not served here)
- README.md
- RELEASING.md
- benchmarks/classifier.py (not served here)
- benchmarks/data/recall_test_queries.json (not served here)
- benchmarks/data/remember_test_data.json (not served here)
- benchmarks/locomo/README.md (not served here)
- benchmarks/locomo/__init__.py (not served here)
- benchmarks/locomo/cognitive.py (not served here)
- benchmarks/locomo/data.py (not served here)
- benchmarks/locomo/judge.py (not served here)
- benchmarks/locomo/memoir_runner.py (not served here)
- benchmarks/locomo/run.py (not served here)
- docker.sh (not served here)
- docs/_static/.gitkeep (not served here)
- docs/_static/custom.css (not served here)
- docs/_static/memoir-demo.gif (not served here)
- docs/_static/memoir-light.png (not served here)
- docs/_static/memoir-multi-agent.gif (not served here)
- docs/_static/memoir.png (not served here)
- docs/_static/ui-graph.png (not served here)
- docs/_static/ui-stats-modal.png (not served here)
- docs/_static/ui-tree.png (not served here)
- docs/api/classifier.md (not served here)
- docs/api/memento.md (not served here)
- docs/api/memoir.md (not served here)
- docs/api/search.md (not served here)
- docs/api/store.md (not served here)
- docs/api/taxonomy.md (not served here)
- docs/architecture.md
- docs/basic_usage.md
- docs/claude_code.md
- docs/cli.md
- docs/cloud.md
- docs/codex.md
- docs/examples.md
- docs/examples/context_branching.md (not served here)
- docs/examples/memory_debugging.md (not served here)
- docs/examples/production_debugging.md (not served here)
- docs/examples/reproducible_testing.md (not served here)
- docs/faq.md
- docs/hermes.md
- docs/index.md
- docs/installation.md
- docs/mcp.md
- docs/openclaw.md
- docs/quickstart.md
- docs/theory/classifier.md (not served here)
- docs/theory/conflict-merge.md (not served here)
- docs/theory/index.md (not served here)
- docs/theory/memento.md (not served here)
- docs/theory/related-work.md (not served here)
- docs/theory/search.md (not served here)
- docs/ui.md
- docs/watch.md
- mkdocs.yml (not served here)
- packaging/mcp/README.md (not served here)
- packaging/mcp/manifest.json (not served here)
- packaging/mcp/server.json (not served here)
- plugins/claude-code/.claude-plugin/plugin.json (not served here)
- plugins/claude-code/.gitignore (not served here)
- plugins/claude-code/README.md (not served here)
- plugins/claude-code/commands/onboard.md (not served here)
- plugins/claude-code/commands/pane.md (not served here)
- plugins/claude-code/commands/push.md (not served here)
- plugins/claude-code/commands/recall.md (not served here)
- plugins/claude-code/commands/remember.md (not served here)
- plugins/claude-code/commands/status.md (not served here)
- plugins/claude-code/commands/sync.md (not served here)
- plugins/claude-code/commands/ui.md (not served here)
- plugins/claude-code/hooks/collect-edits.sh (not served here)
- plugins/claude-code/hooks/collect-metrics.sh (not served here)
- plugins/claude-code/hooks/common.sh (not served here)
- plugins/claude-code/hooks/hooks.json (not served here)
- plugins/claude-code/hooks/merge-metrics.py (not served here)
- plugins/claude-code/hooks/mod/format.ts (not served here)
- plugins/claude-code/hooks/mod/mod.test.tsx (not served here)
- plugins/claude-code/hooks/mod/pane.tsx (not served here)
- plugins/claude-code/hooks/mod/register.tsx (not served here)
- plugins/claude-code/hooks/mod/store.ts (not served here)
- plugins/claude-code/hooks/parse-transcript.sh (not served here)
- plugins/claude-code/hooks/prompts/code_change_summary.tmpl (not served here)
- plugins/claude-code/hooks/prompts/stop_capture.tmpl (not served here)
- plugins/claude-code/hooks/session-end.sh (not served here)
- plugins/claude-code/hooks/session-start.sh (not served here)
- plugins/claude-code/hooks/stop.sh (not served here)
- plugins/claude-code/hooks/user-prompt-submit.sh (not served here)
- plugins/claude-code/scripts/derive-store-path.sh (not served here)
- plugins/claude-code/scripts/ensure-store.sh (not served here)
- plugins/claude-code/scripts/memoir-cli.sh (not served here)
- plugins/claude-code/scripts/memoir-ui-ctl.sh (not served here)
- plugins/claude-code/scripts/pane-cmd.sh (not served here)
- plugins/claude-code/scripts/push-cmd.sh (not served here)
- plugins/claude-code/scripts/resolve-memoir-cli.sh (not served here)
- plugins/claude-code/scripts/status-cmd.sh (not served here)
- plugins/claude-code/scripts/statusline.sh (not served here)
- plugins/claude-code/scripts/sync-cmd.sh (not served here)
- plugins/claude-code/skills/memoir-onboard/SKILL.md (not served here)
- plugins/claude-code/skills/memoir-onboard/extractors.py (not served here)
- plugins/claude-code/skills/memory-recall/SKILL.md (not served here)
- plugins/claude-code/tests/prompt-harness/README.md (not served here)
- plugins/claude-code/tests/prompt-harness/assertions.py (not served here)
- plugins/claude-code/tests/prompt-harness/cases/code_change_summary/doc-only-change.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/code_change_summary/multiedit-refactor.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/code_change_summary/single-edit-rename.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/code_change_summary/whitespace-only-trivial.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/code_change_summary/write-new-test-file.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/gate/user-prompt-submit/acknowledgment-does-not-fire.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/gate/user-prompt-submit/addendum-not-add-does-not-fire.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/gate/user-prompt-submit/case-insensitive-fires.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/gate/user-prompt-submit/code-fence-fires.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/gate/user-prompt-submit/file-path-fires.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/gate/user-prompt-submit/no-memories-does-not-fire.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/gate/user-prompt-submit/noun-memoir-fires.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/gate/user-prompt-submit/noun-recall-fires.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/gate/user-prompt-submit/question-shape-fires.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/gate/user-prompt-submit/short-prompt-does-not-fire.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/gate/user-prompt-submit/verb-add-fires.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/gate/user-prompt-submit/verb-investigate-fires.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/gate/user-prompt-submit/verb-make-fires.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/recall_ab/casual-greeting.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/recall_ab/design-decision.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/recall_ab/refactor-task.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/stop_capture/capture-architectural-decision.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/stop_capture/capture-cross-namespace.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/stop_capture/capture-from-now-on-tool-pref.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/stop_capture/capture-going-forward-rule.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/stop_capture/capture-multi-fact-turn.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/stop_capture/capture-multi-path.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/stop_capture/capture-project-fact.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/stop_capture/capture-stop-doing-x.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/stop_capture/format-no-segment-hyphens.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/stop_capture/format-noisy-preamble-stripped.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/stop_capture/ignore-mixed-rule-vs-action.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/stop_capture/ignore-personal-chitchat.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/stop_capture/ignore-polite-thanks.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/stop_capture/ignore-retraction.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/stop_capture/ignore-routine-qa.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/cases/stop_capture/ignore-tool-mechanics.yaml (not served here)
- plugins/claude-code/tests/prompt-harness/gate.py (not served here)
- plugins/claude-code/tests/prompt-harness/prompts.py (not served here)
- plugins/claude-code/tests/prompt-harness/recall_ab.py (not served here)
- plugins/claude-code/tests/prompt-harness/runner.py (not served here)
- plugins/claude-code/tests/test_branch_sync.sh (not served here)
- plugins/claude-code/tests/test_collect_edits.sh (not served here)
- plugins/claude-code/tests/test_collect_metrics.py (not served here)
- plugins/claude-code/tests/test_ensure_store.sh (not served here)
- plugins/claude-code/tests/test_extractors.py (not served here)
- plugins/claude-code/tests/test_no_git_repo.sh (not served here)
- plugins/claude-code/tests/test_push_cmd.sh (not served here)
- plugins/claude-code/tests/test_stop_code_summary.sh (not served here)
- plugins/claude-code/tests/test_stop_metrics.sh (not served here)
- plugins/claude-code/tests/test_store_mode_drift_warning.sh (not served here)
- plugins/claude-code/tests/test_worktree_shared_store.sh (not served here)
- plugins/claude-code/types/index.d.ts (not served here)
- plugins/codex/.codex-plugin/plugin.json (not served here)
- plugins/codex/README.md (not served here)
- plugins/codex/hooks/collect-edits.sh (not served here)
- plugins/codex/hooks/collect-metrics.sh (not served here)
- plugins/codex/hooks/common.sh (not served here)
- plugins/codex/hooks/hooks.json (not served here)
- plugins/codex/hooks/merge-metrics.py (not served here)
- plugins/codex/hooks/parse-transcript.sh (not served here)
- plugins/codex/hooks/prompts/code_change_summary.tmpl (not served here)
- plugins/codex/hooks/prompts/stop_capture.tmpl (not served here)
- plugins/codex/hooks/session-start.sh (not served here)
- plugins/codex/hooks/stop.sh (not served here)
- plugins/codex/hooks/user-prompt-submit.sh (not served here)
- plugins/codex/scripts/derive-store-path.sh (not served here)
- plugins/codex/scripts/ensure-store.sh (not served here)
- plugins/codex/scripts/install-codex-hooks.sh (not served here)
- plugins/codex/scripts/memoir-cli.sh (not served here)
- plugins/codex/scripts/memoir-ui-ctl.sh (not served here)
- plugins/codex/scripts/resolve-memoir-cli.sh (not served here)
- plugins/codex/scripts/status-cmd.sh (not served here)
- plugins/codex/skills/memoir-onboard/SKILL.md (not served here)
- plugins/codex/skills/memoir-onboard/extractors.py (not served here)
- plugins/codex/skills/memoir-remember/SKILL.md (not served here)
- plugins/codex/skills/memoir-status/SKILL.md (not served here)
- plugins/codex/skills/memoir-ui/SKILL.md (not served here)
- plugins/codex/skills/memory-recall/SKILL.md (not served here)
- plugins/codex/tests/prompt-harness/README.md (not served here)
- plugins/codex/tests/prompt-harness/assertions.py (not served here)
- plugins/codex/tests/prompt-harness/cases/code_change_summary/doc-only-change.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/code_change_summary/multiedit-refactor.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/code_change_summary/single-edit-rename.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/code_change_summary/whitespace-only-trivial.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/code_change_summary/write-new-test-file.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/gate/user-prompt-submit/acknowledgment-does-not-fire.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/gate/user-prompt-submit/addendum-not-add-does-not-fire.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/gate/user-prompt-submit/case-insensitive-fires.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/gate/user-prompt-submit/code-fence-fires.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/gate/user-prompt-submit/file-path-fires.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/gate/user-prompt-submit/no-memories-does-not-fire.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/gate/user-prompt-submit/noun-memoir-fires.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/gate/user-prompt-submit/noun-recall-fires.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/gate/user-prompt-submit/question-shape-fires.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/gate/user-prompt-submit/short-prompt-does-not-fire.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/gate/user-prompt-submit/verb-add-fires.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/gate/user-prompt-submit/verb-investigate-fires.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/gate/user-prompt-submit/verb-make-fires.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/stop_capture/capture-architectural-decision.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/stop_capture/capture-cross-namespace.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/stop_capture/capture-from-now-on-tool-pref.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/stop_capture/capture-going-forward-rule.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/stop_capture/capture-multi-fact-turn.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/stop_capture/capture-multi-path.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/stop_capture/capture-project-fact.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/stop_capture/capture-stop-doing-x.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/stop_capture/format-no-segment-hyphens.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/stop_capture/format-noisy-preamble-stripped.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/stop_capture/ignore-mixed-rule-vs-action.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/stop_capture/ignore-personal-chitchat.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/stop_capture/ignore-polite-thanks.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/stop_capture/ignore-retraction.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/stop_capture/ignore-routine-qa.yaml (not served here)
- plugins/codex/tests/prompt-harness/cases/stop_capture/ignore-tool-mechanics.yaml (not served here)
- plugins/codex/tests/prompt-harness/gate.py (not served here)
- plugins/codex/tests/prompt-harness/prompts.py (not served here)
- plugins/codex/tests/prompt-harness/runner.py (not served here)
- plugins/codex/tests/test_branch_sync.sh (not served here)
- plugins/codex/tests/test_codex_transcript.py (not served here)
- plugins/codex/tests/test_collect_edits.sh (not served here)
- plugins/codex/tests/test_collect_metrics.py (not served here)
- plugins/codex/tests/test_ensure_store.sh (not served here)
- plugins/codex/tests/test_extractors.py (not served here)
- plugins/codex/tests/test_install_codex_hooks.py (not served here)
- plugins/codex/tests/test_no_git_repo.sh (not served here)
- plugins/codex/tests/test_skill_metadata.py (not served here)
- plugins/codex/tests/test_stop_code_summary.sh (not served here)
- plugins/codex/tests/test_stop_metrics.sh (not served here)
- plugins/codex/tests/test_store_mode_drift_warning.sh (not served here)
- plugins/codex/tests/test_worktree_shared_store.sh (not served here)
- plugins/docker-tests/README.md (not served here)
- plugins/docker-tests/claude-code/Dockerfile (not served here)
- plugins/docker-tests/claude-code/test.sh (not served here)
- plugins/docker-tests/codex/Dockerfile (not served here)
- plugins/docker-tests/codex/test.sh (not served here)
- plugins/docker-tests/hermes/Dockerfile (not served here)
- plugins/docker-tests/hermes/README.md (not served here)
- plugins/docker-tests/hermes/test.sh (not served here)
- plugins/hermes/README.md (not served here)
- plugins/hermes/__init__.py (not served here)
- plugins/hermes/bridge.py (not served here)
- plugins/hermes/cli.py (not served here)
- plugins/hermes/plugin.yaml (not served here)
- pyproject.toml (not served here)
- requirements.txt (not served here)
- ruff.toml (not served here)
- scripts/cc-smoke/README.md (not served here)
- scripts/cc-smoke/run.sh (not served here)
- scripts/cc-smoke/smoke.sh (not served here)
- scripts/check_version_consistency.py (not served here)
- scripts/pypi-smoke/Dockerfile (not served here)
- scripts/pypi-smoke/README.md (not served here)
- scripts/pypi-smoke/run.sh (not served here)
- scripts/pypi-smoke/smoke.sh (not served here)
- scripts/stamp_mcp_version.py (not served here)
- src/memoir/__init__.py (not served here)
- src/memoir/classifier/__init__.py (not served here)
- src/memoir/classifier/base.py (not served here)
- src/memoir/classifier/intelligent.py (not served here)
- src/memoir/classifier/semantic.py (not served here)
- src/memoir/cli/__init__.py (not served here)
- src/memoir/cli/commands/__init__.py (not served here)
- src/memoir/cli/commands/analysis.py (not served here)
- src/memoir/cli/commands/branch.py (not served here)
- src/memoir/cli/commands/capture.py (not served here)
- src/memoir/cli/commands/crypto.py (not served here)
- src/memoir/cli/commands/memory.py (not served here)
- src/memoir/cli/commands/search.py (not served here)
- src/memoir/cli/commands/store.py (not served here)
- src/memoir/cli/commands/sync.py (not served here)
- src/memoir/cli/commands/taxonomy.py (not served here)
- src/memoir/cli/commands/tui.py (not served here)
- src/memoir/cli/commands/ui.py (not served here)
- src/memoir/cli/commands/watch.py (not served here)
- src/memoir/cli/main.py (not served here)
- src/memoir/core/__init__.py (not served here)
- src/memoir/core/memory.py (not served here)
- src/memoir/integration/__init__.py (not served here)
- src/memoir/integration/base.py (not served here)
- src/memoir/integration/langgraph/__init__.py (not served here)
- src/memoir/integration/langgraph/memory_store.py (not served here)
- src/memoir/integration/langgraph/types.py (not served here)
- src/memoir/integration/langgraph/utils.py (not served here)
- src/memoir/llm/__init__.py (not served here)
- src/memoir/llm/claude_cli_client.py (not served here)
- src/memoir/llm/litellm_client.py (not served here)
- src/memoir/llm/prompts/capture_assistant.tmpl (not served here)
- src/memoir/llm/prompts/capture_coding.tmpl (not served here)
- src/memoir/llm/prompts/consolidate_read.tmpl (not served here)
- src/memoir/llm/prompts/merge_consolidate.tmpl (not served here)
- src/memoir/mcp/__init__.py (not served here)
- src/memoir/mcp/server.py (not served here)
- src/memoir/memento/__init__.py (not served here)
- src/memoir/memento/location.py (not served here)
- src/memoir/memento/profile.py (not served here)
- src/memoir/memento/timeline.py (not served here)
- src/memoir/sdk/__init__.py (not served here)
- src/memoir/sdk/client.py (not served here)
- src/memoir/search/__init__.py (not served here)
- src/memoir/search/intelligent.py (not served here)
- src/memoir/services/__init__.py (not served here)
- src/memoir/services/agent_account.py (not served here)
- src/memoir/services/base.py (not served here)
- src/memoir/services/branch_service.py (not served here)
- src/memoir/services/cloud_auth.py (not served here)
- src/memoir/services/crypto_service.py (not served here)
- src/memoir/services/memory_service.py (not served here)
- src/memoir/services/merge_policy.py (not served here)
- src/memoir/services/models.py (not served here)
- src/memoir/services/repo_meta.py (not served here)
- src/memoir/services/search_service.py (not served here)
- src/memoir/services/store_service.py (not served here)
- src/memoir/services/sync_service.py (not served here)
- src/memoir/services/vector_service.py (not served here)
- src/memoir/services/watch_service.py (not served here)
- src/memoir/store/__init__.py (not served here)
- src/memoir/store/backend.py (not served here)
- src/memoir/store/cwd_locked.py (not served here)
- src/memoir/store/git_safety.py (not served here)
- src/memoir/store/gitcmd.py (not served here)
- src/memoir/store/node_gc.py (not served here)
- src/memoir/store/prolly_adapter.py (not served here)
- src/memoir/store/repo_mode.py (not served here)
- src/memoir/store/write_lock.py (not served here)
- src/memoir/taxonomy/__init__.py (not served here)
- src/memoir/taxonomy/data/general/descriptions.md (not served here)
- src/memoir/taxonomy/data/general/examples.md (not served here)
- src/memoir/taxonomy/data/general/presets.md (not served here)
- src/memoir/taxonomy/iterative.py (not served here)
- src/memoir/taxonomy/loader.py (not served here)
- src/memoir/taxonomy/markdown_source.py (not served here)
- src/memoir/taxonomy/registry.py (not served here)
- src/memoir/taxonomy/semantic.py (not served here)
- src/memoir/taxonomy/taxonomy.py (not served here)
- src/memoir/tui/__init__.py (not served here)
- src/memoir/tui/app.py (not served here)
- src/memoir/tui/data.py (not served here)
- src/memoir/tui/screens/__init__.py (not served here)
- src/memoir/tui/screens/help.py (not served here)
- src/memoir/tui/screens/main.py (not served here)
- src/memoir/tui/widgets/__init__.py (not served here)
- src/memoir/tui/widgets/commits_pane.py (not served here)
- src/memoir/tui/widgets/diff_view.py (not served here)
- src/memoir/tui/widgets/memory_viewer.py (not served here)
- src/memoir/tui/widgets/outline_pane.py (not served here)
- src/memoir/tui/widgets/watch_pane.py (not served here)
- src/memoir/ui/__init__.py (not served here)
- src/memoir/ui/handlers/__init__.py (not served here)
- src/memoir/ui/handlers/api_handler.py (not served here)
- src/memoir/ui/handlers/branch_handler.py (not served here)
- src/memoir/ui/handlers/crypto_handler.py (not served here)
- src/memoir/ui/handlers/memory_handler.py (not served here)
- src/memoir/ui/handlers/store_handler.py (not served here)
- src/memoir/ui/handlers/utils.py (not served here)
- src/memoir/ui/handlers/watch_handler.py (not served here)
- src/memoir/ui/initializer.py (not served here)
- src/memoir/ui/reader.py (not served here)
- src/memoir/ui/schemas/__init__.py (not served here)
- src/memoir/ui/schemas/branches.py (not served here)
- src/memoir/ui/schemas/commits.py (not served here)
- src/memoir/ui/schemas/diff.py (not served here)
- src/memoir/ui/schemas/memory.py (not served here)
- src/memoir/ui/schemas/statistics.py (not served here)
- src/memoir/ui/schemas/store.py (not served here)
- src/memoir/ui/schemas/timeline.py (not served here)
- src/memoir/ui/server.py (not served here)
- src/memoir/ui/webapp/.gitignore (not served here)
- src/memoir/ui/webapp/index.html (not served here)
- src/memoir/ui/webapp/package-lock.json (not served here)
- src/memoir/ui/webapp/package.json (not served here)
- src/memoir/ui/webapp/pnpm-lock.yaml (not served here)
- src/memoir/ui/webapp/pnpm-workspace.yaml (not served here)
- src/memoir/ui/webapp/public/memoir.png (not served here)
- src/memoir/ui/webapp/src/App.tsx (not served here)
- src/memoir/ui/webapp/src/api/client.test.ts (not served here)
- src/memoir/ui/webapp/src/api/client.ts (not served here)
- src/memoir/ui/webapp/src/api/types.ts (not served here)
- src/memoir/ui/webapp/src/commands/CommandAutocomplete.css (not served here)
- src/memoir/ui/webapp/src/commands/CommandAutocomplete.tsx (not served here)
- src/memoir/ui/webapp/src/commands/availability.test.ts (not served here)
- src/memoir/ui/webapp/src/commands/match.test.ts (not served here)
- src/memoir/ui/webapp/src/commands/match.ts (not served here)
- src/memoir/ui/webapp/src/commands/parity.test.ts (not served here)
- src/memoir/ui/webapp/src/commands/registry.test.ts (not served here)
- src/memoir/ui/webapp/src/commands/registry.ts (not served here)
- src/memoir/ui/webapp/src/config/runtime.test.ts (not served here)
- src/memoir/ui/webapp/src/config/runtime.ts (not served here)
- src/memoir/ui/webapp/src/drawers/CommitDetail.tsx (not served here)
- src/memoir/ui/webapp/src/drawers/DrawerPanels.css (not served here)
- src/memoir/ui/webapp/src/drawers/MemoryDetail.test.tsx (not served here)
- src/memoir/ui/webapp/src/drawers/MemoryDetail.tsx (not served here)
- src/memoir/ui/webapp/src/drawers/RangeDiff.tsx (not served here)
- src/memoir/ui/webapp/src/layout/AppShell.css (not served here)
- src/memoir/ui/webapp/src/layout/AppShell.tsx (not served here)
- src/memoir/ui/webapp/src/layout/BranchMatchToggle.tsx (not served here)
- src/memoir/ui/webapp/src/layout/BranchSwitcher.css (not served here)
- src/memoir/ui/webapp/src/layout/BranchSwitcher.tsx (not served here)
- src/memoir/ui/webapp/src/layout/CommandBar.css (not served here)
- src/memoir/ui/webapp/src/layout/CommandBar.tsx (not served here)
- src/memoir/ui/webapp/src/layout/FilterBar.css (not served here)
- src/memoir/ui/webapp/src/layout/FilterBar.tsx (not served here)
- src/memoir/ui/webapp/src/layout/LeftPane.css (not served here)
- src/memoir/ui/webapp/src/layout/LeftPane.tsx (not served here)
- src/memoir/ui/webapp/src/layout/LiveAnnouncer.css (not served here)
- src/memoir/ui/webapp/src/layout/LiveAnnouncer.tsx (not served here)
- src/memoir/ui/webapp/src/layout/MainCanvas.css (not served here)
- src/memoir/ui/webapp/src/layout/MainCanvas.tsx (not served here)
- src/memoir/ui/webapp/src/layout/RightDrawer.css (not served here)
- src/memoir/ui/webapp/src/layout/RightDrawer.tsx (not served here)
- src/memoir/ui/webapp/src/layout/TopBar.css (not served here)
- src/memoir/ui/webapp/src/layout/TopBar.test.tsx (not served here)
- src/memoir/ui/webapp/src/layout/TopBar.tsx (not served here)
- src/memoir/ui/webapp/src/layout/ViewToolbar.css (not served here)
- src/memoir/ui/webapp/src/layout/ViewToolbar.tsx (not served here)
- src/memoir/ui/webapp/src/lib/category.ts (not served here)
- src/memoir/ui/webapp/src/lib/storage.test.ts (not served here)
- src/memoir/ui/webapp/src/lib/storage.ts (not served here)
- src/memoir/ui/webapp/src/lib/time.test.ts (not served here)
- src/memoir/ui/webapp/src/lib/time.ts (not served here)
- src/memoir/ui/webapp/src/lib/wildcard.test.ts (not served here)
- src/memoir/ui/webapp/src/lib/wildcard.ts (not served here)
- src/memoir/ui/webapp/src/main.tsx (not served here)
- src/memoir/ui/webapp/src/modals/BranchCommitsModal.css (not served here)
- src/memoir/ui/webapp/src/modals/BranchCommitsModal.tsx (not served here)
- src/memoir/ui/webapp/src/modals/BranchFromCommitModal.css (not served here)
- src/memoir/ui/webapp/src/modals/BranchFromCommitModal.tsx (not served here)
- src/memoir/ui/webapp/src/modals/BringOverMemoriesModal.css (not served here)
- src/memoir/ui/webapp/src/modals/BringOverMemoriesModal.tsx (not served here)
- src/memoir/ui/webapp/src/modals/CommandReferenceModal.css (not served here)
- src/memoir/ui/webapp/src/modals/CommandReferenceModal.tsx (not served here)
- src/memoir/ui/webapp/src/modals/StatsModal.css (not served here)
- src/memoir/ui/webapp/src/modals/StatsModal.tsx (not served here)
- src/memoir/ui/webapp/src/modals/SyncBranchesModal.css (not served here)
- src/memoir/ui/webapp/src/modals/SyncBranchesModal.tsx (not served here)
- src/memoir/ui/webapp/src/state/configSlice.test.ts (not served here)
- src/memoir/ui/webapp/src/state/configSlice.ts (not served here)
- src/memoir/ui/webapp/src/state/historySelectionSlice.test.ts (not served here)
- src/memoir/ui/webapp/src/state/historySelectionSlice.ts (not served here)
- src/memoir/ui/webapp/src/state/memorySelectionSlice.ts (not served here)
- src/memoir/ui/webapp/src/state/selectionSlice.test.ts (not served here)
- src/memoir/ui/webapp/src/state/selectionSlice.ts (not served here)
- src/memoir/ui/webapp/src/state/storeSlice.ts (not served here)
- src/memoir/ui/webapp/src/state/uiSlice.test.ts (not served here)
- src/memoir/ui/webapp/src/state/uiSlice.ts (not served here)
- src/memoir/ui/webapp/src/styles/global.css (not served here)
- src/memoir/ui/webapp/src/styles/tokens.css (not served here)
- src/memoir/ui/webapp/src/styles/typography.css (not served here)
- src/memoir/ui/webapp/src/views/commits/CommitList.css (not served here)
- src/memoir/ui/webapp/src/views/commits/CommitList.tsx (not served here)
- src/memoir/ui/webapp/src/views/commits/CommitRow.css (not served here)
- src/memoir/ui/webapp/src/views/commits/CommitRow.tsx (not served here)
- src/memoir/ui/webapp/src/views/graph/TaxonomyGraph.css (not served here)
- src/memoir/ui/webapp/src/views/graph/TaxonomyGraph.tsx (not served here)
- src/memoir/ui/webapp/src/views/history/CategoryTree.css (not served here)
- src/memoir/ui/webapp/src/views/history/CategoryTree.tsx (not served here)
- src/memoir/ui/webapp/src/views/history/HistoryView.css (not served here)
- src/memoir/ui/webapp/src/views/history/HistoryView.tsx (not served here)
- src/memoir/ui/webapp/src/views/places/PlacesView.css (not served here)
- src/memoir/ui/webapp/src/views/places/PlacesView.tsx (not served here)
- src/memoir/ui/webapp/src/views/timeline/TimelineView.css (not served here)
- src/memoir/ui/webapp/src/views/timeline/TimelineView.tsx (not served here)
- src/memoir/ui/webapp/src/views/tree/TaxonomyTree.css (not served here)
- src/memoir/ui/webapp/src/views/tree/TaxonomyTree.tsx (not served here)
- src/memoir/ui/webapp/src/views/tree/TreeNodeView.tsx (not served here)
- src/memoir/ui/webapp/src/views/tree/buildTaxonomy.test.ts (not served here)
- src/memoir/ui/webapp/src/views/tree/buildTaxonomy.ts (not served here)
- src/memoir/ui/webapp/src/views/watch/WatchView.css (not served here)
- src/memoir/ui/webapp/src/views/watch/WatchView.tsx (not served here)
- src/memoir/ui/webapp/src/vite-env.d.ts (not served here)
- src/memoir/ui/webapp/tsconfig.json (not served here)
- src/memoir/ui/webapp/vite.config.ts (not served here)
- static/memoir-light.png (not served here)
- static/memoir.png (not served here)
- tests/__init__.py (not served here)
- tests/conftest.py (not served here)
- tests/data/llm_locomo10_responses.json (not served here)
- tests/data/llm_responses.json (not served here)
- tests/fake_cloud.py (not served here)
- tests/pytest.ini (not served here)
- tests/test_backend_resolution.py (not served here)
- tests/test_branch_routing.py (not served here)
- tests/test_capture.py (not served here)
- tests/test_classifier.py (not served here)
- tests/test_cli.py (not served here)
- tests/test_cloud_agent.py (not served here)
- tests/test_cloud_gateways.py (not served here)
- tests/test_cloud_login.py (not served here)
- tests/test_cloud_sync.py (not served here)
- tests/test_diff_keys.py (not served here)
Advantages
- Provides Git-like version control for AI memory, allowing for branching, merging, and rolling back. evidence: README.md
- Uses hierarchical semantic paths for O(log n) lookups, offering better performance than traditional vector databases. evidence: README.md
Limitations
- Requires an API key for LLM-powered features like intelligent classification and semantic search. evidence: README.md
- Some advanced features like document watching require additional dependencies or manual configuration. evidence: pyproject.toml
Security
Risk level: Medium risk
This brain runs
Git hooks (5)
.pre-commit-config.yaml:pre-commit hooks from https://github.com/pre-commit/pre-commit-hooks at v6.0.0 (contributor tooling: runs only after pre-commit install)· uses the network.pre-commit-config.yaml:pre-commit hooks from https://github.com/pycqa/isort at 6.0.1 (contributor tooling: runs only after pre-commit install)· uses the network.pre-commit-config.yaml:pre-commit hooks from https://github.com/astral-sh/ruff-pre-commit at v0.12.9 (contributor tooling: runs only after pre-commit install)· uses the network.pre-commit-config.yaml:pre-commit hooks from https://github.com/PyCQA/bandit at 1.8.6 (contributor tooling: runs only after pre-commit install)· uses the network.pre-commit-config.yaml:pytest
Agent instructions that run or fetch something (73)
CLAUDE.md:`make ci` runs the full pipeline (lint, test, security, docs).CLAUDE.md:tests use the fake gateway in `tests/fake_cloud.py` (real `git http-backend`, no network).CLAUDE.md:When touching the mod run `claude plugin validate plugins/claude-code` and `claude plugin test plugins/claude-code`;CLAUDE.md:Run with `pytest`.CLAUDE.md:Deep structural details (layouts, refactor history, per-file line counts) intentionally omitted — use `rg` / `git log` when you need them.CLAUDE.md:flipping it later (`git init`, or `rm -rf .git`) surfaces a warning at SessionStart but does not block writes.CLAUDE.md:When changing a prompt, **run the harness against haiku before merging**.CLAUDE.md:The harness has two modes: **gate** (deterministic shell-hook tests, no LLM, sub-second per case — pins the recall-trigger gate in `hooks/user-prompt-submit.sh`) and **LLM mode** (`run` / `case` / `adhoc` against `claude -p` for prompt templates).CLAUDE.md:# Gate mode — no LLM cost, run on every commit.CLAUDE.md:plugins/claude-code/tests/prompt-harness/runner.py run --prompt stop_capture --model haikuCLAUDE.md:plugins/claude-code/tests/prompt-harness/runner.py run --prompt stop_capture --model sonnetCLAUDE.md:Every run drops `system.txt` (the assembled prompt actually sent), `input.txt`, `output.txt`, `command.sh` (replayable), and `result.json` per case under `/tmp/memoir-prompt-tests/<UTC-timestamp>/`.CLAUDE.md:Skill prompts (`skills/<name>/SKILL.md`) are evaluated by the orchestrating Claude rather than dispatched as system prompts to a model, so they are *not* covered by this harness — to test those, run the slash command in a real session and inspect behavior.CLAUDE.md:**Build the docs locally:** `make docs` (mkdocs) → `site/index.html`, or `make docs-live` for auto-reload.plugins/claude-code/skills/memoir-onboard/SKILL.md:bash -c 'source "${CLAUDE_PLUGIN_ROOT}/hooks/common.sh" >/dev/null 2>&1; in_git_repo && echo codebase:onboard || echo project:onboard'plugins/claude-code/skills/memoir-onboard/SKILL.md:Store: !`bash -c 'if [ -n "${MEMOIR_STORE:-}" ];plugins/claude-code/skills/memoir-onboard/SKILL.md:`$MEMOIR` is a wrapper that resolves the right invocation for this machine — `memoir` on PATH if installed, otherwise `uvx --from memoir-ai==<pin> memoir`, otherwise `uv tool run --from memoir-ai==<pin> memoir` (pin lives in `scripts/resolve-memoir-cli.sh`).plugins/claude-code/skills/memoir-onboard/SKILL.md:Wrap **every** memoir call — both branches A and B, both reads and writes — in a subshell that cd's into the store first, and use `"$MEMOIR"` (NOT bare `memoir`) so the call works on machines without a global memoir install:plugins/claude-code/skills/memoir-onboard/SKILL.md:`test.strategy` — test layout, how to run tests, what coverage emphasizes.plugins/claude-code/skills/memoir-onboard/SKILL.md:bash -c 'source "${CLAUDE_PLUGIN_ROOT}/hooks/common.sh" >/dev/null 2>&1; concurrent_session_warning'plugins/claude-code/skills/memoir-onboard/SKILL.md:If the command prints anything, stop and report: "Concurrent session detected — run /memoir:onboard after the other session finishes, or set a distinct MEMOIR_STORE."plugins/claude-code/skills/memoir-onboard/SKILL.md:`git log --oneline -20` and `git log --stat -5` for recent change patterns (informs lessons / rules).plugins/claude-code/skills/memoir-onboard/SKILL.md:Run `git log --stat <last_sha>..HEAD` to enumerate changed paths since the last onboarding pass.plugins/claude-code/skills/memoir-onboard/SKILL.md:Run the confirmation check (`memoir status` → path == `$STORE_PATH`) before doing any writes.plugins/claude-code/skills/memoir-onboard/SKILL.md:`python3 ${CLAUDE_PLUGIN_ROOT}/skills/memoir-onboard/extractors.py walk <root>` — JSON list of `{path, size, mtime_ns, kind}` plus `snapshot_hash`.plugins/claude-code/skills/memoir-onboard/SKILL.md:`python3 ${CLAUDE_PLUGIN_ROOT}/skills/memoir-onboard/extractors.py extract <path>` — `key=value` blob for one file (with `kind=` first, `extractor.stdlib.fields=[…]` for provenance).plugins/claude-code/skills/memoir-onboard/SKILL.md:`python3 ${CLAUDE_PLUGIN_ROOT}/skills/memoir-onboard/extractors.py snapshot-hash <root>` — just the hash.plugins/claude-code/skills/memoir-onboard/SKILL.md:`python3 ${CLAUDE_PLUGIN_ROOT}/skills/memoir-onboard/extractors.py tree <root>` — pruned tree.plugins/claude-code/skills/memoir-onboard/SKILL.md:`python3 ${CLAUDE_PLUGIN_ROOT}/skills/memoir-onboard/extractors.py shape <root>` — `{shape, overview}` JSON.plugins/claude-code/skills/memoir-onboard/SKILL.md:bash -c 'source "${CLAUDE_PLUGIN_ROOT}/hooks/common.sh" >/dev/null 2>&1; concurrent_session_warning'plugins/claude-code/skills/memoir-onboard/SKILL.md:For each file in the walk result, run `extractors.py extract <path>` and capture the structured blob.plugins/claude-code/skills/memoir-onboard/SKILL.md:python3 -c "import json,sys; print('\n'.join(e['path'] for e in json.loads(sys.argv[1])['files']))" "$WALK_JSON" \plugins/claude-code/skills/memoir-onboard/SKILL.md:**added** (in walk, not in store) → run `extract <path>`, write `files.<san>.meta` and `files.<san>.summary`.plugins/claude-code/skills/memoir-onboard/SKILL.md:**modified** (same path, different `(size, mtime_ns)`) → re-run `extract <path>`, write both keys.plugins/claude-code/skills/memoir-onboard/SKILL.md:If a cold run fails partway through, the `_meta.*` keys act as commit markers.plugins/claude-code/skills/memory-recall/SKILL.md:Run this preamble first and reuse `$STORE` and `$MEMOIR` for every memoir invocation below:plugins/claude-code/skills/memory-recall/SKILL.md:`$MEMOIR` is a wrapper that resolves the right invocation for this machine — `memoir` on PATH if installed, otherwise `uvx --from memoir-ai==<pin> memoir`, otherwise `uv tool run --from memoir-ai==<pin> memoir` (pin lives in `scripts/resolve-memoir-cli.sh`).plugins/claude-code/skills/memory-recall/SKILL.md:that way recall works on machines where memoir isn't `pip install`ed but `uv` is.plugins/codex/skills/memoir-onboard/SKILL.md:bash -c 'source "${PLUGIN_ROOT}/hooks/common.sh" >/dev/null 2>&1; in_git_repo && echo codebase:onboard || echo project:onboard'plugins/codex/skills/memoir-onboard/SKILL.md:Store: !`bash -c 'if [ -n "${MEMOIR_STORE:-}" ];plugins/codex/skills/memoir-onboard/SKILL.md:python3 - "$manifest" <<'PY'plugins/codex/skills/memoir-onboard/SKILL.md:`$MEMOIR` is a wrapper that resolves the right invocation for this machine — `memoir` on PATH if installed, otherwise `uvx --from memoir-ai==<pin> memoir`, otherwise `uv tool run --from memoir-ai==<pin> memoir` (pin lives in `scripts/resolve-memoir-cli.sh`).plugins/codex/skills/memoir-onboard/SKILL.md:Wrap **every** memoir call — both branches A and B, both reads and writes — in a subshell that cd's into the store first, and use `"$MEMOIR"` (NOT bare `memoir`) so the call works on machines without a global memoir install:plugins/codex/skills/memoir-onboard/SKILL.md:`test.strategy` — test layout, how to run tests, what coverage emphasizes.plugins/codex/skills/memoir-onboard/SKILL.md:bash -c 'source "${PLUGIN_ROOT}/hooks/common.sh" >/dev/null 2>&1; concurrent_session_warning'plugins/codex/skills/memoir-onboard/SKILL.md:If the command prints anything, stop and report: "Concurrent session detected - run memoir-onboard after the other session finishes, or set a distinct MEMOIR_STORE."plugins/codex/skills/memoir-onboard/SKILL.md:`git log --oneline -20` and `git log --stat -5` for recent change patterns (informs lessons / rules).plugins/codex/skills/memoir-onboard/SKILL.md:Run `git log --stat <last_sha>..HEAD` to enumerate changed paths since the last onboarding pass.plugins/codex/skills/memoir-onboard/SKILL.md:Run the confirmation check (`memoir status` → path == `$STORE_PATH`) before doing any writes.plugins/codex/skills/memoir-onboard/SKILL.md:`python3 ${PLUGIN_ROOT}/skills/memoir-onboard/extractors.py walk <root>` — JSON list of `{path, size, mtime_ns, kind}` plus `snapshot_hash`.plugins/codex/skills/memoir-onboard/SKILL.md:`python3 ${PLUGIN_ROOT}/skills/memoir-onboard/extractors.py extract <path>` — `key=value` blob for one file (with `kind=` first, `extractor.stdlib.fields=[…]` for provenance).plugins/codex/skills/memoir-onboard/SKILL.md:`python3 ${PLUGIN_ROOT}/skills/memoir-onboard/extractors.py snapshot-hash <root>` — just the hash.plugins/codex/skills/memoir-onboard/SKILL.md:`python3 ${PLUGIN_ROOT}/skills/memoir-onboard/extractors.py tree <root>` — pruned tree.plugins/codex/skills/memoir-onboard/SKILL.md:`python3 ${PLUGIN_ROOT}/skills/memoir-onboard/extractors.py shape <root>` — `{shape, overview}` JSON.plugins/codex/skills/memoir-onboard/SKILL.md:bash -c 'source "${PLUGIN_ROOT}/hooks/common.sh" >/dev/null 2>&1; concurrent_session_warning'plugins/codex/skills/memoir-onboard/SKILL.md:For each file in the walk result, run `extractors.py extract <path>` and capture the structured blob.plugins/codex/skills/memoir-onboard/SKILL.md:python3 -c "import json,sys; print('\n'.join(e['path'] for e in json.loads(sys.argv[1])['files']))" "$WALK_JSON" \plugins/codex/skills/memoir-onboard/SKILL.md:**added** (in walk, not in store) → run `extract <path>`, write `files.<san>.meta` and `files.<san>.summary`.plugins/codex/skills/memoir-onboard/SKILL.md:**modified** (same path, different `(size, mtime_ns)`) → re-run `extract <path>`, write both keys.plugins/codex/skills/memoir-onboard/SKILL.md:If a cold run fails partway through, the `_meta.*` keys act as commit markers.plugins/codex/skills/memoir-onboard/SKILL.md:A subsequent forced memoir-onboard run will rewrite cleanly.plugins/codex/skills/memoir-remember/SKILL.md:python3 - "$manifest" <<'PY'plugins/codex/skills/memoir-remember/SKILL.md:bash "$PLUGIN_ROOT/scripts/ensure-store.sh" "$STORE" >/dev/nullplugins/codex/skills/memoir-status/SKILL.md:Run:plugins/codex/skills/memoir-status/SKILL.md:python3 - "$manifest" <<'PY'plugins/codex/skills/memoir-status/SKILL.md:bash "$PLUGIN_ROOT/scripts/status-cmd.sh"plugins/codex/skills/memoir-ui/SKILL.md:Run:plugins/codex/skills/memoir-ui/SKILL.md:python3 - "$manifest" <<'PY'plugins/codex/skills/memoir-ui/SKILL.md:bash "$PLUGIN_ROOT/scripts/memoir-ui-ctl.sh" start "$STORE"plugins/codex/skills/memory-recall/SKILL.md:Run this preamble first and reuse `$STORE` and `$MEMOIR` for every memoir invocation below:plugins/codex/skills/memory-recall/SKILL.md:python3 - "$manifest" <<'PY'plugins/codex/skills/memory-recall/SKILL.md:`$MEMOIR` is a wrapper that resolves the right invocation for this machine — `memoir` on PATH if installed, otherwise `uvx --from memoir-ai==<pin> memoir`, otherwise `uv tool run --from memoir-ai==<pin> memoir` (pin lives in `scripts/resolve-memoir-cli.sh`).plugins/codex/skills/memory-recall/SKILL.md:that way recall works on machines where memoir isn't `pip install`ed but `uv` is.
Install scripts (2)
Makefile(make install):pip install -e .README.md:curl -LsSf https://astral.sh/uv/install.sh | sh· uses the network
MCP servers (1)
README.md(memoir):uvx --from memoir-ai[mcp] memoir-mcp
Hooks (7)
plugins/claude-code/hooks/hooks.json(SessionStart):bash ${CLAUDE_PLUGIN_ROOT}/hooks/session-start.shplugins/claude-code/hooks/hooks.json(UserPromptSubmit):bash ${CLAUDE_PLUGIN_ROOT}/hooks/user-prompt-submit.shplugins/claude-code/hooks/hooks.json(Stop):bash ${CLAUDE_PLUGIN_ROOT}/hooks/stop.shplugins/claude-code/hooks/hooks.json(SessionEnd):bash ${CLAUDE_PLUGIN_ROOT}/hooks/session-end.shplugins/codex/hooks/hooks.json(SessionStart):bash "${PLUGIN_ROOT}/hooks/session-start.sh"plugins/codex/hooks/hooks.json(UserPromptSubmit):bash "${PLUGIN_ROOT}/hooks/user-prompt-submit.sh"plugins/codex/hooks/hooks.json(Stop):bash "${PLUGIN_ROOT}/hooks/stop.sh"
- Medium risk: runs-git-hook-remote-exec (.pre-commit-config.yaml:2)
- Medium risk: runs-git-hook-remote-exec (.pre-commit-config.yaml:23)
- Medium risk: runs-git-hook-remote-exec (.pre-commit-config.yaml:30)
- Medium risk: runs-git-hook-remote-exec (.pre-commit-config.yaml:40)
- Medium risk: runs-install-script-remote-exec (README.md:86)
- Medium risk: runs-mcp-server-unpinned-runner (README.md:153)
- Low risk: network-instructions (Makefile:37)
- Low risk: scripts-present (benchmarks/classifier.py)
- Low risk: scripts-present (benchmarks/locomo/run.py)
- Low risk: scripts-present (plugins/claude-code/hooks/session-end.sh)
- Low risk: scripts-present (plugins/claude-code/hooks/session-start.sh)
- Low risk: scripts-present (plugins/claude-code/hooks/stop.sh)
- Low risk: scripts-present (plugins/claude-code/hooks/user-prompt-submit.sh)
- Low risk: network-instructions (plugins/claude-code/skills/memory-recall/SKILL.md:20)
- Low risk: scripts-present (plugins/codex/hooks/session-start.sh)
- Low risk: scripts-present (plugins/codex/hooks/stop.sh)
- Low risk: scripts-present (plugins/codex/hooks/user-prompt-submit.sh)
- Low risk: network-instructions (plugins/codex/skills/memory-recall/SKILL.md:36)
- Low risk: network-instructions (pyproject.toml:57)
- Low risk: scripts-present (scripts/cc-smoke/run.sh)
- Low risk: scripts-present (scripts/check_version_consistency.py)
Attribution and licence
- First committed by
zhangfengcdt
- Declared authors
- zhangfengcdt (declared in pyproject.toml)
- Publisher
- Feng Zhang (Community)
- Source
- zhangfengcdt/memoir · commit 7fd1b3e
- Licence
- Apache License 2.0 (files served here)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
- Contributors
- zhangfengcdt, rapidSpace, disafronov, KevFoxCodeCraft, Ottohere-Mourn
AutoGineer is not affiliated with or endorsed by zhangfengcdt.
Install from source
489 of its files are not served here, so there is no ZIP. zhangfengcdt/memoir at 7fd1b3e