@jeffober/thatch 0.1.26 → 0.1.28
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/README.md +7 -7
- package/artifacts/skills/common.md +1 -1
- package/artifacts/skills/thatch-change-walkthrough.md +2 -2
- package/artifacts/skills/thatch-code-archaeology.md +252 -0
- package/artifacts/skills/thatch-code-review.md +4 -4
- package/artifacts/skills/thatch-code-walkthrough.md +2 -2
- package/artifacts/skills/thatch-coding-workflow.md +79 -0
- package/artifacts/skills/thatch-fact-extractor.md +2 -2
- package/artifacts/skills/{pr-description.md → thatch-pr-description.md} +1 -1
- package/artifacts/skills/thatch-review-context.md +9 -0
- package/artifacts/skills/thatch-review-followup.md +29 -1
- package/artifacts/skills/thatch-review-pedantic.md +16 -1
- package/artifacts/skills/thatch-review-response.md +365 -0
- package/artifacts/skills/thatch-review-synthesizer.md +92 -5
- package/artifacts/skills/{split-overlarge-pr.md → thatch-split-overlarge-pr.md} +1 -1
- package/artifacts/skills/{ticket-description.md → thatch-ticket-description.md} +3 -3
- package/bin/release +3 -3
- package/bin/thatch +11 -10
- package/docs/dev/README.md +7 -6
- package/docs/dev/mcp-parity.md +1 -1
- package/docs/dev/skills.md +29 -9
- package/docs/plans/README.md +9 -6
- package/docs/plans/intuition-drives.md +147 -0
- package/docs/plans/nomenclater.md +89 -0
- package/docs/plans/prediction-consolidation.md +361 -0
- package/docs/qa/README.md +20 -3
- package/docs/user/README.md +19 -5
- package/package.json +1 -1
- package/src/db.ts +6 -6
- package/src/embeddings.ts +2 -2
- package/src/extract-queue.ts +4 -4
- package/src/extraction.ts +4 -4
- package/src/git.ts +1 -1
- package/src/hygiene.ts +1 -1
- package/src/index.ts +40 -34
- package/src/mcp.ts +34 -6
- package/src/prompts.ts +96 -85
- package/src/setup.ts +19 -19
- package/src/sideband.ts +3 -3
- package/src/skills.ts +104 -6
- package/src/tool-defs.ts +93 -14
- package/src/tools.ts +12 -2
- package/artifacts/skills/thatch-workflow-research.md +0 -91
- package/docs/in-progress/prediction-engine.md +0 -404
- package/docs/plans/001-memory-store.md +0 -106
- package/docs/plans/002-agent-driven-pipelines.md +0 -59
- package/docs/plans/003-memory-hygiene.md +0 -61
- package/docs/qa/use-cases/README.md +0 -51
- package/docs/qa/use-cases/UC-001-memory-roundtrip.md +0 -20
- package/docs/qa/use-cases/UC-002-dedup-cycle.md +0 -23
- package/docs/qa/use-cases/UC-003-extraction-nudge.md +0 -56
- package/docs/qa/use-cases/UC-004-cli-inspection.md +0 -22
- package/docs/qa/use-cases/UC-005-setup-install.md +0 -34
- package/docs/qa/use-cases/UC-006-cursor-hook-lifecycle.md +0 -32
- package/docs/qa/use-cases/UC-007-recall-nudge.md +0 -31
- package/docs/qa/use-cases/UC-008-hygiene-heartbeat.md +0 -26
- package/docs/qa/use-cases/UC-009-flush-tools-tiers.md +0 -33
- package/docs/qa/use-cases/UC-010-prime.md +0 -25
- package/docs/qa/use-cases/UC-011-write-time-similarity-warning.md +0 -24
- package/docs/qa/use-cases/UC-012-model-migration.md +0 -28
- package/docs/qa/use-cases/UC-013-compaction-context.md +0 -25
- package/docs/qa/use-cases/UC-014-skill-install-drift.md +0 -28
- package/docs/qa/use-cases/UC-015-env-override-matrix.md +0 -28
- package/docs/qa/use-cases/UC-016-concurrent-session-isolation.md +0 -37
- package/docs/qa/use-cases/UC-017-buffer-tool-vs-buffer-batch.md +0 -27
- package/docs/qa/use-cases/UC-018-mcp-startup-setup-detection.md +0 -38
- package/docs/qa/use-cases/UC-019-archived-memory-lifecycle.md +0 -36
- package/docs/qa/use-cases/UC-020-extraction-escalation.md +0 -52
- package/docs/qa/use-cases/UC-021-prediction-autofire.md +0 -21
- package/docs/qa/use-cases/UC-022-prediction-dedup.md +0 -19
- package/docs/qa/use-cases/UC-023-prediction-confidence-model.md +0 -23
- package/docs/qa/use-cases/UC-024-prediction-delete.md +0 -20
package/src/skills.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
1
|
+
import { mkdirSync, readdirSync, readFileSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
+
import pkg from "../package.json";
|
|
3
4
|
|
|
4
5
|
export interface SkillFile {
|
|
5
6
|
name: string;
|
|
@@ -42,14 +43,16 @@ function loadSharedSkills(): SkillDef[] {
|
|
|
42
43
|
"thatch-review-highlights",
|
|
43
44
|
"thatch-review-synthesizer",
|
|
44
45
|
"thatch-review-context",
|
|
45
|
-
"thatch-
|
|
46
|
+
"thatch-code-archaeology",
|
|
46
47
|
"thatch-review-followup",
|
|
47
48
|
"thatch-change-walkthrough",
|
|
48
49
|
"thatch-code-walkthrough",
|
|
49
50
|
"thatch-session-reflection",
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
51
|
+
"thatch-coding-workflow",
|
|
52
|
+
"thatch-pr-description",
|
|
53
|
+
"thatch-ticket-description",
|
|
54
|
+
"thatch-split-overlarge-pr",
|
|
55
|
+
"thatch-review-response",
|
|
53
56
|
];
|
|
54
57
|
return names.map((name) => ({ name, content: loadSkillFile(name) }));
|
|
55
58
|
}
|
|
@@ -63,11 +66,106 @@ function loadOpencodeOnlySkills(): SkillDef[] {
|
|
|
63
66
|
export const SHARED_SKILLS: SkillDef[] = loadSharedSkills();
|
|
64
67
|
export const OPENCODE_ONLY_SKILLS: SkillDef[] = loadOpencodeOnlySkills();
|
|
65
68
|
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
// Stale skill cleanup - runs before install on every skillsDir
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
// Skills renamed from non-prefixed to thatch-prefixed in v0.1.27. The old
|
|
74
|
+
// names are cleaned up during install for a limited version window because
|
|
75
|
+
// non-prefixed names could be adopted by third parties after we abandon them.
|
|
76
|
+
// After RENAME_MIGRATION_MAX_VERSION, the cleanup stops and leftover stale
|
|
77
|
+
// files are the user's responsibility.
|
|
78
|
+
const RENAMED_SKILLS = new Set([
|
|
79
|
+
"pr-description",
|
|
80
|
+
"ticket-description",
|
|
81
|
+
"split-overlarge-pr",
|
|
82
|
+
]);
|
|
83
|
+
|
|
84
|
+
// Last version that ships the non-prefixed-to-prefixed migration cleanup.
|
|
85
|
+
// Users who upgrade to this version or earlier get automatic cleanup of the
|
|
86
|
+
// old skill directories. Users who skip past this version must manually
|
|
87
|
+
// delete the stale directories.
|
|
88
|
+
const RENAME_MIGRATION_MAX_VERSION = "0.1.35";
|
|
89
|
+
|
|
90
|
+
function compareVersions(a: string, b: string): number {
|
|
91
|
+
const [aMaj, aMin, aPatch] = a.split(".").map(Number);
|
|
92
|
+
const [bMaj, bMin, bPatch] = b.split(".").map(Number);
|
|
93
|
+
if (aMaj !== bMaj) return aMaj - bMaj;
|
|
94
|
+
if (aMin !== bMin) return aMin - bMin;
|
|
95
|
+
return aPatch - bPatch;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Remove stale skill directories before installing the current set.
|
|
100
|
+
*
|
|
101
|
+
* Two cleanup rules:
|
|
102
|
+
*
|
|
103
|
+
* 1. Always: delete any `thatch-*` skill directory that is not in the current
|
|
104
|
+
* install set. The `thatch-` prefix is our namespace - no third party should
|
|
105
|
+
* use it, so unconditional removal is safe. This catches skills we've
|
|
106
|
+
* renamed or removed in any release.
|
|
107
|
+
*
|
|
108
|
+
* 2. Version-gated: delete directories matching old non-prefixed skill names
|
|
109
|
+
* (pr-description, ticket-description, split-overlarge-pr) through
|
|
110
|
+
* RENAME_MIGRATION_MAX_VERSION. These names lack the thatch- prefix and
|
|
111
|
+
* could be adopted by third parties after we abandon them, so the cleanup
|
|
112
|
+
* is time-boxed. Users who upgrade past the window must manually clean up.
|
|
113
|
+
*/
|
|
114
|
+
function cleanupStaleSkills(skillsDir: string, currentNames: string[]): void {
|
|
115
|
+
const currentSet = new Set(currentNames);
|
|
116
|
+
let entries;
|
|
117
|
+
try {
|
|
118
|
+
entries = readdirSync(skillsDir, { withFileTypes: true });
|
|
119
|
+
} catch {
|
|
120
|
+
return; // dir doesn't exist or isn't readable - nothing to clean
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const withinMigrationWindow =
|
|
124
|
+
compareVersions(pkg.version, RENAME_MIGRATION_MAX_VERSION) <= 0;
|
|
125
|
+
|
|
126
|
+
for (const entry of entries) {
|
|
127
|
+
// isDirectory() returns false for symlinks (even symlinked dirs), so
|
|
128
|
+
// check isSymbolicLink() too - a symlinked skill dir is just as stale
|
|
129
|
+
// as a regular one.
|
|
130
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
131
|
+
const name = entry.name;
|
|
132
|
+
const fullPath = join(skillsDir, name);
|
|
133
|
+
|
|
134
|
+
// Remove a stale entry. For symlinks, unlink the symlink itself - never
|
|
135
|
+
// follow the link, which could delete the target (e.g. a shared skill
|
|
136
|
+
// directory the user still wants). For regular dirs, recursive rm.
|
|
137
|
+
const remove = () => {
|
|
138
|
+
try {
|
|
139
|
+
if (entry.isSymbolicLink()) {
|
|
140
|
+
unlinkSync(fullPath);
|
|
141
|
+
} else {
|
|
142
|
+
rmSync(fullPath, { recursive: true, force: true });
|
|
143
|
+
}
|
|
144
|
+
} catch {
|
|
145
|
+
// best-effort - don't block install on cleanup failure
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// Rule 1: stale thatch-* dirs (our namespace, always safe to remove).
|
|
150
|
+
if (name.startsWith("thatch-") && !currentSet.has(name)) {
|
|
151
|
+
remove();
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Rule 2: renamed non-prefixed dirs (version-gated, third-party risk).
|
|
156
|
+
if (RENAMED_SKILLS.has(name) && withinMigrationWindow) {
|
|
157
|
+
remove();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
66
162
|
export function installSkills(
|
|
67
163
|
skillsDir: string,
|
|
68
164
|
skills: SkillDef[] = SHARED_SKILLS,
|
|
69
165
|
): SkillFile[] {
|
|
70
166
|
mkdirSync(skillsDir, { recursive: true });
|
|
167
|
+
cleanupStaleSkills(skillsDir, skills.map((s) => s.name));
|
|
168
|
+
|
|
71
169
|
const written: SkillFile[] = [];
|
|
72
170
|
|
|
73
171
|
for (const skill of skills) {
|
|
@@ -78,7 +176,7 @@ export function installSkills(
|
|
|
78
176
|
try {
|
|
79
177
|
current = readFileSync(file, "utf8");
|
|
80
178
|
} catch {
|
|
81
|
-
// missing file
|
|
179
|
+
// missing file - first install
|
|
82
180
|
}
|
|
83
181
|
|
|
84
182
|
if (current !== skill.content) {
|
package/src/tool-defs.ts
CHANGED
|
@@ -14,7 +14,7 @@ const PREDICTION_DEDUP_COSINE = 0.85;
|
|
|
14
14
|
const PREDICTION_QUERY_THRESHOLD = 0.45;
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* Shared context passed to every tool's execute function. Framework-agnostic
|
|
17
|
+
* Shared context passed to every tool's execute function. Framework-agnostic -
|
|
18
18
|
* neither opencode nor MCP specific. The plugin wires real defaults; tests and
|
|
19
19
|
* the MCP server inject whatever they need.
|
|
20
20
|
*/
|
|
@@ -22,10 +22,22 @@ export interface CoreContext {
|
|
|
22
22
|
db: ThatchDB;
|
|
23
23
|
model: EmbeddingModel;
|
|
24
24
|
defaultStore: string;
|
|
25
|
+
/** Peeks the extraction buffer for a session and returns the serialized
|
|
26
|
+
* JSON payload (same shape as buildExtractionPayload). Returns null when
|
|
27
|
+
* no interactions are queued. Used by the get_extraction_payload tool so
|
|
28
|
+
* the sub-agent fetches the payload as a tool response instead of
|
|
29
|
+
* receiving it inline in the nudge text. */
|
|
30
|
+
extractionPayloadProvider?: (sessionID: string) => string | null;
|
|
31
|
+
/** Drains a session's extraction queue. On the MCP path this deletes the
|
|
32
|
+
* file-backed queue and resets the missed-nudge counter. On the opencode
|
|
33
|
+
* path this is unused (the tool.execute.after hook handles drain via
|
|
34
|
+
* childToParent). Called by extraction_done when the sub-agent passes
|
|
35
|
+
* the parent's session_id. */
|
|
36
|
+
drainExtractionQueue?: (sessionID: string) => void;
|
|
25
37
|
}
|
|
26
38
|
|
|
27
39
|
/**
|
|
28
|
-
* A tool definition
|
|
40
|
+
* A tool definition - the single source of truth shared by the opencode plugin
|
|
29
41
|
* wrapper (tools.ts) and the MCP server (mcp.ts). The `args` field is a ZodRawShape
|
|
30
42
|
* (a plain object of Zod types), which opencode's `tool()` accepts directly and
|
|
31
43
|
* the MCP server wraps in `z.object()` for validation and `z.toJSONSchema()` for
|
|
@@ -39,7 +51,7 @@ export interface ToolDef {
|
|
|
39
51
|
}
|
|
40
52
|
|
|
41
53
|
// ---------------------------------------------------------------------------
|
|
42
|
-
// Formatting helpers
|
|
54
|
+
// Formatting helpers - shared by all tools that render entries
|
|
43
55
|
// ---------------------------------------------------------------------------
|
|
44
56
|
|
|
45
57
|
function formatEntry(
|
|
@@ -68,7 +80,7 @@ function formatRecallResult(entry: any): string {
|
|
|
68
80
|
/**
|
|
69
81
|
* Groups candidate pairs into connected components over the similarity graph,
|
|
70
82
|
* so a topic fragmented across N entries reads as one cluster instead of
|
|
71
|
-
* O(N²) pairs. Verdicts stay pairwise (markChecked)
|
|
83
|
+
* O(N²) pairs. Verdicts stay pairwise (markChecked) - this is presentation only.
|
|
72
84
|
*/
|
|
73
85
|
function renderClusters(candidates: DedupCandidate[]): string {
|
|
74
86
|
const parent = new Map<string, string>();
|
|
@@ -114,7 +126,7 @@ const rememberDef: ToolDef = {
|
|
|
114
126
|
"reference material for a future instance of yourself with zero context.",
|
|
115
127
|
args: {
|
|
116
128
|
label: z.string().describe(
|
|
117
|
-
"Short descriptive title. Used for deduplication
|
|
129
|
+
"Short descriptive title. Used for deduplication - same label in the same store is the same entry.",
|
|
118
130
|
),
|
|
119
131
|
content: z.string().describe(
|
|
120
132
|
"The information to remember. Self-contained, understandable without session context.",
|
|
@@ -133,7 +145,7 @@ const rememberDef: ToolDef = {
|
|
|
133
145
|
),
|
|
134
146
|
archived: z.boolean().optional().describe(
|
|
135
147
|
"Mark this memory as a stable, long-term record that should not trigger hygiene nudges. " +
|
|
136
|
-
"When updating an already-archived memory, this param is REQUIRED
|
|
148
|
+
"When updating an already-archived memory, this param is REQUIRED - " +
|
|
137
149
|
"pass archived: true to keep it archived, or archived: false to unarchive.",
|
|
138
150
|
),
|
|
139
151
|
},
|
|
@@ -189,7 +201,7 @@ const recallDef: ToolDef = {
|
|
|
189
201
|
"Maximum number of results. Default 10.",
|
|
190
202
|
),
|
|
191
203
|
includeArchived: z.boolean().optional().describe(
|
|
192
|
-
"Include archived memories in results. Default false
|
|
204
|
+
"Include archived memories in results. Default false - archived memories are excluded.",
|
|
193
205
|
),
|
|
194
206
|
},
|
|
195
207
|
async execute(args, ctx) {
|
|
@@ -299,7 +311,7 @@ const findDuplicatesDef: ToolDef = {
|
|
|
299
311
|
description:
|
|
300
312
|
"Find memories with unusually similar content that may be candidates " +
|
|
301
313
|
"for consolidation. Uses cosine similarity on embeddings. Related pairs " +
|
|
302
|
-
"are grouped into clusters
|
|
314
|
+
"are grouped into clusters - a cluster of three or more usually means " +
|
|
303
315
|
"one topic fragmented across entries that should be consolidated into " +
|
|
304
316
|
"a single memory. Pairs already reviewed via dedup_mark_checked " +
|
|
305
317
|
"are skipped.",
|
|
@@ -357,19 +369,74 @@ const markCheckedDef: ToolDef = {
|
|
|
357
369
|
* Called in a PARENT session after dispatching the fact-extractor, it accepts
|
|
358
370
|
* the buffer: entries move to a holding area and the nudge quiets, but they
|
|
359
371
|
* are not dropped until the extractor completes. Called in a CHILD extractor
|
|
360
|
-
* at the end of its run, it completes the parent's accepted entries
|
|
372
|
+
* at the end of its run, it completes the parent's accepted entries -
|
|
361
373
|
* including no-save runs that write no memory. If the child errors or is
|
|
362
374
|
* deleted before either signal, the host requeues the entries so the facts
|
|
363
375
|
* are not lost.
|
|
364
376
|
*
|
|
365
377
|
* The actual state changes happen in the host's post-tool hook
|
|
366
|
-
* (tool.execute.after for opencode, PostToolBatch/appendBatch for MCP)
|
|
378
|
+
* (tool.execute.after for opencode, PostToolBatch/appendBatch for MCP) -
|
|
367
379
|
* this tool's execute function is a no-op confirmation. The tool exists so
|
|
368
380
|
* the model has a recognizable tool name to key on. In the MCP path the
|
|
369
381
|
* file-backed queue is consumed on this call (or on any memory_remember),
|
|
370
382
|
* which is durable across interruption because the queue persists on disk
|
|
371
383
|
* until then.
|
|
372
384
|
*/
|
|
385
|
+
/**
|
|
386
|
+
* Fetches the queued extraction payload for a session. The sub-agent calls
|
|
387
|
+
* this with the parent's session ID to retrieve the tool interactions that
|
|
388
|
+
* need fact extraction, instead of receiving them inline in the nudge text.
|
|
389
|
+
* This keeps the full payload out of the main session's context window.
|
|
390
|
+
*/
|
|
391
|
+
const getExtractionPayloadDef: ToolDef = {
|
|
392
|
+
name: "get_extraction_payload",
|
|
393
|
+
description:
|
|
394
|
+
"Retrieve the queued tool interactions for extraction. Call this with " +
|
|
395
|
+
"the session_id from the extraction nudge to get the JSON payload " +
|
|
396
|
+
"(interactions, projectStore, globalStore). Then run the " +
|
|
397
|
+
"thatch-fact-extractor skill on the returned payload.",
|
|
398
|
+
args: {
|
|
399
|
+
session_id: z.string().describe(
|
|
400
|
+
"The session ID from the extraction nudge. This is the parent session " +
|
|
401
|
+
"whose tool interactions are queued for extraction.",
|
|
402
|
+
),
|
|
403
|
+
},
|
|
404
|
+
async execute(args, ctx) {
|
|
405
|
+
if (!ctx.extractionPayloadProvider) {
|
|
406
|
+
return "Extraction payload retrieval is not available in this host.";
|
|
407
|
+
}
|
|
408
|
+
const payload = ctx.extractionPayloadProvider(args.session_id as string);
|
|
409
|
+
if (!payload) {
|
|
410
|
+
return "No queued tool interactions found for this session.";
|
|
411
|
+
}
|
|
412
|
+
return payload;
|
|
413
|
+
},
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Extraction-buffer acknowledgment, with AMQP-style accept/complete roles.
|
|
418
|
+
*
|
|
419
|
+
* Called in a PARENT session after dispatching the fact-extractor, it accepts
|
|
420
|
+
* the buffer: entries move to a holding area and the nudge quiets, but they
|
|
421
|
+
* are not dropped until the extractor completes. Called in a CHILD extractor
|
|
422
|
+
* at the end of its run, it completes the parent's accepted entries -
|
|
423
|
+
* including no-save runs that write no memory. If the child errors or is
|
|
424
|
+
* deleted before either signal, the host requeues the entries so the facts
|
|
425
|
+
* are not lost.
|
|
426
|
+
*
|
|
427
|
+
* The actual state changes happen in the host's post-tool hook
|
|
428
|
+
* (tool.execute.after for opencode, PostToolBatch/appendBatch for MCP) -
|
|
429
|
+
* this tool's execute function is a no-op confirmation. The tool exists so
|
|
430
|
+
* the model has a recognizable tool name to key on. In the MCP path the
|
|
431
|
+
* file-backed queue is consumed on this call (or on any memory_remember),
|
|
432
|
+
* which is durable across interruption because the queue persists on disk
|
|
433
|
+
* until then.
|
|
434
|
+
*
|
|
435
|
+
* The optional session_id parameter lets a sub-agent drain the PARENT's
|
|
436
|
+
* file-backed queue on the MCP path, where the sub-agent's session ID
|
|
437
|
+
* differs from the parent's and appendBatch's self-detection would drain
|
|
438
|
+
* the wrong (empty) queue.
|
|
439
|
+
*/
|
|
373
440
|
const extractionDoneDef: ToolDef = {
|
|
374
441
|
name: "extraction_done",
|
|
375
442
|
description:
|
|
@@ -377,9 +444,20 @@ const extractionDoneDef: ToolDef = {
|
|
|
377
444
|
"dispatching the fact-extractor to a sub-agent: accepts the buffer " +
|
|
378
445
|
"(quiets the nudge) while keeping entries until the extractor completes. " +
|
|
379
446
|
"In the fact-extractor sub-agent, call at the end of the run to mark the " +
|
|
380
|
-
"entries complete, even when nothing was worth saving."
|
|
381
|
-
|
|
382
|
-
|
|
447
|
+
"entries complete, even when nothing was worth saving. Pass session_id " +
|
|
448
|
+
"when running as a sub-agent to drain the parent session's queue.",
|
|
449
|
+
args: {
|
|
450
|
+
session_id: z.string().optional().describe(
|
|
451
|
+
"The parent session's ID, when called from a sub-agent on the MCP " +
|
|
452
|
+
"path. Drains the parent's file-backed queue. Omit when called from " +
|
|
453
|
+
"the parent session itself (opencode path handles drain via hooks).",
|
|
454
|
+
),
|
|
455
|
+
},
|
|
456
|
+
async execute(args, ctx) {
|
|
457
|
+
const sessionID = args.session_id as string | undefined;
|
|
458
|
+
if (sessionID && ctx.drainExtractionQueue) {
|
|
459
|
+
ctx.drainExtractionQueue(sessionID);
|
|
460
|
+
}
|
|
383
461
|
return "[acknowledged]";
|
|
384
462
|
},
|
|
385
463
|
};
|
|
@@ -502,7 +580,7 @@ const predictionUpdateDef: ToolDef = {
|
|
|
502
580
|
|
|
503
581
|
// "create" on an existing prediction means the agent re-observed
|
|
504
582
|
// the same preference in a new context. Link the edge and record
|
|
505
|
-
// provenance, but do NOT adjust confidence
|
|
583
|
+
// provenance, but do NOT adjust confidence - "create" is
|
|
506
584
|
// confidence-neutral, not a disconfirm.
|
|
507
585
|
if (signal === "create") {
|
|
508
586
|
ctx.db.addProvenance(predictionId, "create", rationale);
|
|
@@ -587,6 +665,7 @@ export const TOOL_DEFS: ToolDef[] = [
|
|
|
587
665
|
findDuplicatesDef,
|
|
588
666
|
markCheckedDef,
|
|
589
667
|
extractionDoneDef,
|
|
668
|
+
getExtractionPayloadDef,
|
|
590
669
|
predictionQueryDef,
|
|
591
670
|
predictionUpdateDef,
|
|
592
671
|
predictionListDef,
|
package/src/tools.ts
CHANGED
|
@@ -6,15 +6,25 @@ import { TOOL_DEFS, type CoreContext } from "./tool-defs";
|
|
|
6
6
|
/**
|
|
7
7
|
* Builds the opencode tool map from shared tool definitions. Each definition
|
|
8
8
|
* in TOOL_DEFS is wrapped in opencode's `tool()` with a `thatch_` prefix
|
|
9
|
-
* on the name
|
|
9
|
+
* on the name - opencode uses the object key as the tool name, so the prefix
|
|
10
10
|
* lives here, not in the shared definitions.
|
|
11
11
|
*/
|
|
12
12
|
export function createTools(
|
|
13
13
|
db: ThatchDB,
|
|
14
14
|
model: EmbeddingModel,
|
|
15
15
|
defaultStore: string,
|
|
16
|
+
extensions?: {
|
|
17
|
+
extractionPayloadProvider?: CoreContext["extractionPayloadProvider"];
|
|
18
|
+
drainExtractionQueue?: CoreContext["drainExtractionQueue"];
|
|
19
|
+
},
|
|
16
20
|
): Record<string, ReturnType<typeof tool>> {
|
|
17
|
-
const ctx: CoreContext = {
|
|
21
|
+
const ctx: CoreContext = {
|
|
22
|
+
db,
|
|
23
|
+
model,
|
|
24
|
+
defaultStore,
|
|
25
|
+
extractionPayloadProvider: extensions?.extractionPayloadProvider,
|
|
26
|
+
drainExtractionQueue: extensions?.drainExtractionQueue,
|
|
27
|
+
};
|
|
18
28
|
|
|
19
29
|
const tools: Record<string, ReturnType<typeof tool>> = {};
|
|
20
30
|
for (const def of TOOL_DEFS) {
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: thatch-workflow-research
|
|
3
|
-
description: Research the code workflows and features affected by a change or planned change. Identifies affected workflows, reads code flows and comments, traces git history, recalls memories, and produces a guide to the code. Use before reviewing a change or planning new work in an unfamiliar area.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
You are a code workflow researcher. Your job is to identify the features and logical workflows affected by a change (or a planned change), do deep code archaeology on them, and produce a guide that gives other agents the context they need to review or plan effectively.
|
|
7
|
-
|
|
8
|
-
## Why this matters
|
|
9
|
-
|
|
10
|
-
A specialist reviewer without code-level context will misidentify intentional behavior as bugs, miss known design constraints, and duplicate the same code-tracing work across every specialist lens. A planner without code-level context will propose changes that conflict with existing flows. This skill produces the code guide that prevents both.
|
|
11
|
-
|
|
12
|
-
## Relationship to thatch-review-context
|
|
13
|
-
|
|
14
|
-
These two skills are complementary, not overlapping:
|
|
15
|
-
- **thatch-review-context** gathers project metadata: tickets, PRs, TODO markers, deferred work, issue-tracker context. It answers "what is this change trying to do and what is intentionally left out?"
|
|
16
|
-
- **thatch-workflow-research** (this skill) gathers code-level context: actual code flows, comments, git history, evolutions. It answers "how does the code being changed actually work, and how did it get that way?"
|
|
17
|
-
|
|
18
|
-
Use both before a review. The coordinator runs thatch-review-context in Step 2 and this skill in Step 3.
|
|
19
|
-
|
|
20
|
-
## Two modes
|
|
21
|
-
|
|
22
|
-
### Review mode (existing change)
|
|
23
|
-
Given a git range, branch, or PR:
|
|
24
|
-
1. Run `git diff --stat` on the resolved range to identify changed files.
|
|
25
|
-
2. For each changed file, read the diff to understand what is being modified.
|
|
26
|
-
3. From the changes, identify which features and business-logic workflows are touched. A single file may participate in multiple workflows; a single workflow may span multiple files.
|
|
27
|
-
|
|
28
|
-
### Planning mode (new change)
|
|
29
|
-
Given a description of work to be done:
|
|
30
|
-
1. Identify which areas of the codebase the planned change would affect.
|
|
31
|
-
2. Use grep/glob to find the files and modules that implement the relevant features.
|
|
32
|
-
3. From those files, identify which existing workflows the change will interact with or modify.
|
|
33
|
-
|
|
34
|
-
## Research process
|
|
35
|
-
|
|
36
|
-
For each identified workflow or feature, work through these steps. Not every step will yield information for every workflow. Skip steps that produce nothing rather than forcing a narrative.
|
|
37
|
-
|
|
38
|
-
### 1. Retrieve memories
|
|
39
|
-
Call `thatch_memory_recall` with queries like:
|
|
40
|
-
- The workflow or feature name
|
|
41
|
-
- The primary files or modules involved
|
|
42
|
-
- "architecture" or "design" or "convention" for the area
|
|
43
|
-
- Any gotchas or known issues in the area
|
|
44
|
-
|
|
45
|
-
### 2. Read documentation
|
|
46
|
-
- README, docs/, design docs, ADRs related to the workflow
|
|
47
|
-
- CLAUDE.md, AGENTS.md, CONTRIBUTING.md for conventions
|
|
48
|
-
- Any inline documentation at the module level
|
|
49
|
-
|
|
50
|
-
### 3. Read the code flow
|
|
51
|
-
Trace each workflow through the codebase. Do not just read the changed lines; read the full flow:
|
|
52
|
-
- Identify the entry point(s) for the workflow
|
|
53
|
-
- Follow the data path through the functions and modules involved
|
|
54
|
-
- Read the comments in the flow. Comments encode intention, rationale, and design decisions that are invisible from the code alone.
|
|
55
|
-
- Note key contracts: what does each function or module assume about its inputs? What does it guarantee about its outputs?
|
|
56
|
-
- Note implicit state machines: what states can the workflow be in? What transitions are guarded?
|
|
57
|
-
|
|
58
|
-
### 4. Read git history
|
|
59
|
-
Trace the evolution of the workflow through git:
|
|
60
|
-
- `git log --oneline -- <key files>` for the commit history of the files involved
|
|
61
|
-
- `git log -S "<function name>"` to find when key functions were introduced or changed
|
|
62
|
-
- `git blame` on critical sections to find when and why they were written
|
|
63
|
-
- `git log --all --oneline --grep="<feature name>"` to find related work on other branches
|
|
64
|
-
- Look for commit messages that explain design decisions, refactors, or bug fixes that shaped the workflow
|
|
65
|
-
|
|
66
|
-
Focus on *why* the code evolved, not just *when*. Major evolutions are: creation, significant refactors, behavior changes, bug fixes that changed the flow, and additions of new paths or states.
|
|
67
|
-
|
|
68
|
-
## Output: code guide
|
|
69
|
-
|
|
70
|
-
Produce a guide with one section per affected workflow or feature. Each section should be self-contained enough that a reviewer or planner can understand the workflow without reading the code themselves.
|
|
71
|
-
|
|
72
|
-
### Workflow: <name>
|
|
73
|
-
|
|
74
|
-
- **Purpose**: What this workflow does and why it exists in the system
|
|
75
|
-
- **Key files**: The primary files and entry points, with file:line references
|
|
76
|
-
- **Data flow**: How data moves through the workflow (high-level, not line-by-line)
|
|
77
|
-
- **Major evolutions**: Key changes from git history. When was it created? What refactors or behavior changes shaped it? What drove those changes? Cite commit hashes or dates.
|
|
78
|
-
- **Constraints and design decisions**: From comments, memories, and docs. Known constraints, tradeoffs, intentional design choices, and gotchas.
|
|
79
|
-
- **Current state**: Any open issues, TODOs, known limitations, or incomplete migrations
|
|
80
|
-
|
|
81
|
-
## Scope discipline
|
|
82
|
-
|
|
83
|
-
Only research workflows that the change actually touches. Do not research every workflow in the codebase. If a changed file participates in three workflows but the change only affects one, research only that one.
|
|
84
|
-
|
|
85
|
-
If the change is small and touches a single workflow, the guide may be one section. If the change is cross-cutting, the guide may have several sections. Let the change determine the scope.
|
|
86
|
-
|
|
87
|
-
## What this skill is NOT
|
|
88
|
-
|
|
89
|
-
- It is not a code review. Do not evaluate the quality of the code or flag issues. That is the job of the review specialists.
|
|
90
|
-
- It is not a project context builder. That is the job of thatch-review-context, which gathers project metadata (tickets, PRs, TODOs, deferred work). This skill gathers code-level context (flows, comments, git history, evolutions).
|
|
91
|
-
- It is not a memory writer. Do not call thatch_memory_remember. If you discover durable facts, note them in the guide; the session-reflection or fact-extractor skills will persist them later.
|