mnemo-claude 0.12.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. mnemo_claude-0.12.0/LICENSE +21 -0
  2. mnemo_claude-0.12.0/PKG-INFO +542 -0
  3. mnemo_claude-0.12.0/README.md +528 -0
  4. mnemo_claude-0.12.0/pyproject.toml +46 -0
  5. mnemo_claude-0.12.0/setup.cfg +4 -0
  6. mnemo_claude-0.12.0/src/mnemo/__init__.py +1 -0
  7. mnemo_claude-0.12.0/src/mnemo/__main__.py +4 -0
  8. mnemo_claude-0.12.0/src/mnemo/cli/__init__.py +31 -0
  9. mnemo_claude-0.12.0/src/mnemo/cli/_helpers/__init__.py +80 -0
  10. mnemo_claude-0.12.0/src/mnemo/cli/commands/__init__.py +25 -0
  11. mnemo_claude-0.12.0/src/mnemo/cli/commands/briefing.py +35 -0
  12. mnemo_claude-0.12.0/src/mnemo/cli/commands/dedup_rules.py +44 -0
  13. mnemo_claude-0.12.0/src/mnemo/cli/commands/disable_rule.py +62 -0
  14. mnemo_claude-0.12.0/src/mnemo/cli/commands/doctor.py +124 -0
  15. mnemo_claude-0.12.0/src/mnemo/cli/commands/doctor_checks/__init__.py +30 -0
  16. mnemo_claude-0.12.0/src/mnemo/cli/commands/doctor_checks/activation.py +211 -0
  17. mnemo_claude-0.12.0/src/mnemo/cli/commands/doctor_checks/fidelity.py +38 -0
  18. mnemo_claude-0.12.0/src/mnemo/cli/commands/doctor_checks/misc.py +92 -0
  19. mnemo_claude-0.12.0/src/mnemo/cli/commands/doctor_checks/orphan_worktree_briefings.py +85 -0
  20. mnemo_claude-0.12.0/src/mnemo/cli/commands/doctor_checks/reflex.py +189 -0
  21. mnemo_claude-0.12.0/src/mnemo/cli/commands/doctor_checks/rules.py +179 -0
  22. mnemo_claude-0.12.0/src/mnemo/cli/commands/extract.py +88 -0
  23. mnemo_claude-0.12.0/src/mnemo/cli/commands/init.py +204 -0
  24. mnemo_claude-0.12.0/src/mnemo/cli/commands/list_enforced.py +56 -0
  25. mnemo_claude-0.12.0/src/mnemo/cli/commands/migrate_worktree_briefings.py +90 -0
  26. mnemo_claude-0.12.0/src/mnemo/cli/commands/misc.py +87 -0
  27. mnemo_claude-0.12.0/src/mnemo/cli/commands/recall.py +79 -0
  28. mnemo_claude-0.12.0/src/mnemo/cli/commands/status.py +224 -0
  29. mnemo_claude-0.12.0/src/mnemo/cli/commands/statusline.py +34 -0
  30. mnemo_claude-0.12.0/src/mnemo/cli/commands/telemetry.py +24 -0
  31. mnemo_claude-0.12.0/src/mnemo/cli/parser.py +102 -0
  32. mnemo_claude-0.12.0/src/mnemo/cli/runtime.py +47 -0
  33. mnemo_claude-0.12.0/src/mnemo/core/__init__.py +0 -0
  34. mnemo_claude-0.12.0/src/mnemo/core/agent.py +111 -0
  35. mnemo_claude-0.12.0/src/mnemo/core/briefing.py +234 -0
  36. mnemo_claude-0.12.0/src/mnemo/core/config.py +138 -0
  37. mnemo_claude-0.12.0/src/mnemo/core/dashboard.py +200 -0
  38. mnemo_claude-0.12.0/src/mnemo/core/dedup_rules.py +169 -0
  39. mnemo_claude-0.12.0/src/mnemo/core/errors.py +126 -0
  40. mnemo_claude-0.12.0/src/mnemo/core/extract/__init__.py +542 -0
  41. mnemo_claude-0.12.0/src/mnemo/core/extract/inbox/__init__.py +75 -0
  42. mnemo_claude-0.12.0/src/mnemo/core/extract/inbox/apply.py +160 -0
  43. mnemo_claude-0.12.0/src/mnemo/core/extract/inbox/branches/__init__.py +14 -0
  44. mnemo_claude-0.12.0/src/mnemo/core/extract/inbox/branches/auto_promoted.py +163 -0
  45. mnemo_claude-0.12.0/src/mnemo/core/extract/inbox/branches/inbox_flow.py +180 -0
  46. mnemo_claude-0.12.0/src/mnemo/core/extract/inbox/branches/upgrade.py +41 -0
  47. mnemo_claude-0.12.0/src/mnemo/core/extract/inbox/dedup.py +218 -0
  48. mnemo_claude-0.12.0/src/mnemo/core/extract/inbox/io.py +86 -0
  49. mnemo_claude-0.12.0/src/mnemo/core/extract/inbox/paths.py +66 -0
  50. mnemo_claude-0.12.0/src/mnemo/core/extract/inbox/rendering.py +157 -0
  51. mnemo_claude-0.12.0/src/mnemo/core/extract/inbox/state_io.py +94 -0
  52. mnemo_claude-0.12.0/src/mnemo/core/extract/inbox/types.py +46 -0
  53. mnemo_claude-0.12.0/src/mnemo/core/extract/promote.py +100 -0
  54. mnemo_claude-0.12.0/src/mnemo/core/extract/prompts/__init__.py +42 -0
  55. mnemo_claude-0.12.0/src/mnemo/core/extract/prompts/encoding.py +30 -0
  56. mnemo_claude-0.12.0/src/mnemo/core/extract/prompts/render.py +127 -0
  57. mnemo_claude-0.12.0/src/mnemo/core/extract/prompts/templates/__init__.py +7 -0
  58. mnemo_claude-0.12.0/src/mnemo/core/extract/prompts/templates/briefing.py +50 -0
  59. mnemo_claude-0.12.0/src/mnemo/core/extract/prompts/templates/few_shot_feedback.py +167 -0
  60. mnemo_claude-0.12.0/src/mnemo/core/extract/prompts/templates/few_shot_simple.py +44 -0
  61. mnemo_claude-0.12.0/src/mnemo/core/extract/prompts/templates/schema.py +48 -0
  62. mnemo_claude-0.12.0/src/mnemo/core/extract/prompts/templates/system_feedback.py +105 -0
  63. mnemo_claude-0.12.0/src/mnemo/core/extract/prompts/templates/system_simple.py +33 -0
  64. mnemo_claude-0.12.0/src/mnemo/core/extract/prompts/vault_tags.py +33 -0
  65. mnemo_claude-0.12.0/src/mnemo/core/extract/scanner.py +215 -0
  66. mnemo_claude-0.12.0/src/mnemo/core/filters.py +229 -0
  67. mnemo_claude-0.12.0/src/mnemo/core/llm.py +184 -0
  68. mnemo_claude-0.12.0/src/mnemo/core/locks.py +44 -0
  69. mnemo_claude-0.12.0/src/mnemo/core/log_utils.py +15 -0
  70. mnemo_claude-0.12.0/src/mnemo/core/log_writer.py +70 -0
  71. mnemo_claude-0.12.0/src/mnemo/core/mcp/__init__.py +6 -0
  72. mnemo_claude-0.12.0/src/mnemo/core/mcp/access_log.py +113 -0
  73. mnemo_claude-0.12.0/src/mnemo/core/mcp/access_log_summary.py +220 -0
  74. mnemo_claude-0.12.0/src/mnemo/core/mcp/recall.py +346 -0
  75. mnemo_claude-0.12.0/src/mnemo/core/mcp/server.py +231 -0
  76. mnemo_claude-0.12.0/src/mnemo/core/mcp/session_state.py +212 -0
  77. mnemo_claude-0.12.0/src/mnemo/core/mcp/tools.py +291 -0
  78. mnemo_claude-0.12.0/src/mnemo/core/mirror.py +103 -0
  79. mnemo_claude-0.12.0/src/mnemo/core/paths.py +44 -0
  80. mnemo_claude-0.12.0/src/mnemo/core/pricing.py +36 -0
  81. mnemo_claude-0.12.0/src/mnemo/core/reflex/__init__.py +1 -0
  82. mnemo_claude-0.12.0/src/mnemo/core/reflex/bm25.py +100 -0
  83. mnemo_claude-0.12.0/src/mnemo/core/reflex/gates.py +74 -0
  84. mnemo_claude-0.12.0/src/mnemo/core/reflex/index.py +159 -0
  85. mnemo_claude-0.12.0/src/mnemo/core/reflex/stopwords.py +42 -0
  86. mnemo_claude-0.12.0/src/mnemo/core/reflex/tokenizer.py +44 -0
  87. mnemo_claude-0.12.0/src/mnemo/core/rule_activation/__init__.py +53 -0
  88. mnemo_claude-0.12.0/src/mnemo/core/rule_activation/activity_log.py +85 -0
  89. mnemo_claude-0.12.0/src/mnemo/core/rule_activation/globs.py +113 -0
  90. mnemo_claude-0.12.0/src/mnemo/core/rule_activation/index.py +316 -0
  91. mnemo_claude-0.12.0/src/mnemo/core/rule_activation/matching.py +232 -0
  92. mnemo_claude-0.12.0/src/mnemo/core/rule_activation/parsing.py +263 -0
  93. mnemo_claude-0.12.0/src/mnemo/core/session.py +66 -0
  94. mnemo_claude-0.12.0/src/mnemo/core/text_utils.py +27 -0
  95. mnemo_claude-0.12.0/src/mnemo/core/transcript.py +54 -0
  96. mnemo_claude-0.12.0/src/mnemo/hooks/__init__.py +0 -0
  97. mnemo_claude-0.12.0/src/mnemo/hooks/pre_tool_use.py +161 -0
  98. mnemo_claude-0.12.0/src/mnemo/hooks/session_end.py +304 -0
  99. mnemo_claude-0.12.0/src/mnemo/hooks/session_start.py +241 -0
  100. mnemo_claude-0.12.0/src/mnemo/hooks/user_prompt_submit.py +209 -0
  101. mnemo_claude-0.12.0/src/mnemo/install/__init__.py +0 -0
  102. mnemo_claude-0.12.0/src/mnemo/install/preflight.py +105 -0
  103. mnemo_claude-0.12.0/src/mnemo/install/scaffold.py +48 -0
  104. mnemo_claude-0.12.0/src/mnemo/install/settings.py +451 -0
  105. mnemo_claude-0.12.0/src/mnemo/statusline.py +319 -0
  106. mnemo_claude-0.12.0/src/mnemo/templates/HOME.md +53 -0
  107. mnemo_claude-0.12.0/src/mnemo/templates/README.md +34 -0
  108. mnemo_claude-0.12.0/src/mnemo/templates/__init__.py +0 -0
  109. mnemo_claude-0.12.0/src/mnemo/templates/graph-dark-gold.css +16 -0
  110. mnemo_claude-0.12.0/src/mnemo/templates/mnemo.config.json +10 -0
  111. mnemo_claude-0.12.0/src/mnemo_claude.egg-info/PKG-INFO +542 -0
  112. mnemo_claude-0.12.0/src/mnemo_claude.egg-info/SOURCES.txt +114 -0
  113. mnemo_claude-0.12.0/src/mnemo_claude.egg-info/dependency_links.txt +1 -0
  114. mnemo_claude-0.12.0/src/mnemo_claude.egg-info/entry_points.txt +2 -0
  115. mnemo_claude-0.12.0/src/mnemo_claude.egg-info/requires.txt +4 -0
  116. mnemo_claude-0.12.0/src/mnemo_claude.egg-info/top_level.txt +1 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 xyrlan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,542 @@
1
+ Metadata-Version: 2.4
2
+ Name: mnemo-claude
3
+ Version: 0.12.0
4
+ Summary: The Obsidian that populates itself so your Claude never forgets.
5
+ Author: xyrlan
6
+ License: MIT
7
+ Requires-Python: >=3.8
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Provides-Extra: dev
11
+ Requires-Dist: pytest>=7.0; extra == "dev"
12
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
13
+ Dynamic: license-file
14
+
15
+ # mnemo
16
+
17
+ > The Obsidian that populates itself so your Claude never forgets.
18
+
19
+ **mnemo** is a Claude Code plugin that turns every coding session into a
20
+ self-organizing knowledge base — and then feeds that knowledge back into
21
+ Claude so it stops forgetting what you taught it last week.
22
+
23
+ It runs as a hooks-only, stdlib-only Python package. Zero third-party
24
+ dependencies, zero network calls. Identical on Linux,
25
+ macOS, and Windows.
26
+
27
+ ## Install
28
+
29
+ One command:
30
+
31
+ ```bash
32
+ npx @xyrlan/mnemo install
33
+ ```
34
+
35
+ That installs the Python package (via `uv` / `pipx` / `pip --user`, whichever is available), prompts you to choose **global** (every Claude Code session) or **project** (this directory only), wires the hooks + MCP server + slash commands, and you're done.
36
+
37
+ Non-interactive variants:
38
+
39
+ ```bash
40
+ npx @xyrlan/mnemo install --yes # global, no prompts
41
+ npx @xyrlan/mnemo install --project --yes # project-scoped, no prompts
42
+ ```
43
+
44
+ `npx @xyrlan/mnemo uninstall [--scope global|project|both]` reverses everything; the vault is preserved.
45
+
46
+ **Prerequisites:** Python 3.8+ on your machine. Node is already there if you run `npx`. mnemo's npm wrapper is zero-dep and ~150 LOC — it's a thin bootstrap, not the runtime.
47
+
48
+ `mnemo init` (run automatically by `npx … install`) is idempotent and does four things:
49
+
50
+ 1. Scaffolds a vault at `~/mnemo/` (or wherever you point it)
51
+ 2. Injects four hooks into `~/.claude/settings.json`:
52
+ `SessionStart`, `SessionEnd`, `PreToolUse` (rule activation, v0.5),
53
+ and `UserPromptSubmit` (Prompt Reflex, v0.8)
54
+ 3. Registers a stdio MCP server in `~/.claude.json` so Claude Code can call mnemo's tools
55
+ 4. Wires an additive status line composer (preserves your existing one if you have it)
56
+ 5. Writes `~/.claude/commands/<name>.md` slash command files so `/init`, `/status`, `/doctor`, etc. work inside Claude Code without a separate plugin install
57
+
58
+ That's it. Use Claude Code normally — your vault populates itself, the
59
+ HOME dashboard regenerates after every extraction, and Claude starts
60
+ consulting captured rules on its own.
61
+
62
+ ### Alternative install paths
63
+
64
+ If you'd rather skip npm entirely:
65
+
66
+ ```bash
67
+ pipx install mnemo-claude # or: uv tool install mnemo-claude
68
+ mnemo init # global
69
+ mnemo init --project # project-only
70
+ ```
71
+
72
+ The PyPI distribution is named **`mnemo-claude`** (the bare `mnemo` slot was already taken on PyPI by an unrelated package). The CLI binary, import name, and project repository are still `mnemo` — only `pipx install` / `pip install` see the dashed name.
73
+
74
+ If you'd rather wire the slash commands via Claude Code's marketplace:
75
+
76
+ ```
77
+ /plugin marketplace add xyrlan/mnemo
78
+ /plugin install mnemo@mnemo-marketplace
79
+ ```
80
+
81
+ (The marketplace path requires `pipx install mnemo-claude` first — `/plugin install` registers slash commands but does not install the Python runtime.)
82
+
83
+ ### Installation scope: global vs project (v0.12+)
84
+
85
+ By default `mnemo init` installs **globally** — every Claude Code session,
86
+ in any directory, fires mnemo. Pass `--project` (alias `--local`) to scope
87
+ the install to the current directory only:
88
+
89
+ ```
90
+ mnemo init --project
91
+ ```
92
+
93
+ This writes to `<cwd>/.claude/settings.json` + `<cwd>/.mcp.json`, scaffolds
94
+ a self-contained vault at `<cwd>/.mnemo/`, and appends `.claude/` and
95
+ `.mnemo/` to your project's `.gitignore`. `~/.claude/settings.json`,
96
+ `~/.claude.json`, and `~/mnemo/` stay untouched. Claude Code only loads
97
+ mnemo when launched in that directory; sessions in any other path get the
98
+ unmodified global behavior (or no mnemo at all).
99
+
100
+ Use this when you want to:
101
+
102
+ - evaluate mnemo on a single project before committing to it globally,
103
+ - isolate per-project memory so different repos don't share a vault,
104
+ - ship a repo where mnemo is part of the dev environment without forcing
105
+ collaborators to install it globally — they get it automatically when
106
+ they `cd` into the project.
107
+
108
+ `mnemo uninstall --project` removes only the local install (the vault is
109
+ preserved, same as global uninstall). `mnemo status --scope project|global|all`
110
+ reports both scopes independently — Claude Code itself decides which is
111
+ "active" for any given session based on cwd.
112
+
113
+ > Note on portability: hook commands embed an absolute Python path
114
+ > (`sys.executable`), so `<cwd>/.claude/settings.json` is gitignored by
115
+ > default. Each developer runs their own `mnemo init --project` to wire
116
+ > their interpreter.
117
+
118
+ ## How it works — Capture → Present → Inject → Reflex
119
+
120
+ mnemo's tagline is one sentence: *"so your Claude never forgets."* That
121
+ breaks into four stages, each shipped in a different release, all live
122
+ together from v0.8 on.
123
+
124
+ ### 1. Capture (v0.2 → v0.3.1)
125
+
126
+ mnemo watches Claude Code's lifecycle hooks and writes a structured trail
127
+ into your vault as you work:
128
+
129
+ - **Session start / end markers** — `🟢` and `🔴` in `bots/<repo>/logs/YYYY-MM-DD.md`
130
+ - **Claude memory mirror** — anything Claude saves to `~/.claude/projects/*/memory/`
131
+ is mirrored into `bots/<repo>/memory/` so it lives next to the rest of the trail
132
+ - **Per-session briefings** *(opt-in, v0.3.1)* — at session end, an LLM pass
133
+ summarizes the full transcript into a structured handoff document under
134
+ `bots/<repo>/briefings/sessions/`. Briefings are the dense input that
135
+ feeds extraction; they're the difference between mnemo capturing
136
+ ~1 file/day vs. capturing every meaningful decision.
137
+
138
+ The extraction pipeline (`mnemo extract`, also auto-run after sessions
139
+ when enabled) consolidates everything in `bots/` into canonical Tier 2
140
+ pages under `shared/{feedback,user,reference,project}/`. Single-source
141
+ pages auto-promote into the canonical layer; multi-source clusters
142
+ (cross-agent merges) stage in `shared/_inbox/<type>/` for review. Your
143
+ manual edits to auto-promoted files are protected by content-addressing —
144
+ a conflict produces a `.proposed.md` sibling instead of overwriting your
145
+ work.
146
+
147
+ ### 2. Present (v0.4)
148
+
149
+ Capture without surfacing is just a dump. v0.4 added two consumer
150
+ surfaces over the same data:
151
+
152
+ - **HOME.md dashboard** — a managed block at the top of `HOME.md`
153
+ regenerates after every extraction. Pages are grouped by trust tier
154
+ (cross-agent synthesized first, single-source auto-promoted second) and
155
+ by topic tag, so you can scan the project brain in seconds inside
156
+ Obsidian. Everything below the managed block is yours to edit; mnemo
157
+ never touches it.
158
+ - **Dimensional tags** — extraction asks the LLM to tag each page with
159
+ topic kebab-case identifiers (`auth`, `react`, `package-management`),
160
+ using a controlled-vocabulary hint built from your existing vault tags
161
+ to prevent sprawl. Tags become the ontology Claude navigates in v0.5.
162
+
163
+ ### 3. Inject (v0.5)
164
+
165
+ The loop closes. Claude Code itself reaches into mnemo at the start of
166
+ every session, no manual command needed.
167
+
168
+ - **MCP stdio server** — `mnemo init` registers a long-running JSON-RPC
169
+ server in `~/.claude.json` exposing three read-only tools. All three
170
+ default to `scope="project"` (only rules owned by the current repo);
171
+ pass `scope="vault"` for cross-project lookups:
172
+ - `list_rules_by_topic(topic, scope?)` — slugs sorted by source count desc
173
+ (multi-agent synthesized rules surface first)
174
+ - `read_mnemo_rule(slug, scope?)` — full body + frontmatter
175
+ - `get_mnemo_topics(scope?)` — sorted union of topic tags
176
+ - **SessionStart topic injection** *(opt-in)* — the SessionStart hook
177
+ emits a ~120-token instruction listing the topics in your vault and
178
+ telling Claude to call the MCP tools BEFORE writing code when the task
179
+ matches a known topic. ~120 tokens regardless of vault size: the topic
180
+ list is the only thing pre-loaded; rule bodies are fetched on demand.
181
+ - **Filter parity** — both the dashboard and the MCP tools call the same
182
+ `is_consumer_visible` predicate, so evolving and needs-review pages
183
+ never reach Claude.
184
+
185
+ The result: Claude consumes in real time the rules you taught it weeks
186
+ earlier in a different session, without you having to remember to copy
187
+ them in.
188
+
189
+ ### 4. Reflex (v0.8)
190
+
191
+ SessionStart tells Claude *what topics exist*. PreToolUse fires *when Claude
192
+ touches a file*. **Reflex** closes the gap in between: it reacts to the
193
+ actual prompt you just typed and injects the single most relevant rule
194
+ before Claude thinks about the question.
195
+
196
+ - **BM25F retrieval** — the `UserPromptSubmit` hook tokenizes your prompt
197
+ (lowercase + fenced-code stripping + EN/PT stopwords), scores every
198
+ consumer-visible rule across five weighted fields (`name`, `topic_tags`,
199
+ `aliases`, `description`, `body`), and returns the top candidate. Pure
200
+ stdlib; p50 1.3 ms / p95 1.7 ms on a 500-rule vault.
201
+ - **Triple-gate confidence test** — silence is the default. A rule is
202
+ injected only if it clears all three gates: term-overlap ≥ 2, relative
203
+ gap ≥ 1.5× over the runner-up, and absolute score ≥ 2.0. Low-confidence
204
+ matches stay silent; wrong context is worse than no context.
205
+ - **`aliases:` bridges** — rules can declare a list of lowercase synonym
206
+ tokens (e.g. `aliases: [banco, database, db]`) so a prompt in Portuguese
207
+ activates an English-written rule. Extraction auto-emits aliases when
208
+ the rule body carries domain terms with bilingual synonyms.
209
+ - **Session-lifetime dedupe** — a rule injected once is not re-injected
210
+ the same day (`injected_cache` in `.mnemo/mcp-call-counter.json`). The
211
+ `PreToolUse` enrichment path reads the same cache so Reflex and
212
+ enrichment never double-surface the same rule in one session.
213
+ - **Cap** — each session emits at most `reflex.maxEmissionsPerSession`
214
+ (default 10). Hitting the cap logs `silence_reason: session_cap_reached`
215
+ for doctor to analyze.
216
+ - **Fail-open absolute** — any exception anywhere in the pipeline returns
217
+ exit 0 with empty stdout. Reflex can never stall a prompt.
218
+
219
+ ### Last-briefing handoff (v0.10+)
220
+
221
+ When `briefings.injectLastOnSessionStart` is true (default), every new Claude
222
+ Code session in a project whose canonical agent has at least one briefing
223
+ on disk gets a `[last-briefing session=… date=… duration_minutes=…] … [/last-briefing]`
224
+ block appended to the SessionStart injection envelope. Worktrees of the same
225
+ repo share one briefing pool, resolved via `.git` worktree pointers.
226
+
227
+ If you have orphan worktree briefings from before this change, run
228
+ `mnemo migrate-worktree-briefings --repos /path/to/repo --dry-run` to preview
229
+ moves, then drop `--dry-run` to apply.
230
+
231
+ **Known limitation (early upgraders):** If you upgraded to v0.10 before any
232
+ canonical-repo session wrapped up, `mnemo doctor` cannot detect orphan
233
+ worktree briefings (the check requires the canonical agent to have at least
234
+ one briefing on disk). In that case, run the migration command manually and
235
+ inspect `bots/` for `<repo>-<suffix>` subdirectories.
236
+
237
+ ### Cost telemetry (v0.10+)
238
+
239
+ Every `llm.call()` (briefing + extraction consolidations) writes a
240
+ `tool: "llm.call"` entry into `.mnemo/mcp-access-log.jsonl` with
241
+ `usage.input_tokens` / `usage.output_tokens`. Every SessionStart writes a
242
+ `tool: "session_start.inject"` entry with `envelope_bytes`. `mnemo telemetry`
243
+ aggregates both into per-purpose token totals + estimated USD (using a
244
+ hard-coded pricing table at `src/mnemo/core/pricing.py` — bump the table
245
+ when Anthropic prices change).
246
+
247
+ ## Scope model (v0.7+)
248
+
249
+ Rules in `shared/{feedback,user,reference}/` are **local by default**: a rule
250
+ is visible only from projects that appear in its `sources[]` frontmatter. A
251
+ rule is automatically promoted to **universal** when it has been seen in at
252
+ least `scoping.universalThreshold` distinct projects (default: 2). Universal
253
+ rules are visible from every project.
254
+
255
+ MCP retrieval accepts three `scope` values:
256
+
257
+ | `scope` | Returns |
258
+ |---------------|------------------------------------------------------|
259
+ | `"project"` | local rules + universal rules (default) |
260
+ | `"local-only"`| local rules only (legacy v0.6.2 behaviour) |
261
+ | `"vault"` | every consumer-visible rule |
262
+
263
+ Promotion and demotion are automatic: the index is re-derived from `sources[]`
264
+ on every SessionStart. To change the threshold, set
265
+ `scoping.universalThreshold` in `mnemo.config.json`.
266
+
267
+ ## Status line
268
+
269
+ After `mnemo init`, your Claude Code status line shows the brain's heartbeat:
270
+
271
+ ```
272
+ mnemo mcp · 9 topics · 7↓ today · 3⚡
273
+ ```
274
+
275
+ - `mnemo mcp` — MCP server is registered in `~/.claude.json`
276
+ - `9 topics` — topic tags currently known in your vault (live count)
277
+ - `7↓ today` — number of times Claude has called a mnemo MCP tool today
278
+ (resets at midnight, atomic write)
279
+ - `3⚡` — Reflex emissions today (v0.8). The bolt only appears when at
280
+ least one rule has been injected via `UserPromptSubmit` this day.
281
+
282
+ The status line is **additive**: if you already had a custom statusLine
283
+ in `~/.claude/settings.json`, mnemo wraps it instead of overwriting.
284
+ Your original output appears first, then ` · `, then mnemo's segment.
285
+ `mnemo uninstall` restores your original cleanly. If you manually edit
286
+ settings.json after `mnemo init`, `mnemo doctor` warns about the drift.
287
+
288
+ ## Runtime flags
289
+
290
+ The Capture → Present → Inject → Reflex loop is **on by default** —
291
+ `mnemo init` gives you the full product, not an inert scaffold. The
292
+ five runtime flags can be flipped to `false` in `~/mnemo/mnemo.config.json`
293
+ if you want to disable specific behaviors:
294
+
295
+ ```json
296
+ {
297
+ "extraction": {
298
+ "auto": {
299
+ "enabled": true,
300
+ "minNewMemories": 1,
301
+ "minIntervalMinutes": 60
302
+ }
303
+ },
304
+ "briefings": {
305
+ "enabled": true
306
+ },
307
+ "injection": {
308
+ "enabled": true
309
+ },
310
+ "enrichment": {
311
+ "enabled": true
312
+ },
313
+ "reflex": {
314
+ "enabled": true
315
+ }
316
+ }
317
+ ```
318
+
319
+ - **`extraction.auto.enabled`** *(default `true`)* — at every session end,
320
+ if there are at least `minNewMemories` new files (default 1) and at
321
+ least `minIntervalMinutes` since the last run (default 60), spawn a
322
+ detached background extraction. The hook returns in <100ms; extraction
323
+ runs asynchronously. Check progress via `mnemo status`, diagnose with
324
+ `mnemo doctor`.
325
+ - **`briefings.enabled`** *(default `true`)* — at every session end,
326
+ generate a per-session briefing into `bots/<repo>/briefings/sessions/`.
327
+ Briefings feed the next extraction run as dense input.
328
+ - **`injection.enabled`** *(default `true`, v0.5)* — at every session
329
+ start, emit the MCP topic list into Claude's `additionalContext`. The
330
+ MCP tools are always available once `mnemo init` has run; this flag
331
+ controls only whether Claude is *told about* the topics at session start.
332
+ - **`enrichment.enabled`** *(default `true`, v0.5)* — when Claude is about
333
+ to `Edit`/`Write`/`MultiEdit` a file matching a rule's `path_globs`, the
334
+ PreToolUse hook injects the rule body as additional context before the
335
+ tool runs. See "Rule activation" below for details.
336
+ - **`reflex.enabled`** *(default `true`, v0.8)* — on every user prompt,
337
+ the `UserPromptSubmit` hook runs BM25F retrieval over the vault-wide
338
+ reflex index and injects the single highest-confidence rule preview
339
+ inline (triple-gate: overlap ≥ 2, relative gap ≥ 1.5×, absolute floor
340
+ ≥ 2.0). Tune `reflex.thresholds.*`, `reflex.bm25f.fieldWeights`, and
341
+ `reflex.maxEmissionsPerSession` in `mnemo.config.json`. See "Prompt
342
+ Reflex" below.
343
+
344
+ ## Rule activation *(v0.5)*
345
+
346
+ The three flags above tell Claude *that rules exist* at session start. **Rule
347
+ activation** makes mnemo push a rule directly into Claude's turn at the exact
348
+ moment it's about to run a tool — not just once per session. Two modes share
349
+ the same per-project index and the same `PreToolUse` hook:
350
+
351
+ - **Enforcement** — when Claude is about to run a `Bash` command that
352
+ matches a `deny_pattern` regex or a `deny_command` prefix from any rule
353
+ owned by the current project, the hook emits `permissionDecision: deny`
354
+ and Claude Code blocks the call with the rule's `reason` string visible to
355
+ the model. Use for hard guardrails: "never commit with Co-Authored-By",
356
+ "never run `drizzle-kit push`".
357
+ - **Enrichment** — when Claude is about to run `Edit`, `Write`, or
358
+ `MultiEdit` on a file path that matches one of a rule's `path_globs`, the
359
+ hook emits `additionalContext` containing the rule body preview (up to 3
360
+ matching rules, ordered by source count). Claude sees the text as a
361
+ `<system-reminder>` *before* performing the edit. Use for advisory rules
362
+ with file-level scope: "HeroUI v3 modals use the Drawer slot pattern",
363
+ "React key remount is required for these components".
364
+
365
+ Both modes are **strictly per-project** — a rule captured while working on
366
+ project A never fires while working on project B. Project ownership is
367
+ derived from the `sources:` frontmatter field (e.g. `bots/sg-imports/...`
368
+ belongs to `sg-imports`). Cross-project rules self-heal via repeated
369
+ capture.
370
+
371
+ ### Defaults and kill switch
372
+
373
+ - **`enforcement.enabled`** defaults to **`true`**. This is safe because
374
+ enforcement is *inert until you own a rule with an `enforce:` block* — a
375
+ fresh vault has zero such rules, so the hook fires but matches nothing.
376
+ Rules gain activation metadata either by hand-editing the frontmatter or
377
+ when the extraction LLM emits it for a high-confidence rule (see "Rule
378
+ frontmatter shape" below).
379
+ - **`enrichment.enabled`** defaults to **`true`** during the dogfood
380
+ phase. Enrichment surfaces context every time you edit a file matching a
381
+ rule's `path_globs`. If you want to silence it temporarily while iterating
382
+ on rules, set to `false` in `mnemo.config.json`.
383
+
384
+ To disable enforcement (kill switch), add to `~/mnemo/mnemo.config.json`:
385
+
386
+ ```json
387
+ {
388
+ "enforcement": { "enabled": false }
389
+ }
390
+ ```
391
+
392
+ The `PreToolUse` hook is **absolutely fail-open**: any error at any stage
393
+ (missing index, corrupt JSON, exception in match logic) returns exit code 0
394
+ with empty stdout. The hook can never block Claude Code from running. You
395
+ can trust it not to brick your session even if mnemo itself is broken.
396
+
397
+ ### Rule frontmatter shape
398
+
399
+ Both blocks are optional and live in a feedback page's YAML frontmatter:
400
+
401
+ ```yaml
402
+ ---
403
+ name: no-co-authored-by-in-commits
404
+ description: Never add Co-Authored-By trailers in git commits
405
+ type: feedback
406
+ stability: stable
407
+ sources:
408
+ - bots/mnemo/memory/feedback_no_coauthored.md
409
+ tags:
410
+ - git
411
+ - workflow
412
+ aliases:
413
+ - coauthor
414
+ - trailer
415
+ - commit-footer
416
+ enforce:
417
+ tool: Bash
418
+ deny_pattern: git commit.*Co-Authored-By
419
+ reason: No Co-Authored-By trailers in commits
420
+ activates_on:
421
+ tools: [Edit, Write, MultiEdit]
422
+ path_globs:
423
+ - '**/components/modals/**'
424
+ - '**/*modal*.tsx'
425
+ ---
426
+ ```
427
+
428
+ - `aliases` *(v0.8, optional)* — a list of 3-8 lowercase synonym tokens
429
+ used by the Reflex BM25F index (`aliases` field weight 2.5). Emit when
430
+ the rule carries domain terms a developer would naturally type in a
431
+ different language or abbreviation (PT↔EN, `db`↔`database`↔`banco`).
432
+ Omit for generic rules without natural synonyms; extraction auto-emits
433
+ aliases for high-signal rules.
434
+
435
+ - `enforce.tool` must be `"Bash"` in v0.5 (the only tool v1 supports
436
+ for hard-blocking).
437
+ - `enforce.deny_pattern` is a regex compiled with `re.IGNORECASE | re.DOTALL`
438
+ and pre-validated against ReDoS at index build time (a time-budget probe
439
+ rejects pathological patterns like `(a+)+b`).
440
+ - `enforce.deny_command` is an alternative to the regex: a list of command
441
+ prefixes; the hook normalizes the command (strips `sudo`, `env FOO=bar`,
442
+ shell inline env) before the match.
443
+ - `activates_on.tools` must be a subset of `{Edit, Write, MultiEdit}`.
444
+ - `activates_on.path_globs` supports `**` (match any number of path
445
+ segments), single `*` (no slash crossing), and bracket classes with
446
+ `[!...]` negation.
447
+
448
+ Rules tagged `needs-review` or with `stability: evolving`, and rules still
449
+ in `shared/_inbox/`, **never** become activation rules — they're gated
450
+ through the same `is_consumer_visible` predicate that the dashboard and
451
+ MCP retrieval use.
452
+
453
+ ### Observability
454
+
455
+ - `mnemo status` — shows per-project rule counts, recent denials, recent
456
+ enrichments, and the last denied command.
457
+ - `mnemo doctor` — checks for malformed `enforce:`/`activates_on:` blocks,
458
+ stale activation index, suspicious `deny_pattern` regex, and overly
459
+ broad `path_globs` (`**/*`, `*`).
460
+ - `<vault>/.mnemo/denial-log.jsonl` — JSONL stream of every deny, with
461
+ slug, reason, tool, full command (truncated to 500 chars), timestamp.
462
+ - `<vault>/.mnemo/enrichment-log.jsonl` — JSONL stream of every enrichment,
463
+ with hit slugs, tool name, file path, timestamp.
464
+ - `<vault>/.mnemo/rule-activation-index.json` — the per-project index
465
+ written by `build_index` after every extraction and on session start. If
466
+ you suspect drift, delete it; the next session rebuilds it.
467
+
468
+ ### Reflex observability *(v0.8)*
469
+
470
+ - `mnemo status` — shows whether `reflex.enabled` and today's emission count.
471
+ - `mnemo doctor` — three new checks:
472
+ - `reflex-index-stale` — flags when `reflex.enabled=true` but
473
+ `reflex-index.json` is missing (usually: never ran `mnemo extract`).
474
+ - `reflex-session-cap-hit` — scans the last 7 days of `reflex-log.jsonl`
475
+ and warns when >20% of sessions hit `session_cap_reached`. Tune
476
+ `reflex.maxEmissionsPerSession` up or raise `thresholds.absoluteFloor`.
477
+ - `reflex-bilingual-gap` — flags when 3+ rules have non-ASCII descriptions
478
+ but no `aliases:` field (probable recall loss on PT prompts).
479
+ - `<vault>/.mnemo/reflex-index.json` — the vault-wide BM25F index, rebuilt
480
+ on every `SessionStart`. Schema v1; `load_index` returns `None` on
481
+ version mismatch so the hook silences cleanly if you downgrade.
482
+ - `<vault>/.mnemo/reflex-log.jsonl` — per-prompt telemetry: session id,
483
+ project, SHA256-12 prompt hash, scores, emitted slugs, silence reason.
484
+ Never contains raw prompt text. Rotates at 1 MiB.
485
+
486
+ ## Commands
487
+
488
+ ```
489
+ npx @xyrlan/mnemo install one-command setup (recommended)
490
+ npx @xyrlan/mnemo uninstall remove mnemo (vault preserved)
491
+ mnemo init first-run setup if installed via pip directly
492
+ mnemo init --project scope the install to <cwd> only (v0.12+)
493
+ mnemo status [--scope ...] vault state + hook health + auto-brain state
494
+ (--scope project|global|all, default all)
495
+ mnemo doctor full diagnostic with actionable fixes
496
+ mnemo extract manually run the extraction pipeline (also rebuilds the dashboard)
497
+ mnemo open open vault in Obsidian / file manager
498
+ mnemo fix reset circuit breaker
499
+ mnemo uninstall [--project] remove hooks, MCP server, status line (vault preserved)
500
+ mnemo help list commands
501
+ ```
502
+
503
+ ## Where things live
504
+
505
+ ```
506
+ ~/mnemo/ your vault
507
+ ├── HOME.md dashboard at the top, your notes below
508
+ ├── bots/<repo-name>/
509
+ │ ├── logs/YYYY-MM-DD.md session start/end markers
510
+ │ ├── memory/ mirror of Claude memories for this repo
511
+ │ └── briefings/sessions/ per-session shift handoffs (opt-in)
512
+ ├── shared/
513
+ │ ├── feedback/ auto-promoted preference rules
514
+ │ ├── user/ user-profile facts
515
+ │ ├── reference/ pointers to external systems
516
+ │ ├── project/ per-repo project context
517
+ │ └── _inbox/<type>/ drafts awaiting review
518
+ └── .mnemo/
519
+ ├── extract.lock background extraction lock
520
+ ├── last-auto-run.json background extraction telemetry
521
+ ├── mcp-call-counter.json daily MCP tool call counter + reflex cache (v0.5/v0.8)
522
+ ├── mcp-access-log.jsonl per-call MCP telemetry (v0.5.3, local only)
523
+ ├── reflex-index.json vault-wide BM25F index for Prompt Reflex (v0.8)
524
+ ├── reflex-log.jsonl per-prompt Reflex telemetry (v0.8, local only)
525
+ └── statusline-original.json preserved user statusLine (v0.5)
526
+
527
+ ~/.claude/settings.json hooks + status line composer
528
+ ~/.claude.json MCP server registration
529
+ ```
530
+
531
+ See [docs/getting-started.md](docs/getting-started.md) for a deeper tour.
532
+
533
+ ## Privacy
534
+
535
+ 100% local. Zero network. No third-party Python packages
536
+ (`pyproject.toml` declares `dependencies = []` as a load-bearing
537
+ architectural choice). MCP access telemetry (`.mnemo/mcp-access-log.jsonl`)
538
+ stays on disk — nothing leaves your machine. Read the [source](src/mnemo).
539
+
540
+ ## License
541
+
542
+ MIT — see [LICENSE](LICENSE).