mind-palace-graph 0.3.0

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 (51) hide show
  1. package/INSTALL.md +387 -0
  2. package/README.md +602 -0
  3. package/dist/api.d.ts +682 -0
  4. package/dist/api.js +660 -0
  5. package/dist/api.js.map +1 -0
  6. package/dist/cli.d.ts +95 -0
  7. package/dist/cli.js +856 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/format.d.ts +16 -0
  10. package/dist/format.js +199 -0
  11. package/dist/format.js.map +1 -0
  12. package/dist/fuzzy.d.ts +45 -0
  13. package/dist/fuzzy.js +150 -0
  14. package/dist/fuzzy.js.map +1 -0
  15. package/dist/index.d.ts +9 -0
  16. package/dist/index.js +528 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/mcp-server.d.ts +24 -0
  19. package/dist/mcp-server.js +187 -0
  20. package/dist/mcp-server.js.map +1 -0
  21. package/dist/mind-palace.d.ts +148 -0
  22. package/dist/mind-palace.js +780 -0
  23. package/dist/mind-palace.js.map +1 -0
  24. package/dist/nodes.d.ts +57 -0
  25. package/dist/nodes.js +220 -0
  26. package/dist/nodes.js.map +1 -0
  27. package/dist/pagination.d.ts +41 -0
  28. package/dist/pagination.js +63 -0
  29. package/dist/pagination.js.map +1 -0
  30. package/dist/palace-format.d.ts +30 -0
  31. package/dist/palace-format.js +146 -0
  32. package/dist/palace-format.js.map +1 -0
  33. package/dist/rg.d.ts +34 -0
  34. package/dist/rg.js +288 -0
  35. package/dist/rg.js.map +1 -0
  36. package/dist/sources.d.ts +87 -0
  37. package/dist/sources.js +457 -0
  38. package/dist/sources.js.map +1 -0
  39. package/dist/tokens.d.ts +35 -0
  40. package/dist/tokens.js +95 -0
  41. package/dist/tokens.js.map +1 -0
  42. package/dist/types.d.ts +236 -0
  43. package/dist/types.js +8 -0
  44. package/dist/types.js.map +1 -0
  45. package/package.json +67 -0
  46. package/skills/mpg-context/SKILL.md +556 -0
  47. package/skills/mpg-context/references/anti-patterns.md +133 -0
  48. package/skills/mpg-context/references/integration.md +123 -0
  49. package/skills/mpg-context/references/mind-palace.md +217 -0
  50. package/skills/mpg-context/references/multi-agent.md +147 -0
  51. package/skills/mpg-context/references/sources.md +120 -0
@@ -0,0 +1,556 @@
1
+ ---
2
+ name: mpg-context
3
+ description: >
4
+ Token-budgeted codebase search with composable short-term memory.
5
+ Search files, command output, and URLs for regex patterns; results
6
+ return as context nodes sized in tokens (not lines) with file:line
7
+ attribution. Persistent "mind palace" of named stashes can be
8
+ composed, intersected, linked into a graph, pruned by age/tag/count,
9
+ and traversed. Three integration paths: MCP server (Claude Desktop,
10
+ Claude Code, Cline, Windsurf), CLI shell-out (any agent), and
11
+ programmatic import (Anthropic / Google SDKs).
12
+ Use for codebase exploration, multi-step investigation, finding
13
+ references, and building cross-invocation working memory.
14
+ DO NOT use mpg for: a single known file path (use Read); a single
15
+ symbol grep on a small tree where you expect ≤ ~30 hits (use Grep);
16
+ files under ~200 lines you'll fully consume (the budgeting overhead
17
+ is only worth it for >1KB result sets or persistent recall). The
18
+ decision tree under "When NOT to use mpg" makes the cutoff concrete.
19
+ tools:
20
+ - mpg_search
21
+ - mpg_stash
22
+ - mpg_list_stashes
23
+ - mpg_get_stash
24
+ - mpg_drop_stash
25
+ install:
26
+ npm: npm install -g mpg-cli
27
+ source: git clone https://github.com/JadeZaher/mind-palace-graph && cd mind-palace-graph && npm install && npm run build && npm link
28
+ verify: mpg --version
29
+ ---
30
+
31
+ # mpg-context — Codebase Context Retrieval Skill
32
+
33
+ ## The lens mental model
34
+
35
+ mpg is a single **lens over the corpus**, not a separate tool you reach
36
+ for after grep and read. There are no boundaries between files — you
37
+ dial the lens to fit the task:
38
+
39
+ - **Focal points** = matches (set by the pattern + sort + fuzzy).
40
+ - **Depth at each focal point** = the window (`effort` / `clip_chars` /
41
+ `before` / `after` / `window_curve`).
42
+ - **Surface** = where the lens looks (`in`, `from`, `compose`, `page`).
43
+
44
+ With the right flags, one `mpg_search` call replaces what would
45
+ otherwise be 1–N `grep` + `read` combos:
46
+
47
+ | Job | Lens setting |
48
+ | :--- | :--- |
49
+ | "List file:line hits, like grep" | `effort: "scan", clip_chars: 30` (3.2× cheaper than rg on bench corpora) |
50
+ | "Read this one file for what it says about X" | `in: ["file.md"], effort: "deep"` |
51
+ | "Browse recent memory for X" | `effort: "scan", sort: "recent", page: 1, page_size: 10` |
52
+ | "Compact a topic to N tokens" | `effort: "scan", clip_chars: 30, max_tokens: N` |
53
+ | "Catch a typo'd term" | `fuzzy: true` |
54
+ | "Search only files I already touched" | `from: "<stash-name>"` |
55
+
56
+ The mind palace (`mpg_stash`) is just persistent state for the lens:
57
+ stash a result and the next search can be scoped to those files
58
+ across the entire corpus without re-scanning.
59
+
60
+ ## When NOT to use mpg
61
+
62
+ mpg has a real startup cost (~200ms cold) and the budgeting machinery
63
+ only pays off above a certain payload size. Use the wrong tool for the
64
+ job and you pay overhead for no win. The cutoffs in plain terms:
65
+
66
+ ```
67
+ mpg vs alternative?
68
+ ├─ Known file path, you want to read it → host's Read tool
69
+ ├─ Single symbol grep, ≤ ~30 hits expected → host's Grep / rg
70
+ ├─ One file < 200 lines, you'll read it all → Read it directly
71
+ ├─ One-word "does X exist?" answer → rg / grep is cheaper
72
+ ├─ Multi-file scan, results > ~1KB total → mpg_search
73
+ ├─ Need persistent recall across turns → mpg_search + mpg_stash
74
+ ├─ Cross-cutting investigation, 2+ threads → mpg_search --mp-compose
75
+ ├─ Already stashed the file set you want → --mp-from (skip re-scan)
76
+ └─ Opaque tool output > ~3KB you want to filter → mpg --cmd or --url
77
+ ```
78
+
79
+ Hard rule: **if you can name the file and you're going to consume the
80
+ whole thing, do not route through mpg**. The token budgeter assumes you
81
+ want a *subset* of a *larger* payload. When the payload is small, the
82
+ overhead (~200ms cold start, plus token-counting work) buys nothing.
83
+
84
+ ## Palace lifecycle (one cycle = one task)
85
+
86
+ The palace is **working memory**, not an archive. Plan stash usage along
87
+ this cycle — it's the difference between "I have 47 stashes and no idea
88
+ which ones matter" and "I have 8 stashes that map to the open threads."
89
+
90
+ ```
91
+ ┌─────────────────────────────────────┐
92
+ ↓ │
93
+ CAPTURE → TAG → LINK → REUSE → PRUNE → CLOSE
94
+ (--mp-stash) (--mp-link) (--mp-from) (--mp-prune-*)
95
+ (--mp-compose)
96
+ ```
97
+
98
+ | Verb | When | Flag |
99
+ | :--- | :--- | :--- |
100
+ | **Capture** | Anything you might reference more than once. Cheap. | `--mp-stash <name> <note> --mp-ttl 4h` |
101
+ | **Tag** | At capture time. Repeat the flag for multiple tags. | `--mp-stash-tag scan --mp-stash-tag topic` (NOT comma-separated — that becomes one literal tag named `scan,topic`) |
102
+ | **Link** | The moment you notice a relationship — three sessions later you won't remember why two stashes mattered together. | `--mp-link <from> <to> <type> [note]` |
103
+ | **Reuse** | Re-scope a deeper search to a stash's file list. 3× cheaper than re-scanning the whole tree. | `--mp-from <name>` (one), `--mp-compose <a> <b>` (union), `--mp-intersect` (both) |
104
+ | **Prune** | At session open AND between major phases. The palace should shrink between sessions, not grow. | `--mp-prune-expired` (free), `--mp-prune-tag scan` (drop scratch), `--mp-prune-keep 20` (cap) |
105
+ | **Close** | At session close, drop scratch tags; keep findings. | `--mp-prune-tag scan` |
106
+
107
+ Three rules of thumb for the lifecycle:
108
+
109
+ 1. **Always TTL scratch.** `--mp-ttl 4h` on exploratory scans, `--mp-ttl 24h` on findings, no TTL on canonical context. Auto-prune on `--mp-list`/`--mp-get` will clean up the rest.
110
+ 2. **One palace per task.** Set `MPG_MIND_PALACE=.mpg/<task>.json` or pass `--mp-path` per invocation. Today there is one *active* palace per call — there is no cross-palace federation; isolation is the path you point at. Don't mix unrelated tasks in one palace.
111
+ 3. **`--mp-list` is the palace overview; `--mp-get` is the per-stash card view; `--mp-get --with-nodes` is the full read.** `--mp-list` is one line of metadata per stash — best for "what's in my palace?". `--mp-get <name>` is the **card view by default**: note, tags, relations, sources, and counts (5–6× cheaper than the old full dump — the synthesized intel without the captured bodies). Add `--with-nodes` (or its synonym `--full`) only when you actually need the captured node context. Pagination only applies in `--with-nodes` mode; use `--page 1 --page-size 5` when a stash is large.
112
+
113
+ ## Quick start
114
+
115
+ ```bash
116
+ npm install -g mpg-cli && mpg --version
117
+ ```
118
+
119
+ If running through an MCP-capable host (Claude Desktop, Claude Code,
120
+ Cline, Windsurf, Continue.dev), the five tools below register
121
+ automatically once the `mpg` MCP server is configured.
122
+
123
+ ## When to use
124
+
125
+ The lens table above is the short answer. Below is the same advice
126
+ indexed by situation.
127
+
128
+ | Situation | Tool |
129
+ | :--- | :--- |
130
+ | "Browse my recent memory — what just changed about X?" | `mpg_search` (`effort: "scan", sort: "recent", clip_chars: 30`) |
131
+ | "Where is X referenced?" | `mpg_search` (`effort: "scan", clip_chars: 30`) — cheapest hit list |
132
+ | "Need context around a match" | `mpg_search` (`effort: "quick"`) — small windows around top 10 hits |
133
+ | "Deep context for one targeted answer" | `mpg_search` (`effort: "deep"`) — 100 nodes × 2k tokens each |
134
+ | "User typed a typo — find anyway" | `mpg_search` (`fuzzy: true`) — edit distance ≤ 2 |
135
+ | "Compact a topic into a token budget" | `mpg_search` (`effort: "scan", clip_chars: 30, max_tokens: N`) |
136
+ | "I'll need these hits again later" | `mpg_stash` |
137
+ | "Search only files I previously stashed" | `mpg_search` (`from` or `compose`) |
138
+ | "What stashes do I have?" | `mpg_list_stashes` |
139
+ | "Forget this stash" | `mpg_drop_stash` |
140
+ | "Just read one file" | use the host's read tool — mpg is for *searching* |
141
+ | "Search a URL or command output" | `mpg_search` (`url` or `cmd`) — see `references/sources.md` |
142
+
143
+ ## Effort presets and shape knobs
144
+
145
+ | effort | before | after | max_nodes | use case |
146
+ | :--- | ---: | ---: | ---: | :--- |
147
+ | **scan** | 20 | 20 | uncapped | **Index mode.** Every hit gets a tiny disambiguating window. Recall AND precision match rg regardless of hit count; tokens scale O(hits). Combine with `clip_chars: 30` and `sort: "recent"` for the cheapest first-touch index. |
148
+ | **quick** | 200 | 200 | 10 | **DEFAULT.** Small windows, small cap. First touch of a topic. |
149
+ | normal | 500 | 500 | 30 | Bump when quick was ambiguous. |
150
+ | deep | 2000 | 2000 | 100 | Final answer grounding — for one targeted query you commit to. |
151
+
152
+ Shape knobs (mix with any effort):
153
+
154
+ | flag | what it does | when it wins |
155
+ | :--- | :--- | :--- |
156
+ | `clip_chars: N` | Sub-line snippet around the matched span (N chars each side, ellipsis-marked). Drops per-line context entirely. | **Memory-corpus literal recall**: scan + clip_chars=30 is 3.2× cheaper than ripgrep (377 vs 1197 tokens) at 100% recall + 100% precision on the same corpus. |
157
+ | `fuzzy: true` | Trigram-union driver + Levenshtein post-filter (edit distance ≤ 2). Handles drop / insert / substitute / swap typos. | **Typo recovery**: 100% recall on typo'd input vs rg's 0%. 12× cheaper than per-file embeddings. |
158
+ | `sort: "recent"` | Order returned nodes by source-file mtime, newest first. | **Time-ordered memory index**: combine with scan and pagination to browse "what just changed" first; dig deeper into history on demand. |
159
+ | `window_curve: "log"` | Per-node window decays as `full / log2(rank+2)`. Rank 0 keeps full context; later ranks shrink. | **Recency-weighted context**: saves ~53% tokens vs flat windows while keeping the top hit's full context. Pairs with sort:recent. |
160
+
161
+ ### Recommended patterns (bench-driven)
162
+
163
+ **1. Cheapest first-touch index — "browse my memory"**
164
+
165
+ ```ts
166
+ mpg_search({
167
+ pattern: "JWT Bearer", // or any concept keyword
168
+ in: ["./conductor/tracks"],
169
+ effort: "scan",
170
+ clip_chars: 30,
171
+ sort: "recent",
172
+ page: 1,
173
+ page_size: 10
174
+ })
175
+ ```
176
+
177
+ What you get: file:line hits with a 30-char snippet on each side, sorted by recency, paginated. Empirically **3.2× cheaper than rg** (377 vs 1197 tokens) at 100/100 recall/precision on memory-system content. Skip to deeper modes only when this isn't enough.
178
+
179
+ **2. Typo-tolerant search**
180
+
181
+ ```ts
182
+ mpg_search({ pattern: "PrvderiContext", in: [...], fuzzy: true })
183
+ ```
184
+
185
+ `fuzzy: true` catches all four common typo modes (drop / insert / substitute / swap). Use when the user's input is uncertain.
186
+
187
+ **3. Scan → stash → drill-down across turns**
188
+
189
+ ```ts
190
+ // Turn 1: scan + stash
191
+ const scan = await mpg_search({ pattern: "...", effort: "scan", clip_chars: 30, sort: "recent" });
192
+ await mpg_stash({ name: "topic-index", note: "...", tags: ["topic"] });
193
+
194
+ // Turn 2+: drill into ONE file from the index
195
+ await mpg_search({ pattern: "...", effort: "normal", from: "topic-index" });
196
+ ```
197
+
198
+ The mind palace makes the index addressable across turns. Re-scoping to a stash is cheaper than re-searching the whole tree.
199
+
200
+ **4. Compaction at zero LLM cost**
201
+
202
+ ```ts
203
+ // Produces a topic-focused compaction in one tool call.
204
+ mpg_search({
205
+ pattern: "auth|JWT|Bearer|ProviderContext", // OR your topic keywords
206
+ in: ["..."],
207
+ effort: "scan",
208
+ clip_chars: 30,
209
+ sort: "recent",
210
+ window_curve: "log",
211
+ max_tokens: 2000 // hard cap the compaction size
212
+ })
213
+ ```
214
+
215
+ On the compaction bench, this single CLI call beats LLM-driven summarization (67% pass vs 33%) at **zero LLM input tokens**. Use when you'd otherwise spend a 50k-token summarization round-trip.
216
+
217
+ **5. Whole-repo scan ("does X appear anywhere?")**
218
+
219
+ ```ts
220
+ mpg_search({ pattern: "ProviderContext", in: ["."], effort: "scan", clip_chars: 20 })
221
+ ```
222
+
223
+ Pass directories — including `.` — directly. The dir spec goes straight to ripgrep's parallel ignore-aware walk, so a full-repo scan is comparable to running `rg` itself rather than fanning out per file. Don't pre-expand to a file list in your harness; that's strictly slower.
224
+
225
+ **6. Cross-cutting set-ops on stashed evidence**
226
+
227
+ ```bash
228
+ # union: files matching either thread
229
+ mpg_search({ pattern: "Redis", compose: ["rate-limit-impl", "rate-limit-docs"] })
230
+ # intersection: files mentioned in BOTH threads (CLI only)
231
+ mpg --mp-intersect rate-limit-impl rate-limit-docs
232
+ # subtraction: in A but not in B (CLI only)
233
+ mpg --mp-except rate-limit-impl --mp-except-name rate-limit-archived
234
+ ```
235
+
236
+ Use this instead of "let me list both stashes and diff in my head." The set operations are O(stash count), not O(corpus).
237
+
238
+ **7. Filter opaque tool output / web pages without reading the whole body**
239
+
240
+ The job mpg does best is **token-budgeting a payload you don't want
241
+ to read in full**. WebFetch on a long doc page, `gh pr view --json`,
242
+ `kubectl describe`, `terraform plan`, `npm ls`, a CI log — these are
243
+ all the same shape: many KB of mostly-irrelevant text wrapping the few
244
+ lines that actually answer the question. Route the source through
245
+ `mpg --cmd "..."` or `mpg --url "..."` instead of dumping the full
246
+ body into context.
247
+
248
+ ```ts
249
+ // WebFetch a doc page, only see the auth section
250
+ mpg_search({
251
+ pattern: "authentication|auth|token",
252
+ url: "https://example.com/api/docs",
253
+ effort: "scan", clip_chars: 50, max_tokens: 1500
254
+ })
255
+
256
+ // Pull just the failing tests from a verbose CI log
257
+ mpg_search({
258
+ pattern: "FAIL|✗|error TS",
259
+ cmd: "gh run view --log 12345",
260
+ effort: "scan", clip_chars: 80, max_tokens: 2000
261
+ })
262
+
263
+ // Filter `kubectl describe` to just events / errors
264
+ mpg_search({
265
+ pattern: "Warning|Error|Failed",
266
+ cmd: "kubectl describe pod my-pod",
267
+ effort: "scan", clip_chars: 100
268
+ })
269
+ ```
270
+
271
+ Three rules of thumb:
272
+
273
+ 1. **If a tool output is >3 KB and you only care about 1–2 patterns,
274
+ route it through mpg.** The wins compound — every avoided
275
+ full-body read is tokens you can spend on reasoning.
276
+ 2. **Stash the filtered result if you'll reference it again.**
277
+ Filtered tool output is often the cheapest stash you'll ever make.
278
+ Next turn's `--mp-get` is free.
279
+ 3. **Set `max_tokens` explicitly when filtering opaque payloads.**
280
+ Tool output can spike (thousands of ERROR lines in a log) and the
281
+ default node caps are sized for code, not for runaway streams.
282
+
283
+ Hard caps you can rely on so a hostile or runaway source can't drain
284
+ context: `url` is capped at 16 MB and 30 s with a content-type guard;
285
+ `cmd` is capped at 64 MB and 60 s. Past those, mpg returns truncated
286
+ output with a marker — not a hung agent.
287
+
288
+ **8. `window_curve` — token sculpting across ranked results**
289
+
290
+ `window_curve` decides how aggressively per-node windows shrink as you
291
+ go down the ranked list. It's the single biggest lever for making one
292
+ search call match the *shape* of how an LLM actually consumes results:
293
+ the first hit usually needs full context, the tenth probably just
294
+ needs a line and a half. Saving tokens past the first few ranks frees
295
+ budget for more nodes overall.
296
+
297
+ Three curves, each with a specific use case:
298
+
299
+ | Curve | Shape | Use when |
300
+ | :--- | :--- | :--- |
301
+ | `flat` (default) | Every node gets the full `before`/`after` window. | You need uniform context across all hits. Fine when `max_nodes` is tight (≤5). |
302
+ | `linear` | Window shrinks linearly from 100% at rank 0 to ~10% at the last rank. | **"What just changed?" browsing.** Pair with `sort: "recent"` — newest file gets full context, older files get a one-liner each. Roughly 40-50% token savings vs `flat` at the same `max_nodes`. |
303
+ | `log` | Window decays as `full / log2(rank + 2)`. Gentler — rank 5 keeps ~38% of full context, not ~50% like linear. | **Multi-hit synthesis.** When ranks 2–10 still carry signal you'd hate to truncate. Used by the compaction pattern. ~53% token savings vs flat. |
304
+
305
+ Concrete invocations:
306
+
307
+ ```ts
308
+ // "What changed in auth recently?" — newest file gets a full read,
309
+ // older ones get disambiguating snippets.
310
+ mpg_search({
311
+ pattern: "session|token|auth",
312
+ in: ["src/auth/"],
313
+ effort: "deep",
314
+ sort: "recent",
315
+ window_curve: "linear",
316
+ })
317
+
318
+ // Compaction — the canonical 0-LLM-cost summary pattern. log curve
319
+ // keeps the top 3 hits substantial while letting the long tail shrink.
320
+ mpg_search({
321
+ pattern: "auth|JWT|Bearer|ProviderContext",
322
+ in: ["."],
323
+ effort: "scan",
324
+ clip_chars: 30,
325
+ sort: "recent",
326
+ window_curve: "log",
327
+ max_tokens: 2000,
328
+ })
329
+
330
+ // Use flat when you genuinely need all hits with equal weight (e.g.
331
+ // reviewing every TODO before a release).
332
+ mpg_search({
333
+ pattern: "TODO|FIXME",
334
+ in: ["src/"],
335
+ effort: "normal",
336
+ window_curve: "flat", // default; named for clarity
337
+ })
338
+ ```
339
+
340
+ Rule of thumb: if you're using `sort: "recent"` or `"oldest"`, you
341
+ almost always want a non-flat curve. Flat + sort wastes tokens on the
342
+ ranks that already lost the prioritization battle. The two go
343
+ together.
344
+
345
+ **9. Linked palace nodes — building a graph of investigation threads**
346
+
347
+ Stashes are powerful by themselves, but the real win on a multi-day
348
+ investigation is when you can **traverse between them by intent**, not
349
+ by remembering names. `--mp-link` makes a directed edge between two
350
+ stashes; `--mp-related` lists everything connected to a stash;
351
+ `--mp-graph` walks the graph N levels deep.
352
+
353
+ When to link (and what edge types to use):
354
+
355
+ | Edge type | Meaning | Example |
356
+ | :--- | :--- | :--- |
357
+ | `depends-on` | "B is a precondition for understanding A" | `mpg --mp-link auth-rewrite db-schema depends-on "new tables back the migration"` |
358
+ | `supersedes` | "A is the current view; B is the old one" | `mpg --mp-link auth-v2 auth-v1 supersedes "post-rewrite, ignore v1"` |
359
+ | `see-also` | "B is a related thread you'll want when reading A" | `mpg --mp-link rate-limit-impl rate-limit-docs see-also "implementation ↔ design"` |
360
+ | `parent-of` / `child-of` | Subtopics of a larger investigation | `mpg --mp-link epic-payments stripe-webhooks parent-of "webhook handling is part of payments"` |
361
+ | `blocks` | "A can't ship without resolving B" | `mpg --mp-link release-v3 schema-migration blocks "must run migration first"` |
362
+ | `contradicts` | "A and B disagree — needs reconciliation" | `mpg --mp-link spec-claim impl-reality contradicts "spec says X, code does Y"` |
363
+
364
+ Edge types are conventional strings, not enforced — pick any
365
+ vocabulary, but stay consistent within one investigation.
366
+
367
+ Typical workflow:
368
+
369
+ ```bash
370
+ # 1. Build stashes as you investigate, with consistent tags
371
+ mpg "JWT" --in src/auth/ --mp-stash auth-jwt --mp-tag rewrite --mp-ttl 24h
372
+ mpg "JWT" --in docs/spec/ --mp-stash spec-jwt --mp-tag rewrite --mp-ttl 24h
373
+ mpg "JWT" --in src/legacy/ --mp-stash legacy-jwt --mp-tag rewrite --mp-ttl 24h
374
+
375
+ # 2. Link them as you discover relationships
376
+ mpg --mp-link auth-jwt spec-jwt see-also "implementation of the spec"
377
+ mpg --mp-link auth-jwt legacy-jwt supersedes "post-rewrite, legacy goes away"
378
+
379
+ # 3. Next session: navigate by intent, not by name
380
+ mpg --mp-related auth-jwt # show neighbors + edge labels
381
+ mpg --mp-graph auth-jwt 2 # BFS two hops out
382
+ ```
383
+
384
+ Rules of thumb:
385
+
386
+ 1. **Only link what you'll traverse.** Edges are cheap, but a graph
387
+ nobody walks is just noise on `--mp-related`. If you wouldn't run
388
+ `--mp-graph` later, don't link.
389
+ 2. **Link when discovery is fresh.** The right time to add an edge is
390
+ the moment you notice the relationship — three sessions later you
391
+ won't remember why two stashes mattered together.
392
+ 3. **One vocabulary per investigation.** Mixing `depends-on` and
393
+ `requires` for the same concept makes traversal noisy. Pick one
394
+ and stick to it; rename via unlink/relink if you change your mind.
395
+ 4. **`--mp-graph` is your "context resurrection" tool.** When a
396
+ conversation gets compacted away and you need to rebuild the
397
+ thread, `mpg --mp-graph <root> 3` reconstructs the investigation
398
+ topology in one CLI call.
399
+ 5. **Don't link across unrelated tasks.** If you're disciplined about
400
+ one palace per task (`MPG_MIND_PALACE=.mpg/<task>.json`), this is
401
+ automatic — cross-task links can't even be expressed.
402
+
403
+ The full set of graph operations is CLI-only (not MCP yet) —
404
+ `--mp-link`, `--mp-unlink`, `--mp-related`, `--mp-graph`. See
405
+ `references/mind-palace.md` for the storage format.
406
+
407
+ **Traversal semantics worth knowing:**
408
+
409
+ - `--mp-related <name>` — shows direct neighbors in **both directions**
410
+ (inbound and outbound). Use this for a single-hop "what's connected to
411
+ X" view.
412
+ - `--mp-graph <name> [depth]` — BFS from `<name>`. Depth 1 is
413
+ **bidirectional** (both outbound edges from `<name>` and inbound edges
414
+ pointing at `<name>`). Depth ≥ 2 follows **outbound only**. A leaf-to-leaf
415
+ edge (e.g. `a → c` and `b → c` from a root that points at both `a` and
416
+ `b`) shows up exactly once via the first BFS path that reaches `c`; the
417
+ second edge is hidden by visited-deduplication.
418
+ - If you suspect a relationship the graph isn't surfacing, fall back to
419
+ `--mp-related` on the *target* of the suspected edge — that view is
420
+ always bidirectional.
421
+
422
+ ### Behavior you can rely on
423
+
424
+ These are the load-bearing guarantees worth quoting at yourself before
425
+ deciding whether to re-search vs recall:
426
+
427
+ - **Directory scans are cheap.** `--in <dir>` passes through to rg's
428
+ parallel walk. You don't need to pre-expand a dir to a file list.
429
+ - **Parallel `--mp-stash` calls don't lose data.** Two processes
430
+ stashing different findings at the same moment both land cleanly —
431
+ a tmp-file + rename plus a sibling `.lock` serialize the write step,
432
+ and a diff-based merge means each writer's intent (added X, modified
433
+ Y, removed Z) is replayed on top of fresh on-disk state.
434
+ - **`--mp-drop` persists.** When drop reports success, the entry is
435
+ gone from disk and stays gone — even if a follow-up stash from
436
+ another process commits afterwards. Treat the exit code as truth.
437
+ - **Pathological lines won't crash you.** Alternation patterns
438
+ (`(TODO|FIXME|HACK)`) over minified assets complete in tens of ms;
439
+ per-match text is hard-capped at 16 KB with a `…[clipped]` marker.
440
+ - **Result status is honest.** `partial` means some sources errored;
441
+ `result.errors[]` is structured. A quiet `no_matches` is reliable;
442
+ a quiet `partial` is not — always inspect `errors[]` before
443
+ concluding "nothing here."
444
+ - **Empty / whitespace-only fuzzy patterns throw**, instead of
445
+ silently matching every line and exploding the token budget.
446
+ - **`--json` is an alias for `--format json`** (matches `rg`, `gh`,
447
+ `jq`). Either works; `--json` is shorter.
448
+
449
+ ## The five MCP tools (signatures only)
450
+
451
+ | Tool | Required params | Optional |
452
+ | :--- | :--- | :--- |
453
+ | `mpg_search` | `pattern` | `in[]`, `cmd`, `url`, `before`, `after`, `max_nodes`, `max_tokens`, `effort` (scan/quick/normal/deep), `strategy`, `from`, `compose[]`, `page`, `page_size`, `all`, **`clip_chars`** (sub-line snippet N), **`fuzzy`** (typo-tolerant), **`sort`** ("recent"/"oldest"/"default"), **`window_curve`** ("flat"/"linear"/"log") |
454
+ | `mpg_stash` | `name` | `note`, `tags[]`, `replace` |
455
+ | `mpg_list_stashes` | — | `tag_filter[]`, `page`, `page_size` |
456
+ | `mpg_get_stash` | `name` | `with_nodes` (default false — card view; pass `true` for full nodes), `page`, `page_size` (only honored with `with_nodes: true`) |
457
+ | `mpg_drop_stash` | `name` | — |
458
+
459
+ The mind palace has a *wider* surface than the five MCP tools above
460
+ (relationships, pruning, TTL, intersect/except, isolated palaces).
461
+ Those are CLI-only today — see `references/mind-palace.md`.
462
+
463
+ ## Golden rules
464
+
465
+ 1. **Stash by default, with a TTL.** Even if you think you won't reuse it. Stashes are cheap; an unbounded palace is not. `--mp-ttl 4h` on scratch, `--mp-ttl 24h` on findings, no TTL on canonical context.
466
+ 2. **Tag every stash.** `auth`, `p0`, `scan`, `finding`, `review` — pays off at >10 stashes and makes `--mp-prune-tag` trivial.
467
+ 3. **Compose before concluding.** Set-union across two stashes catches cross-cutting evidence one search would miss. Re-reading is ~3× more expensive than `--mp-from` over the same files.
468
+ 4. **Prune actively, not eventually.** Run `--mp-prune-expired` at session start and `--mp-prune-tag scan` (or `--mp-prune-keep 30`) between major phases. Always `--mp-prune-dry-run` first.
469
+ 5. **One palace per task.** Pass `--mp-path` or set `MPG_MIND_PALACE` per task to keep contexts isolated. Cross-task palaces bleed and waste tokens.
470
+ 6. **Page large results.** Pass `page: 1, page_size: 5` for searches with >10 expected hits; check `pagination.has_next`.
471
+ 7. **Read `errors[]` even when `status === "ok"`.** A `"partial"` status means some sources errored — the result is real but incomplete. Decide if the missing sources mattered before concluding.
472
+
473
+ ## Pagination pattern
474
+
475
+ ```
476
+ Start: mpg_search(pattern, page: 1, page_size: 5)
477
+ Check: result.pagination.has_next
478
+ If yes: mpg_search(pattern, page: 2, page_size: 5)
479
+ Stop when has_next is false or you have enough context.
480
+ ```
481
+
482
+ Same pattern for `mpg_list_stashes` and `mpg_get_stash`.
483
+
484
+ ## Error recovery
485
+
486
+ | Condition | What to do |
487
+ | :--- | :--- |
488
+ | `status: "no_matches"` | Broaden pattern, drop `-w`, add `-I` (case-insensitive). |
489
+ | `status: "truncated"` | Hit `--max-tokens`. Narrow pattern OR increase budget. If you stash truncated results, the stash inherits the same partial node set — the search-level truncation marker is the only signal, and the `mpg: created stash …` line does not restate it. When stashing for archival purposes, drop `--max-tokens` or raise it well above the expected hit count so the stash is complete. |
490
+ | `status: "partial"` | Some sources errored, others returned matches. Inspect `result.errors[]` — decide whether the missing sources mattered. Common cause: a single pathological file (minified asset) that you can `--exclude`. |
491
+ | `status: "error"` | All sources errored. Check `result.errors[]` and stderr. Common: unknown stash name (`mpg_list_stashes`), rg not installed, bad regex. |
492
+ | Unknown stash | Run `mpg_list_stashes` to discover. |
493
+ | `pagination.has_next` | More data exists. Decide if current page is enough. |
494
+ | `--mp-from` returns nothing | Stashed files may have moved or been deleted. Re-stash fresh. |
495
+ | `WARNING — mind palace is corrupt` on stderr | mpg copied the bad file aside as `<palace>.corrupt.<ts>` and is refusing to save. Inspect the backup, then either fix it by hand or set `MPG_FORCE_RESET=1` to start fresh. **Do not** plow on without reading the backup — you will lose every stash. |
496
+
497
+ ## Long-context backoff workflow
498
+
499
+ This is the part that's underused. The mind palace is not just
500
+ storage; it's a **token budget** for the conversation. Treat it
501
+ like working memory with a budget, and prune actively.
502
+
503
+ The shape of a long-horizon task:
504
+
505
+ | Phase | What to do |
506
+ | :--- | :--- |
507
+ | **Open** | At session start, `mpg --mp-prune-expired` to clear stashes whose TTL passed. Optional `--mp-prune-older-than 24h` if the previous session left scratch. |
508
+ | **Explore** | Use `effort: "scan", clip_chars: 30, sort: "recent"` to build a cheap first-touch index. **Always** stash these scan results with a TTL: `--mp-ttl 4h`, tags like `scan` and the topic. The TTL is the auto-cleanup; without it, scan-stashes accumulate. |
509
+ | **Drill** | `--mp-from <scan-stash>` to re-scope a deeper search to just the files the scan flagged. Stash these too, but with a longer TTL (`--mp-ttl 24h`) and an unambiguous topic tag — these are findings, not scratch. |
510
+ | **Synthesize** | Before answering, `--mp-compose <finding-a> <finding-b>` to feed the union back as the search target. Saves you re-reading the source — the stashed nodes are already token-budgeted. For "what's the intersection of two threads?" use `--mp-intersect`. |
511
+ | **Close** | `--mp-prune-tag scan` (or whatever scratch tag you used) to drop exploratory clutter. Keep findings. The palace should shrink between sessions, not grow. |
512
+
513
+ Concrete budget targets that work:
514
+
515
+ - ≤20 active stashes in a single palace before retrieval becomes
516
+ noisy.
517
+ - TTLs that match the work cadence: `4h` for scan-and-discard scratch,
518
+ `24h` for findings you might revisit tomorrow, no TTL for
519
+ cross-session canonical context.
520
+ - One palace per major task (`MPG_MIND_PALACE=.mpg/<task-id>.json`).
521
+ Don't mix unrelated tasks — context bleed wastes more tokens than
522
+ isolated palaces save.
523
+
524
+ Token math you can quote at the LLM:
525
+
526
+ > Recomputing a scan is ~1200 rg tokens. Reading the same stash via
527
+ > `--mp-from` is ~377 tokens (3.2× cheaper). Pruning is free.
528
+
529
+ That ratio is why **prune + compose** beats **re-search** every time
530
+ at the synthesis step.
531
+
532
+ ## What the bench data says (informs the patterns above)
533
+
534
+ Headline numbers from `BENCHMARKS.md` (oasis-sleek conductor tracks corpus — markdown specs + JSON metadata):
535
+
536
+ | comparison | mpg config | result |
537
+ | :--- | :--- | :--- |
538
+ | Literal recall vs **ripgrep** | `scan + clip_chars: 30` | 100% / 100% / **377 tokens** vs rg's 1197 — **3.2× cheaper than rg** |
539
+ | Typo recovery | `fuzzy: true` | 100% recall vs rg's 0%, 89% precision, ~1900 tokens. Embeddings get 45% at 23,610 tokens. |
540
+ | Compaction at fixed budget | `scan + clip + max_tokens` (no LLM) | 67% pass beats LLM summarization (33%) at zero LLM cost |
541
+ | Multi-turn convergence | treatment uses mpg + stash | 24% fewer input tokens, **half the tool calls and turns** vs control |
542
+ | Mind palace set semantics | `compose` / `intersect` / `except` / graph | 17/17 micro assertions pass |
543
+
544
+ Where mpg loses on the bench (worth knowing):
545
+ - **Single-keyword lookups** where you only need a file:line list (T2 BLAKE3, T3 load_to_bevy in macro): rg is cheaper. Use `bash`/`grep` for one-word answers.
546
+ - **Cold-start wall-clock**: ~200ms per CLI call. MCP server / programmatic import avoid this (the cost is paid once at boot).
547
+
548
+ ## Read further (load on demand)
549
+
550
+ When you need depth on one of these areas, read the matching file:
551
+
552
+ - `references/integration.md` — MCP server vs CLI vs programmatic SDK import; when to prefer each.
553
+ - `references/mind-palace.md` — full mind palace surface: relationships, pruning, TTL, set ops (`compose`/`except`/`intersect`), graph traversal, multi-palace isolation.
554
+ - `references/sources.md` — search files, dirs, globs, command stdout (`--cmd`), URLs (`--url`), stdin (`--stdin`); `--include`/`--exclude`/`--type` filters.
555
+ - `references/multi-agent.md` — palace sharing across agents, write-write race caveats, recommended layouts.
556
+ - `references/anti-patterns.md` — full list of what NOT to do and why.