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.
- package/INSTALL.md +387 -0
- package/README.md +602 -0
- package/dist/api.d.ts +682 -0
- package/dist/api.js +660 -0
- package/dist/api.js.map +1 -0
- package/dist/cli.d.ts +95 -0
- package/dist/cli.js +856 -0
- package/dist/cli.js.map +1 -0
- package/dist/format.d.ts +16 -0
- package/dist/format.js +199 -0
- package/dist/format.js.map +1 -0
- package/dist/fuzzy.d.ts +45 -0
- package/dist/fuzzy.js +150 -0
- package/dist/fuzzy.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +528 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-server.d.ts +24 -0
- package/dist/mcp-server.js +187 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/mind-palace.d.ts +148 -0
- package/dist/mind-palace.js +780 -0
- package/dist/mind-palace.js.map +1 -0
- package/dist/nodes.d.ts +57 -0
- package/dist/nodes.js +220 -0
- package/dist/nodes.js.map +1 -0
- package/dist/pagination.d.ts +41 -0
- package/dist/pagination.js +63 -0
- package/dist/pagination.js.map +1 -0
- package/dist/palace-format.d.ts +30 -0
- package/dist/palace-format.js +146 -0
- package/dist/palace-format.js.map +1 -0
- package/dist/rg.d.ts +34 -0
- package/dist/rg.js +288 -0
- package/dist/rg.js.map +1 -0
- package/dist/sources.d.ts +87 -0
- package/dist/sources.js +457 -0
- package/dist/sources.js.map +1 -0
- package/dist/tokens.d.ts +35 -0
- package/dist/tokens.js +95 -0
- package/dist/tokens.js.map +1 -0
- package/dist/types.d.ts +236 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/package.json +67 -0
- package/skills/mpg-context/SKILL.md +556 -0
- package/skills/mpg-context/references/anti-patterns.md +133 -0
- package/skills/mpg-context/references/integration.md +123 -0
- package/skills/mpg-context/references/mind-palace.md +217 -0
- package/skills/mpg-context/references/multi-agent.md +147 -0
- 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.
|