@jeffober/thatch 0.1.10 → 0.1.12
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/docs/dev/skills.md +7 -6
- package/package.json +1 -1
- package/src/db.ts +48 -27
- package/src/prompts.ts +62 -5
- package/src/skills.ts +134 -4
- package/src/tool-defs.ts +13 -3
package/docs/dev/skills.md
CHANGED
|
@@ -21,9 +21,9 @@ description: Extract durable project facts ... Use when ...
|
|
|
21
21
|
- `description` drives when the agent loads the skill (both opencode and
|
|
22
22
|
Cursor auto-discover skills and use the description for relevance).
|
|
23
23
|
|
|
24
|
-
## The
|
|
24
|
+
## The 14 skills
|
|
25
25
|
|
|
26
|
-
**Shared (
|
|
26
|
+
**Shared (13)** — installed everywhere; no sub-agents required:
|
|
27
27
|
|
|
28
28
|
| Skill | Role |
|
|
29
29
|
|-------|------|
|
|
@@ -35,6 +35,7 @@ description: Extract durable project facts ... Use when ...
|
|
|
35
35
|
| `thatch-review-state-flow` | Data flow and contracts: module boundaries, implicit FSMs, error propagation. |
|
|
36
36
|
| `thatch-review-no-slop` | AI writing anti-patterns: change narration, fourth-wall breaks, em dashes, filler. |
|
|
37
37
|
| `thatch-review-breadcrumbs` | Comment narrative: do comments form a coherent outline of behavior? |
|
|
38
|
+
| `thatch-review-mark-and-sweep` | Mechanical change completeness: whole-repo sweep for stragglers after renames, flag removals, API substitutions. |
|
|
38
39
|
| `thatch-review-synthesizer` | Verify specialist findings against code, dedupe, classify, calibrate severity. |
|
|
39
40
|
| `thatch-review-context` | Gather project/feature context (PR descriptions, git archaeology, ticket references, memory) before fan-out. Prevents false positives about intentionally deferred work. |
|
|
40
41
|
| `thatch-workflow-research` | Research code workflows/features affected by a change or planned change. Reads code flows, comments, git history, memories, docs. Produces a guide to the code for reviewers or planners. |
|
|
@@ -44,11 +45,11 @@ description: Extract durable project facts ... Use when ...
|
|
|
44
45
|
|
|
45
46
|
| Skill | Role |
|
|
46
47
|
|-------|------|
|
|
47
|
-
| `thatch-code-review` | Resolve review target, gather project context, research affected workflows, estimate complexity, partition, dispatch the
|
|
48
|
+
| `thatch-code-review` | Resolve review target, gather project context, research affected workflows, estimate complexity, partition, dispatch the 6 specialists in parallel, synthesize. |
|
|
48
49
|
|
|
49
50
|
## REVIEW_COMMON
|
|
50
51
|
|
|
51
|
-
The
|
|
52
|
+
The six review specialists share a framework interpolated via `${REVIEW_COMMON}`:
|
|
52
53
|
|
|
53
54
|
- **Static analysis only** — no running tests/linters/compilers.
|
|
54
55
|
- **Scope gathering** — resolve the git range, `git diff --stat`, read changed
|
|
@@ -78,7 +79,7 @@ The synthesizer reuses the same verification rigor but has its own structure
|
|
|
78
79
|
## The two arrays
|
|
79
80
|
|
|
80
81
|
```ts
|
|
81
|
-
const SHARED_SKILLS: SkillDef[] = [ /*
|
|
82
|
+
const SHARED_SKILLS: SkillDef[] = [ /* 13 skills above */ ];
|
|
82
83
|
const OPENCODE_ONLY_SKILLS: SkillDef[] = [ /* code-review coordinator */ ];
|
|
83
84
|
```
|
|
84
85
|
|
|
@@ -112,7 +113,7 @@ and `--cursor` pass only the shared set.
|
|
|
112
113
|
## Memory review skills in practice
|
|
113
114
|
|
|
114
115
|
- **Quick single lens**: load any specialist directly and point it at a branch.
|
|
115
|
-
- **Full review on opencode**: load `thatch-code-review` — it gathers project context, researches affected workflows, dispatches all
|
|
116
|
+
- **Full review on opencode**: load `thatch-code-review` — it gathers project context, researches affected workflows, dispatches all 6 specialists in parallel (with both the context brief and workflow guide injected into each briefing), then synthesizes.
|
|
116
117
|
- **Full review on Claude Code/Cursor**: run each specialist in sequence, then
|
|
117
118
|
run `thatch-review-synthesizer` to verify and aggregate.
|
|
118
119
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jeffober/thatch",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"description": "Persistent memory for AI coding agents — local embeddings, SQLite stores, zero config. Works with OpenCode (as a plugin), Claude Code (as an MCP server), and Cursor (as an MCP server).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
package/src/db.ts
CHANGED
|
@@ -9,6 +9,7 @@ export interface MemoryRow {
|
|
|
9
9
|
model: string | null;
|
|
10
10
|
branch: string | null;
|
|
11
11
|
confidence: number | null;
|
|
12
|
+
archived: boolean;
|
|
12
13
|
created_at: string;
|
|
13
14
|
updated_at: string;
|
|
14
15
|
recall_count: number;
|
|
@@ -69,6 +70,7 @@ export class ThatchDB {
|
|
|
69
70
|
model TEXT,
|
|
70
71
|
branch TEXT,
|
|
71
72
|
confidence INTEGER,
|
|
73
|
+
archived INTEGER NOT NULL DEFAULT 0,
|
|
72
74
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
|
|
73
75
|
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
|
|
74
76
|
recall_count INTEGER NOT NULL DEFAULT 0,
|
|
@@ -102,6 +104,7 @@ export class ThatchDB {
|
|
|
102
104
|
const wanted: [string, string][] = [
|
|
103
105
|
["recall_count", "INTEGER NOT NULL DEFAULT 0"],
|
|
104
106
|
["last_recalled_at", "TEXT"],
|
|
107
|
+
["archived", "INTEGER NOT NULL DEFAULT 0"],
|
|
105
108
|
];
|
|
106
109
|
for (const [col, decl] of wanted) {
|
|
107
110
|
if (!existing.has(col)) {
|
|
@@ -170,27 +173,23 @@ export class ThatchDB {
|
|
|
170
173
|
content: string,
|
|
171
174
|
embedding: Float32Array,
|
|
172
175
|
model: string,
|
|
173
|
-
opts?: { branch?: string; confidence?: number; overwrite?: boolean },
|
|
176
|
+
opts?: { branch?: string; confidence?: number; overwrite?: boolean; archived?: boolean },
|
|
174
177
|
): { ok: true } | { ok: false; error: string } {
|
|
175
178
|
const slug = this.slugify(label);
|
|
176
179
|
this.ensureStore(store);
|
|
177
180
|
|
|
178
|
-
// The embedding may be a view into a larger buffer (transformers.js tensor
|
|
179
|
-
// output); serialize exactly the view's bytes, not the whole backing buffer.
|
|
180
181
|
const blob = new Uint8Array(embedding.buffer, embedding.byteOffset, embedding.byteLength);
|
|
181
182
|
const branch = opts?.branch ?? null;
|
|
182
183
|
const confidence = opts?.confidence ?? null;
|
|
184
|
+
const archived = opts?.archived === true ? 1 : (opts?.archived === false ? 0 : null);
|
|
183
185
|
const now = new Date().toISOString();
|
|
184
|
-
const params = [slug, store, label, content, blob, model, branch, confidence, now, now];
|
|
185
186
|
|
|
186
187
|
if (!opts?.overwrite) {
|
|
187
|
-
// Plain INSERT so the PK constraint enforces no-clobber atomically —
|
|
188
|
-
// a check-then-upsert would race against concurrent sessions.
|
|
189
188
|
try {
|
|
190
189
|
this.#db.run(
|
|
191
|
-
`INSERT INTO entries (slug, store, label, content, embedding, model, branch, confidence, created_at, updated_at)
|
|
192
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
193
|
-
|
|
190
|
+
`INSERT INTO entries (slug, store, label, content, embedding, model, branch, confidence, archived, created_at, updated_at)
|
|
191
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
192
|
+
[slug, store, label, content, blob, model, branch, confidence, archived ?? 0, now, now] as any,
|
|
194
193
|
);
|
|
195
194
|
return { ok: true };
|
|
196
195
|
} catch (err: any) {
|
|
@@ -205,10 +204,25 @@ export class ThatchDB {
|
|
|
205
204
|
}
|
|
206
205
|
}
|
|
207
206
|
|
|
207
|
+
const existing = this.#db
|
|
208
|
+
.query("SELECT archived FROM entries WHERE slug = ? AND store = ?")
|
|
209
|
+
.get(slug, store) as { archived: number } | null;
|
|
210
|
+
|
|
211
|
+
if (existing && existing.archived && opts?.archived === undefined) {
|
|
212
|
+
return {
|
|
213
|
+
ok: false,
|
|
214
|
+
error: `"${label}" is archived. Pass archived: true to keep it archived, or archived: false to unarchive it.`,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// archived column for upsert: the INSERT VALUES always needs a non-null
|
|
219
|
+
// value (NOT NULL constraint), so use archived ?? 0. To preserve the
|
|
220
|
+
// existing value when the caller doesn't specify archived, the UPDATE
|
|
221
|
+
// clause accepts a nullable extra param: NULL means "keep existing."
|
|
208
222
|
this.#db.run(
|
|
209
223
|
`
|
|
210
|
-
INSERT INTO entries (slug, store, label, content, embedding, model, branch, confidence, created_at, updated_at)
|
|
211
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
224
|
+
INSERT INTO entries (slug, store, label, content, embedding, model, branch, confidence, archived, created_at, updated_at)
|
|
225
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
212
226
|
ON CONFLICT(slug, store) DO UPDATE SET
|
|
213
227
|
label = excluded.label,
|
|
214
228
|
content = excluded.content,
|
|
@@ -216,13 +230,12 @@ export class ThatchDB {
|
|
|
216
230
|
model = excluded.model,
|
|
217
231
|
branch = COALESCE(excluded.branch, entries.branch),
|
|
218
232
|
confidence = COALESCE(excluded.confidence, entries.confidence),
|
|
233
|
+
archived = COALESCE(?, entries.archived),
|
|
219
234
|
updated_at = excluded.updated_at
|
|
220
235
|
`,
|
|
221
|
-
|
|
236
|
+
[slug, store, label, content, blob, model, branch, confidence, archived ?? 0, now, now, archived] as any,
|
|
222
237
|
);
|
|
223
238
|
|
|
224
|
-
// Content changed, so prior dedup verdicts involving this entry are stale;
|
|
225
|
-
// clear them so findDuplicates can re-flag the pair.
|
|
226
239
|
this.#db.run(
|
|
227
240
|
"DELETE FROM dedup_pairs WHERE store = ? AND (slug_a = ? OR slug_b = ?)",
|
|
228
241
|
[store, slug, slug],
|
|
@@ -242,22 +255,25 @@ export class ThatchDB {
|
|
|
242
255
|
search(
|
|
243
256
|
stores: string[],
|
|
244
257
|
queryEmbedding: Float32Array,
|
|
245
|
-
opts?: { branch?: string; limit?: number },
|
|
258
|
+
opts?: { branch?: string; limit?: number; includeArchived?: boolean },
|
|
246
259
|
): (MemoryRow & { _score: number })[] {
|
|
247
260
|
if (stores.length === 0) return [];
|
|
248
261
|
|
|
249
262
|
const limit = opts?.limit ?? 10;
|
|
250
263
|
const branch = opts?.branch;
|
|
264
|
+
const includeArchived = opts?.includeArchived ?? false;
|
|
251
265
|
|
|
252
266
|
const placeholders = stores.map(() => "?").join(", ");
|
|
253
267
|
|
|
254
268
|
interface SqlParams { sql: string; params: any[] }
|
|
255
269
|
|
|
256
270
|
const { sql, params }: SqlParams = (() => {
|
|
271
|
+
const clauses = ["store IN (" + placeholders + ")", "embedding IS NOT NULL"];
|
|
272
|
+
if (!includeArchived) clauses.push("archived = 0");
|
|
257
273
|
const base = `
|
|
258
|
-
SELECT slug, store, label, content, embedding, model, branch, confidence, created_at, updated_at, recall_count, last_recalled_at
|
|
274
|
+
SELECT slug, store, label, content, embedding, model, branch, confidence, archived, created_at, updated_at, recall_count, last_recalled_at
|
|
259
275
|
FROM entries
|
|
260
|
-
WHERE
|
|
276
|
+
WHERE ${clauses.join(" AND ")}
|
|
261
277
|
`;
|
|
262
278
|
|
|
263
279
|
if (branch) {
|
|
@@ -278,6 +294,7 @@ export class ThatchDB {
|
|
|
278
294
|
// rather than ranked. Dimension is the discriminator — model tags are
|
|
279
295
|
// informational only.
|
|
280
296
|
const scored = rows.flatMap((row) => {
|
|
297
|
+
row.archived = !!row.archived;
|
|
281
298
|
const emb = blobToVector(row.embedding!);
|
|
282
299
|
if (emb.length !== queryEmbedding.length) return [];
|
|
283
300
|
return [{ ...row, embedding: row.embedding, _score: cosineSimilarity(queryEmbedding, emb) }];
|
|
@@ -295,7 +312,7 @@ export class ThatchDB {
|
|
|
295
312
|
recall(
|
|
296
313
|
stores: string[],
|
|
297
314
|
queryEmbedding: Float32Array,
|
|
298
|
-
opts?: { branch?: string; limit?: number },
|
|
315
|
+
opts?: { branch?: string; limit?: number; includeArchived?: boolean },
|
|
299
316
|
): (MemoryRow & { _score: number })[] {
|
|
300
317
|
const top = this.search(stores, queryEmbedding, opts);
|
|
301
318
|
|
|
@@ -326,7 +343,7 @@ export class ThatchDB {
|
|
|
326
343
|
const limit = opts?.limit ?? 3;
|
|
327
344
|
|
|
328
345
|
const rows = this.#db
|
|
329
|
-
.query("SELECT slug, label, embedding FROM entries WHERE store = ? AND embedding IS NOT NULL")
|
|
346
|
+
.query("SELECT slug, label, embedding FROM entries WHERE store = ? AND archived = 0 AND embedding IS NOT NULL")
|
|
330
347
|
.all(store) as any[];
|
|
331
348
|
|
|
332
349
|
const hits: SimilarEntry[] = [];
|
|
@@ -345,10 +362,10 @@ export class ThatchDB {
|
|
|
345
362
|
}
|
|
346
363
|
|
|
347
364
|
/** Lists all entries in a store, returning metadata without content. */
|
|
348
|
-
listEntries(store: string): { slug: string; label: string; branch: string | null; confidence: number | null; updated_at: string }[] {
|
|
365
|
+
listEntries(store: string): { slug: string; label: string; branch: string | null; confidence: number | null; archived: boolean; updated_at: string }[] {
|
|
349
366
|
return this.#db
|
|
350
367
|
.query(
|
|
351
|
-
"SELECT slug, label, branch, confidence, updated_at FROM entries WHERE store = ? ORDER BY label",
|
|
368
|
+
"SELECT slug, label, branch, confidence, archived, updated_at FROM entries WHERE store = ? ORDER BY label",
|
|
352
369
|
)
|
|
353
370
|
.all(store)
|
|
354
371
|
.map((r: any) => ({
|
|
@@ -356,6 +373,7 @@ export class ThatchDB {
|
|
|
356
373
|
label: r.label,
|
|
357
374
|
branch: r.branch,
|
|
358
375
|
confidence: r.confidence,
|
|
376
|
+
archived: !!r.archived,
|
|
359
377
|
updated_at: r.updated_at,
|
|
360
378
|
}));
|
|
361
379
|
}
|
|
@@ -363,11 +381,14 @@ export class ThatchDB {
|
|
|
363
381
|
/** Full content of a single entry by label. */
|
|
364
382
|
showEntry(store: string, label: string): MemoryRow | null {
|
|
365
383
|
const slug = this.slugify(label);
|
|
366
|
-
|
|
384
|
+
const row = this.#db
|
|
367
385
|
.query(
|
|
368
|
-
"SELECT slug, store, label, content, embedding, model, branch, confidence, created_at, updated_at, recall_count, last_recalled_at FROM entries WHERE slug = ? AND store = ?",
|
|
386
|
+
"SELECT slug, store, label, content, embedding, model, branch, confidence, archived, created_at, updated_at, recall_count, last_recalled_at FROM entries WHERE slug = ? AND store = ?",
|
|
369
387
|
)
|
|
370
|
-
.get(slug, store) as
|
|
388
|
+
.get(slug, store) as Record<string, unknown> | null;
|
|
389
|
+
if (!row) return null;
|
|
390
|
+
row.archived = !!row.archived;
|
|
391
|
+
return row as unknown as MemoryRow;
|
|
371
392
|
}
|
|
372
393
|
|
|
373
394
|
forgetEntry(store: string, label: string): boolean {
|
|
@@ -386,7 +407,7 @@ export class ThatchDB {
|
|
|
386
407
|
findDuplicates(store: string, threshold = 0.85): DedupCandidate[] {
|
|
387
408
|
const rows = this.#db
|
|
388
409
|
.query(
|
|
389
|
-
"SELECT slug, label, content, embedding FROM entries WHERE store = ? AND embedding IS NOT NULL ORDER BY slug",
|
|
410
|
+
"SELECT slug, label, content, embedding FROM entries WHERE store = ? AND archived = 0 AND embedding IS NOT NULL ORDER BY slug",
|
|
390
411
|
)
|
|
391
412
|
.all(store) as any[];
|
|
392
413
|
|
|
@@ -446,7 +467,7 @@ export class ThatchDB {
|
|
|
446
467
|
const row = this.#db
|
|
447
468
|
.query(
|
|
448
469
|
`SELECT COUNT(*) AS n FROM entries
|
|
449
|
-
WHERE store = ? AND max(updated_at, COALESCE(last_recalled_at, updated_at)) < ?`,
|
|
470
|
+
WHERE store = ? AND archived = 0 AND max(updated_at, COALESCE(last_recalled_at, updated_at)) < ?`,
|
|
450
471
|
)
|
|
451
472
|
.get(store, cutoffIso) as any;
|
|
452
473
|
return row?.n ?? 0;
|
|
@@ -465,7 +486,7 @@ export class ThatchDB {
|
|
|
465
486
|
if (branches.length === 0) return 0;
|
|
466
487
|
const placeholders = branches.map(() => "?").join(", ");
|
|
467
488
|
const row = this.#db
|
|
468
|
-
.query(`SELECT COUNT(*) AS n FROM entries WHERE store = ? AND branch IN (${placeholders})`)
|
|
489
|
+
.query(`SELECT COUNT(*) AS n FROM entries WHERE store = ? AND archived = 0 AND branch IN (${placeholders})`)
|
|
469
490
|
.get(store, ...branches) as any;
|
|
470
491
|
return row?.n ?? 0;
|
|
471
492
|
}
|
package/src/prompts.ts
CHANGED
|
@@ -32,7 +32,7 @@ you're working in. Prior sessions may have already investigated it.
|
|
|
32
32
|
Thatch ships skills for code review, project investigation, and memory
|
|
33
33
|
workflows. The host auto-discovers them, but reach for them proactively:
|
|
34
34
|
|
|
35
|
-
- \`thatch-code-review\` — full multi-agent code review (dispatches
|
|
35
|
+
- \`thatch-code-review\` — full multi-agent code review (dispatches 6 specialists + synthesizer). Requires sub-agent support.
|
|
36
36
|
- \`thatch-workflow-research\` — research code workflows affected by a change or planned change, before reviewing or planning.
|
|
37
37
|
- \`thatch-review-context\` — gather project context (PRs, tickets, TODOs, deferred work) before a review.
|
|
38
38
|
- \`thatch-project-primer\` — investigate a new project and write foundational memories.
|
|
@@ -69,6 +69,25 @@ new project knowledge, conventions, architectural decisions, investigation resul
|
|
|
69
69
|
Session-specific context, incomplete/unverified info, anything already in
|
|
70
70
|
CLAUDE.md or OPENCODE.md, speculative conclusions.
|
|
71
71
|
|
|
72
|
+
## Archived Memories
|
|
73
|
+
|
|
74
|
+
Memories can be marked \`archived\` — a flag for stable, long-term historical
|
|
75
|
+
records that should not trigger hygiene nudges (stale, orphaned, duplicate).
|
|
76
|
+
Archived memories are excluded from search/recall results by default; pass
|
|
77
|
+
\`includeArchived: true\` to surface them for archaeological dives.
|
|
78
|
+
|
|
79
|
+
When a branch is merged or about to be deleted, consolidate its branch-scoped
|
|
80
|
+
memories into a single archived memory scoped to that same branch (preserving
|
|
81
|
+
provenance). Capture intent, design decisions, review back-and-forth, PR
|
|
82
|
+
number, unexpected pivots — the kind of git-archaeology context that explains
|
|
83
|
+
_why_ the code looks the way it does a year from now. Then memory_forget the
|
|
84
|
+
originals. The archived record outlives the branch. Future sessions searching
|
|
85
|
+
with \`includeArchived: true\` can pull it up when investigating ambiguous code.
|
|
86
|
+
|
|
87
|
+
Updating an archived memory requires passing \`archived\` explicitly — omit it
|
|
88
|
+
and the tool returns an error. Pass \`archived: true\` to keep it archived,
|
|
89
|
+
\`archived: false\` to unarchive.
|
|
90
|
+
|
|
72
91
|
## Explicit Requests
|
|
73
92
|
|
|
74
93
|
"Remember X" — save immediately.
|
|
@@ -94,7 +113,7 @@ Use thatch_store_list then thatch_memory_list to see what's available, then that
|
|
|
94
113
|
return `${base}
|
|
95
114
|
|
|
96
115
|
[thatch hygiene] ${hygiene}
|
|
97
|
-
When convenient this session (not before addressing the user's request), tend the store: run thatch_find_duplicates and consolidate clusters, review stale memories with thatch_memory_show and update or forget them, and
|
|
116
|
+
When convenient this session (not before addressing the user's request), tend the store: run thatch_find_duplicates and consolidate clusters, review stale memories with thatch_memory_show and update or forget them, and for memories scoped to deleted branches, consolidate them into an archived historical record before forgetting the originals.`;
|
|
98
117
|
}
|
|
99
118
|
|
|
100
119
|
/**
|
|
@@ -146,7 +165,7 @@ workflows. The host auto-discovers them, but reach for them proactively:
|
|
|
146
165
|
|
|
147
166
|
- \`thatch-workflow-research\` — research code workflows affected by a change or planned change, before reviewing or planning.
|
|
148
167
|
- \`thatch-review-context\` — gather project context (PRs, tickets, TODOs, deferred work) before a review.
|
|
149
|
-
- \`thatch-review-pedantic\` / \`-acceptance\` / \`-state-flow\` / \`-no-slop\` / \`-breadcrumbs\` —
|
|
168
|
+
- \`thatch-review-pedantic\` / \`-acceptance\` / \`-state-flow\` / \`-no-slop\` / \`-breadcrumbs\` / \`-mark-and-sweep\` — six specialist review lenses. Run individually, then \`thatch-review-synthesizer\` to verify and aggregate.
|
|
150
169
|
- \`thatch-project-primer\` — investigate a new project and write foundational memories.
|
|
151
170
|
- \`thatch-session-reflection\` — record what you learned at end of session.
|
|
152
171
|
- \`thatch-fact-extractor\` — extract durable facts from recent tool interactions (auto-triggered by the extraction pipeline).
|
|
@@ -181,6 +200,25 @@ new project knowledge, conventions, architectural decisions, investigation resul
|
|
|
181
200
|
Session-specific context, incomplete/unverified info, anything already in
|
|
182
201
|
CLAUDE.md, speculative conclusions.
|
|
183
202
|
|
|
203
|
+
## Archived Memories
|
|
204
|
+
|
|
205
|
+
Memories can be marked \`archived\` — a flag for stable, long-term historical
|
|
206
|
+
records that should not trigger hygiene nudges (stale, orphaned, duplicate).
|
|
207
|
+
Archived memories are excluded from search/recall results by default; pass
|
|
208
|
+
\`includeArchived: true\` to surface them for archaeological dives.
|
|
209
|
+
|
|
210
|
+
When a branch is merged or about to be deleted, consolidate its branch-scoped
|
|
211
|
+
memories into a single archived memory scoped to that same branch (preserving
|
|
212
|
+
provenance). Capture intent, design decisions, review back-and-forth, PR
|
|
213
|
+
number, unexpected pivots — the kind of git-archaeology context that explains
|
|
214
|
+
_why_ the code looks the way it does a year from now. Then memory_forget the
|
|
215
|
+
originals. The archived record outlives the branch. Future sessions searching
|
|
216
|
+
with \`includeArchived: true\` can pull it up when investigating ambiguous code.
|
|
217
|
+
|
|
218
|
+
Updating an archived memory requires passing \`archived\` explicitly — omit it
|
|
219
|
+
and the tool returns an error. Pass \`archived: true\` to keep it archived,
|
|
220
|
+
\`archived: false\` to unarchive.
|
|
221
|
+
|
|
184
222
|
## Explicit Requests
|
|
185
223
|
|
|
186
224
|
"Remember X" — save immediately.
|
|
@@ -205,7 +243,7 @@ Use store_list then memory_list to see what's available, then memory_recall as n
|
|
|
205
243
|
return `${base}
|
|
206
244
|
|
|
207
245
|
[thatch hygiene] ${hygiene}
|
|
208
|
-
When convenient this session (not before addressing the user's request), tend the store: run find_duplicates and consolidate clusters, review stale memories with memory_show and update or forget them, and
|
|
246
|
+
When convenient this session (not before addressing the user's request), tend the store: run find_duplicates and consolidate clusters, review stale memories with memory_show and update or forget them, and for memories scoped to deleted branches, consolidate them into an archived historical record before forgetting the originals.`;
|
|
209
247
|
}
|
|
210
248
|
|
|
211
249
|
/**
|
|
@@ -274,7 +312,7 @@ workflows. The host auto-discovers them, but reach for them proactively:
|
|
|
274
312
|
|
|
275
313
|
- \`thatch-workflow-research\` — research code workflows affected by a change or planned change, before reviewing or planning.
|
|
276
314
|
- \`thatch-review-context\` — gather project context (PRs, tickets, TODOs, deferred work) before a review.
|
|
277
|
-
- \`thatch-review-pedantic\` / \`-acceptance\` / \`-state-flow\` / \`-no-slop\` / \`-breadcrumbs\` —
|
|
315
|
+
- \`thatch-review-pedantic\` / \`-acceptance\` / \`-state-flow\` / \`-no-slop\` / \`-breadcrumbs\` / \`-mark-and-sweep\` — six specialist review lenses. Run individually, then \`thatch-review-synthesizer\` to verify and aggregate.
|
|
278
316
|
- \`thatch-project-primer\` — investigate a new project and write foundational memories.
|
|
279
317
|
- \`thatch-session-reflection\` — record what you learned at end of session.
|
|
280
318
|
- \`thatch-fact-extractor\` — extract durable facts from recent tool interactions (auto-triggered by the extraction pipeline).
|
|
@@ -309,6 +347,25 @@ new project knowledge, conventions, architectural decisions, investigation resul
|
|
|
309
347
|
Session-specific context, incomplete/unverified info, anything already in
|
|
310
348
|
AGENTS.md, speculative conclusions.
|
|
311
349
|
|
|
350
|
+
## Archived Memories
|
|
351
|
+
|
|
352
|
+
Memories can be marked \`archived\` — a flag for stable, long-term historical
|
|
353
|
+
records that should not trigger hygiene nudges (stale, orphaned, duplicate).
|
|
354
|
+
Archived memories are excluded from search/recall results by default; pass
|
|
355
|
+
\`includeArchived: true\` to surface them for archaeological dives.
|
|
356
|
+
|
|
357
|
+
When a branch is merged or about to be deleted, consolidate its branch-scoped
|
|
358
|
+
memories into a single archived memory scoped to that same branch (preserving
|
|
359
|
+
provenance). Capture intent, design decisions, review back-and-forth, PR
|
|
360
|
+
number, unexpected pivots — the kind of git-archaeology context that explains
|
|
361
|
+
_why_ the code looks the way it does a year from now. Then memory_forget the
|
|
362
|
+
originals. The archived record outlives the branch. Future sessions searching
|
|
363
|
+
with \`includeArchived: true\` can pull it up when investigating ambiguous code.
|
|
364
|
+
|
|
365
|
+
Updating an archived memory requires passing \`archived\` explicitly — omit it
|
|
366
|
+
and the tool returns an error. Pass \`archived: true\` to keep it archived,
|
|
367
|
+
\`archived: false\` to unarchive.
|
|
368
|
+
|
|
312
369
|
## Explicit Requests
|
|
313
370
|
|
|
314
371
|
"Remember X" — save immediately.
|
package/src/skills.ts
CHANGED
|
@@ -652,6 +652,133 @@ Good section headers create a table of contents:
|
|
|
652
652
|
Do NOT report on files you did not actually read.
|
|
653
653
|
`;
|
|
654
654
|
|
|
655
|
+
const REVIEW_MARK_AND_SWEEP = `---
|
|
656
|
+
name: thatch-review-mark-and-sweep
|
|
657
|
+
description: Mechanical change completeness audit — verifies that renames, flag removals, API substitutions, and mass replacements left no stragglers. Sweeps the whole repo, not just changed files. Use for post-implementation review of a branch, PR, or commit range.
|
|
658
|
+
---
|
|
659
|
+
|
|
660
|
+
You are a mark-and-sweep review agent. You verify that mechanical changes — renames, feature flag removals, API/module substitutions, mass replacements — achieved closure: every occurrence of the old identifier was updated, no dead branches or orphaned references remain, and touch points were neatened with appropriate comments.
|
|
661
|
+
${REVIEW_COMMON}
|
|
662
|
+
## When to sweep
|
|
663
|
+
|
|
664
|
+
Not every change is a mechanical change. Before sweeping, determine whether the diff shows patterns of mechanical change:
|
|
665
|
+
|
|
666
|
+
**Sweep triggers:**
|
|
667
|
+
- Same identifier removed or replaced across multiple files
|
|
668
|
+
- Import substitution (old module replaced with new module)
|
|
669
|
+
- Renamed function, module, class, or variable (old name to new name)
|
|
670
|
+
- Removed config key, feature flag, or environment variable
|
|
671
|
+
- Deleted function/module/class that may have callers elsewhere
|
|
672
|
+
|
|
673
|
+
**No sweep needed:**
|
|
674
|
+
- Bug fixes (changing logic, not removing identifiers)
|
|
675
|
+
- New features (adding code, not removing identifiers)
|
|
676
|
+
- Behavioral refactors (changing what code does, not what it's called)
|
|
677
|
+
|
|
678
|
+
If no mechanical change is detected, report: "No mechanical change detected — nothing to sweep." Do not force a sweep on a diff that has nothing to mark.
|
|
679
|
+
|
|
680
|
+
## Your focus
|
|
681
|
+
|
|
682
|
+
You care about:
|
|
683
|
+
- **Stragglers**: Old identifiers still referenced in files the diff did not touch
|
|
684
|
+
- **Dead branches**: Conditional branches now unreachable because their guard or flag was removed but the branch was not collapsed
|
|
685
|
+
- **Stale comments**: Comments that reference the old identifier, old behavior, or old config that no longer exists
|
|
686
|
+
- **Orphaned imports**: Imports of the old module or function that are no longer used after the change
|
|
687
|
+
- **Config residue**: Default values, config schemas, or env var docs for removed identifiers
|
|
688
|
+
- **Test residue**: Test fixtures or test cases that still set removed flags, call removed functions, or import removed modules
|
|
689
|
+
|
|
690
|
+
You do NOT care about:
|
|
691
|
+
- Whether the new code is correct (other reviewers handle logic)
|
|
692
|
+
- UX or behavioral concerns
|
|
693
|
+
- Code style or formatting
|
|
694
|
+
- Comment narrative quality
|
|
695
|
+
|
|
696
|
+
## The mark-and-sweep method
|
|
697
|
+
|
|
698
|
+
Your scope is NOT limited to changed files. The scope gathering steps above identify what changed and give you the diffs you need to extract old identifiers. The sweep below extends across the entire repository.
|
|
699
|
+
|
|
700
|
+
### 1. Mark — extract old identifiers from the diff
|
|
701
|
+
|
|
702
|
+
For each changed file, read the diff (\`git diff <range> -- <file>\`). From the removed lines (starting with \`-\`), extract identifiers that were removed, renamed, or replaced:
|
|
703
|
+
|
|
704
|
+
- **Function and method definitions**: \`def old_name\`, \`function old_name\`, \`fn old_name\`, \`const old_name\`, etc.
|
|
705
|
+
- **Imports**: \`import old_module\`, \`from old_module import ...\`, \`require("old_module")\`, \`use old_module\`, etc.
|
|
706
|
+
- **Variables and constants**: \`old_flag = ...\`, \`OLD_CONFIG_KEY = ...\`, etc.
|
|
707
|
+
- **Renames**: If an old name appears on a \`-\` line and a new name on the corresponding \`+\` line, mark the old name. The new name tells you what to expect in updated files.
|
|
708
|
+
- **Config keys and feature flags**: Names of removed flags, config keys, or env vars from deleted lines.
|
|
709
|
+
|
|
710
|
+
Also read commit messages (\`git log --oneline <range>\`) for intent: "remove X", "rename A to B", "replace Y with Z", "delete deprecated Z".
|
|
711
|
+
|
|
712
|
+
Build a mark list: each entry is an old identifier and what replaced it (if anything).
|
|
713
|
+
|
|
714
|
+
### 2. Sweep — search the entire repository for each marked identifier
|
|
715
|
+
|
|
716
|
+
For each identifier in the mark list, run a whole-repo search:
|
|
717
|
+
- \`git grep -n "<identifier>"\` or \`rg -n "<identifier>"\` across the entire repository
|
|
718
|
+
- Exclude: vendored dependencies, generated files, lockfiles, compiled assets, node_modules, .git
|
|
719
|
+
|
|
720
|
+
For each hit, classify it:
|
|
721
|
+
- **In a changed file, still present**: The old identifier appears in a changed file but was not updated. This is a finding — the change was incomplete even within the files it touched.
|
|
722
|
+
- **In an unchanged file**: This is the primary finding type. The sweep missed this occurrence entirely.
|
|
723
|
+
- **False positive**: Substring match, different namespace, homonym, or unrelated use. Filter these out.
|
|
724
|
+
|
|
725
|
+
For common-word identifiers, use word-boundary patterns or filter by context. A reference to a removed \`process\` function is a straggler; the word "process" in a comment about CPU scheduling is not.
|
|
726
|
+
|
|
727
|
+
### 3. Touchpoint neatness — verify change sites were cleaned up
|
|
728
|
+
|
|
729
|
+
For each changed file where an old identifier was removed or replaced:
|
|
730
|
+
- **Dead branches**: If a conditional guard was removed (e.g., \`if feature_flag_enabled\`), was the entire conditional collapsed or just the guard line? Is the else branch now dead code that should be removed?
|
|
731
|
+
- **Orphaned imports**: Does the old import still appear even though the imported name is no longer used?
|
|
732
|
+
- **Stale comments**: Do comments near the change site still reference the old name, old behavior, or old config?
|
|
733
|
+
- **Config residue**: Are there default values, schema entries, or env var documentation for the removed identifier?
|
|
734
|
+
- **Test residue**: Do test fixtures still set removed flags, call removed functions, or import removed modules?
|
|
735
|
+
|
|
736
|
+
### 4. Verify intent for each finding
|
|
737
|
+
|
|
738
|
+
A straggler may be intentional:
|
|
739
|
+
- **Backward compatibility shim**: The old name is kept for external callers. Check git history and comments for evidence.
|
|
740
|
+
- **Deprecated module**: Scheduled for removal in a future PR. Check for TODO ($ticket) markers.
|
|
741
|
+
- **Separate cleanup PR**: The removal was split across PRs. Check the project context brief for deferred work.
|
|
742
|
+
|
|
743
|
+
Use the intent verification steps (trace callers, check git history, check memories) before reporting. If the straggler is intentional or deferred, do not report it as a finding.
|
|
744
|
+
|
|
745
|
+
## What is NOT a finding
|
|
746
|
+
|
|
747
|
+
- **Substring matches**: \`git grep "config"\` matching \`oldConfigValue\` when the marked identifier was \`Config\` (the class). Use word boundaries.
|
|
748
|
+
- **Homonyms**: The same word used in an unrelated context (e.g., \`process\` the function vs. "process" in documentation prose).
|
|
749
|
+
- **Different namespace**: An identifier with the same name in a different module or package that was not part of the change.
|
|
750
|
+
- **Intentional shims**: Backward compatibility wrappers with documented purpose or comments explaining why the old name is retained.
|
|
751
|
+
- **Deferred cleanup**: Marked with a TODO ($ticket) referencing an open ticket.
|
|
752
|
+
- **Vendored or generated code**: References in third-party or generated files that the project does not maintain.
|
|
753
|
+
|
|
754
|
+
## Worked example
|
|
755
|
+
|
|
756
|
+
**Change**: PR removes the \`USE_NEW_VALIDATION\` feature flag. The diff shows \`config.ts\` (flag definition deleted) and \`validation.ts\` (conditional \`if USE_NEW_VALIDATION\` removed, new path is now unconditional).
|
|
757
|
+
|
|
758
|
+
**Mark**: \`USE_NEW_VALIDATION\`
|
|
759
|
+
|
|
760
|
+
**Sweep**: \`git grep -n "USE_NEW_VALIDATION"\` returns hits in:
|
|
761
|
+
- \`src/config.ts\` (changed — expected, the diff removed it here)
|
|
762
|
+
- \`tests/validation.test.ts\` (unchanged — STRAGGLER: test still sets the flag in a fixture)
|
|
763
|
+
- \`docs/config.md\` (unchanged — STRAGGLER: docs still describe the flag)
|
|
764
|
+
- \`.env.example\` (unchanged — CONFIG_RESIDUE: env var example still present)
|
|
765
|
+
|
|
766
|
+
**Touchpoint neatness**: In \`validation.ts\`, the diff removed the \`if USE_NEW_VALIDATION { ... } else { oldValidation() }\` conditional, but \`oldValidation()\` was left as a now-unreachable function (DEAD_BRANCH). The comment above it still says "fallback when USE_NEW_VALIDATION is disabled" (STALE_COMMENT).
|
|
767
|
+
|
|
768
|
+
**Findings**: 4 findings (2 STRAGGLER, 1 CONFIG_RESIDUE, 1 DEAD_BRANCH) plus 1 STALE_COMMENT at the touchpoint.
|
|
769
|
+
|
|
770
|
+
## Category taxonomy
|
|
771
|
+
|
|
772
|
+
- **STRAGGLER**: Old identifier found in a file the diff did not touch — the sweep missed this occurrence
|
|
773
|
+
- **DEAD_BRANCH**: Code path now unreachable because its guard, flag, or condition was removed but the branch was not collapsed
|
|
774
|
+
- **STALE_COMMENT**: Comment references the old identifier, old behavior, or old config that no longer exists
|
|
775
|
+
- **ORPHANED_IMPORT**: Import of the old module or function remains but is no longer used after the change
|
|
776
|
+
- **CONFIG_RESIDUE**: Config keys, defaults, schema entries, or env var docs for a removed identifier
|
|
777
|
+
- **TEST_RESIDUE**: Test fixtures or test cases that reference removed identifiers
|
|
778
|
+
|
|
779
|
+
For straggler findings, the source of truth is the diff (the identifier was removed) and the grep hit (the identifier still exists elsewhere). Use "N/A — mechanical finding" for the producer chain.
|
|
780
|
+
`;
|
|
781
|
+
|
|
655
782
|
// ---------------------------------------------------------------------------
|
|
656
783
|
// Review synthesizer — verify, deduplicate, and aggregate specialist findings
|
|
657
784
|
// ---------------------------------------------------------------------------
|
|
@@ -661,7 +788,7 @@ name: thatch-review-synthesizer
|
|
|
661
788
|
description: Verify and synthesize findings from multiple review specialist skills into a single deduplicated, severity-grouped report. Use after running one or more thatch-review-* specialist skills.
|
|
662
789
|
---
|
|
663
790
|
|
|
664
|
-
You are a review synthesizer. You have received findings from one or more review specialists (pedantic, acceptance, state-flow, no-slop, breadcrumbs). Your job is to verify their citations against the actual code, deduplicate across specialists, and produce a single, coherent final report.
|
|
791
|
+
You are a review synthesizer. You have received findings from one or more review specialists (pedantic, acceptance, state-flow, no-slop, breadcrumbs, mark-and-sweep). Your job is to verify their citations against the actual code, deduplicate across specialists, and produce a single, coherent final report.
|
|
665
792
|
|
|
666
793
|
## Static analysis only
|
|
667
794
|
You review code by reading it. Do NOT run tests, linters, compilers, or any build commands.
|
|
@@ -1089,7 +1216,7 @@ Dispatch a sub-agent using the Task tool to research the code workflows affected
|
|
|
1089
1216
|
|
|
1090
1217
|
The sub-agent produces a code guide with one section per affected workflow or feature. Wait for it to complete and collect the guide.
|
|
1091
1218
|
|
|
1092
|
-
This guide gives specialists the code-level context (purpose, data flow, evolutions, constraints) they need to avoid false positives about intentional behavior and long-standing design decisions, and reduces duplicate code-tracing across the
|
|
1219
|
+
This guide gives specialists the code-level context (purpose, data flow, evolutions, constraints) they need to avoid false positives about intentional behavior and long-standing design decisions, and reduces duplicate code-tracing across the six specialist lenses.
|
|
1093
1220
|
|
|
1094
1221
|
For small changes (a single file or trivial diff), you may do this research inline instead of dispatching a sub-agent. Use your judgment: if the workflow research would require reading more than 3-4 files, dispatch a sub-agent to preserve your context for orchestration.
|
|
1095
1222
|
|
|
@@ -1119,7 +1246,7 @@ For changes estimated at 3 points or fewer, skip partitioning — dispatch one s
|
|
|
1119
1246
|
|
|
1120
1247
|
## Step 6: Dispatch specialist sub-agents
|
|
1121
1248
|
|
|
1122
|
-
For each review unit, dispatch sub-agents using the Task tool. Each sub-agent runs one specialist lens on one review unit. The
|
|
1249
|
+
For each review unit, dispatch sub-agents using the Task tool. Each sub-agent runs one specialist lens on one review unit. The six specialists are:
|
|
1123
1250
|
|
|
1124
1251
|
1. **Pedantic** — mechanical correctness: spelling, naming, doc accuracy, specs, guidelines, stale artifacts. Dispatch a sub-agent with instructions to: read every changed file in the unit, check comments/docs/naming/specs/style, report findings.
|
|
1125
1252
|
|
|
@@ -1131,6 +1258,8 @@ For each review unit, dispatch sub-agents using the Task tool. Each sub-agent ru
|
|
|
1131
1258
|
|
|
1132
1259
|
5. **Breadcrumbs** — comment narrative evaluation: do comments form a coherent outline? Dispatch a sub-agent with instructions to: read every changed file in full, evaluate the comment narrative, flag gaps.
|
|
1133
1260
|
|
|
1261
|
+
6. **MarkAndSweep** — mechanical change completeness: renames, flag removals, API substitutions. Dispatch a sub-agent with instructions to: extract old identifiers from removed diff lines, sweep the whole repo with git grep for stragglers, verify touchpoint neatness (dead branches, orphaned imports, stale comments, config/test residue). Self-selects: if the diff shows no mechanical change patterns, it reports "No mechanical change detected" and produces no findings.
|
|
1262
|
+
|
|
1134
1263
|
For the integration review unit (5+ points), dispatch a sub-agent focused on:
|
|
1135
1264
|
- Cross-component contracts — do the interfaces between components match?
|
|
1136
1265
|
- Boundary correctness — race conditions, ordering dependencies, shared state issues at boundaries.
|
|
@@ -1155,7 +1284,7 @@ Alternatively, perform the synthesis yourself:
|
|
|
1155
1284
|
When dispatching each sub-agent, include in the prompt:
|
|
1156
1285
|
- The git range to review
|
|
1157
1286
|
- The specific files in this unit's scope
|
|
1158
|
-
- The specialist focus (from the
|
|
1287
|
+
- The specialist focus (from the six specialists above)
|
|
1159
1288
|
- The diff stat for this unit's files
|
|
1160
1289
|
- **The project context brief** from Step 2, filtered to what is relevant to this unit's scope. Explicitly list any deferred work that falls within this unit's files, and call out TODO ($ticket) markers the specialists should recognize as intentional.
|
|
1161
1290
|
- **The workflow guide** from Step 3, filtered to the workflows relevant to this unit's scope. Use it to understand the purpose and evolution of the code before flagging issues. It provides the code-level context (flows, contracts, history, constraints) that prevents false positives about intentional behavior and long-standing design decisions.
|
|
@@ -1180,6 +1309,7 @@ const SHARED_SKILLS: SkillDef[] = [
|
|
|
1180
1309
|
{ name: "thatch-review-state-flow", content: REVIEW_STATE_FLOW },
|
|
1181
1310
|
{ name: "thatch-review-no-slop", content: REVIEW_NO_SLOP },
|
|
1182
1311
|
{ name: "thatch-review-breadcrumbs", content: REVIEW_BREADCRUMBS },
|
|
1312
|
+
{ name: "thatch-review-mark-and-sweep", content: REVIEW_MARK_AND_SWEEP },
|
|
1183
1313
|
{ name: "thatch-review-synthesizer", content: REVIEW_SYNTHESIZER },
|
|
1184
1314
|
{ name: "thatch-review-context", content: REVIEW_CONTEXT },
|
|
1185
1315
|
{ name: "thatch-workflow-research", content: WORKFLOW_RESEARCH },
|
package/src/tool-defs.ts
CHANGED
|
@@ -40,6 +40,7 @@ function formatEntry(
|
|
|
40
40
|
let meta = `[${entry.store}]`;
|
|
41
41
|
if (entry.branch) meta += ` branch:${entry.branch}`;
|
|
42
42
|
if (entry.confidence) meta += ` confidence:${entry.confidence}`;
|
|
43
|
+
if (entry.archived) meta += " archived:true";
|
|
43
44
|
parts.push(meta, "", entry.content);
|
|
44
45
|
return parts.join("\n");
|
|
45
46
|
}
|
|
@@ -48,6 +49,7 @@ function formatRecallResult(entry: any): string {
|
|
|
48
49
|
let meta = `store:${entry.store}`;
|
|
49
50
|
if (entry.branch) meta += ` branch:${entry.branch}`;
|
|
50
51
|
if (entry.confidence) meta += ` confidence:${entry.confidence}`;
|
|
52
|
+
if (entry.archived) meta += " archived:true";
|
|
51
53
|
const score = entry._score.toFixed(3);
|
|
52
54
|
return `[${meta}] [score:${score}]\n${entry.content}`;
|
|
53
55
|
}
|
|
@@ -118,6 +120,11 @@ const rememberDef: ToolDef = {
|
|
|
118
120
|
overwrite: z.boolean().optional().describe(
|
|
119
121
|
"Set to true to replace an existing memory with the same label.",
|
|
120
122
|
),
|
|
123
|
+
archived: z.boolean().optional().describe(
|
|
124
|
+
"Mark this memory as a stable, long-term record that should not trigger hygiene nudges. " +
|
|
125
|
+
"When updating an already-archived memory, this param is REQUIRED — " +
|
|
126
|
+
"pass archived: true to keep it archived, or archived: false to unarchive.",
|
|
127
|
+
),
|
|
121
128
|
},
|
|
122
129
|
async execute(args, ctx) {
|
|
123
130
|
const store = (args.store as string) || ctx.defaultStore;
|
|
@@ -125,9 +132,6 @@ const rememberDef: ToolDef = {
|
|
|
125
132
|
const content = `# ${label}\n\n${args.content as string}`;
|
|
126
133
|
const embedding = await ctx.model.passageEmbed(content);
|
|
127
134
|
|
|
128
|
-
// Write-time collision check: the embedding is already computed, so
|
|
129
|
-
// scanning the store is nearly free. The save proceeds regardless —
|
|
130
|
-
// the agent decides whether and how to reconcile.
|
|
131
135
|
const similar = ctx.db.findSimilar(store, embedding, {
|
|
132
136
|
excludeSlug: ctx.db.slugify(label),
|
|
133
137
|
});
|
|
@@ -136,6 +140,7 @@ const rememberDef: ToolDef = {
|
|
|
136
140
|
branch: args.branch as string | undefined,
|
|
137
141
|
confidence: args.confidence as number | undefined,
|
|
138
142
|
overwrite: args.overwrite as boolean | undefined,
|
|
143
|
+
archived: args.archived as boolean | undefined,
|
|
139
144
|
});
|
|
140
145
|
|
|
141
146
|
if (!result.ok) return result.error;
|
|
@@ -172,6 +177,9 @@ const recallDef: ToolDef = {
|
|
|
172
177
|
limit: z.number().int().min(1).max(20).optional().describe(
|
|
173
178
|
"Maximum number of results. Default 10.",
|
|
174
179
|
),
|
|
180
|
+
includeArchived: z.boolean().optional().describe(
|
|
181
|
+
"Include archived memories in results. Default false — archived memories are excluded.",
|
|
182
|
+
),
|
|
175
183
|
},
|
|
176
184
|
async execute(args, ctx) {
|
|
177
185
|
const stores = args.store
|
|
@@ -183,6 +191,7 @@ const recallDef: ToolDef = {
|
|
|
183
191
|
const results = ctx.db.recall(stores, queryEmbedding, {
|
|
184
192
|
branch: args.branch as string | undefined,
|
|
185
193
|
limit,
|
|
194
|
+
includeArchived: args.includeArchived as boolean | undefined,
|
|
186
195
|
});
|
|
187
196
|
|
|
188
197
|
if (results.length === 0) return "No matching memories found.";
|
|
@@ -212,6 +221,7 @@ const listDef: ToolDef = {
|
|
|
212
221
|
let line = `[${store}] ${e.label}`;
|
|
213
222
|
if (e.branch) line += ` (branch:${e.branch})`;
|
|
214
223
|
if (e.confidence) line += ` (c:${e.confidence})`;
|
|
224
|
+
if (e.archived) line += " (archived)";
|
|
215
225
|
line += ` (${e.updated_at})`;
|
|
216
226
|
return line;
|
|
217
227
|
})
|