@jeffober/thatch 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +4 -2
  2. package/artifacts/skills/thatch-fact-extractor.md +1 -1
  3. package/artifacts/skills/thatch-memory-verify.md +2 -2
  4. package/artifacts/skills/thatch-plan-refinement.md +3 -2
  5. package/artifacts/skills/thatch-refine-infra.md +66 -0
  6. package/artifacts/skills/thatch-refine-oss-contribution.md +68 -0
  7. package/artifacts/skills/thatch-refine-personal.md +61 -0
  8. package/artifacts/skills/thatch-refine-shared-lib.md +69 -0
  9. package/artifacts/skills/thatch-refine-spike.md +53 -0
  10. package/artifacts/skills/thatch-refine-team-app.md +69 -0
  11. package/artifacts/skills/thatch-refine.md +84 -0
  12. package/docs/dev/README.md +11 -7
  13. package/docs/dev/features/cross-session-chat.md +8 -1
  14. package/docs/dev/features/memory-store.md +1 -1
  15. package/docs/dev/features/nudge-pipeline.md +12 -0
  16. package/docs/dev/features/opencode-plugin.md +1 -1
  17. package/docs/dev/features/session-lifecycle.md +1 -1
  18. package/docs/dev/gotchas.md +71 -1
  19. package/docs/dev/mcp-parity.md +1 -1
  20. package/docs/dev/setup-and-hooks.md +1 -1
  21. package/docs/dev/skills.md +8 -1
  22. package/docs/user/README.md +8 -1
  23. package/docs/user/commands.md +8 -0
  24. package/docs/user/memory.md +1 -1
  25. package/docs/user/skills.md +12 -0
  26. package/package.json +1 -1
  27. package/src/chat.ts +17 -0
  28. package/src/commands.ts +23 -3
  29. package/src/db.ts +77 -12
  30. package/src/extraction.ts +89 -3
  31. package/src/index.ts +45 -0
  32. package/src/opencode/v2.ts +48 -4
  33. package/src/prompts.ts +118 -30
  34. package/src/runtime.ts +271 -72
  35. package/src/skills.ts +7 -0
  36. package/src/tool-defs.ts +156 -28
  37. package/src/watchers.ts +14 -0
@@ -72,6 +72,10 @@ export async function setup(context: V2Context): Promise<V2Cleanup | void> {
72
72
  // here, and the pump lets their events through.
73
73
  const childSessions = new Set<string>();
74
74
 
75
+ // Tool executions currently running against this instance's runtime (see
76
+ // the counter in the tool execute wrapper below).
77
+ let inflightTools = 0;
78
+
75
79
  const capabilities = buildCapabilities(context, worktree, childSessions);
76
80
  const runtime = await createRuntime({ capabilities, directory, worktree });
77
81
  // Seed the forwarding set from rehydrated child bookkeeping: after a
@@ -97,9 +101,33 @@ export async function setup(context: V2Context): Promise<V2Cleanup | void> {
97
101
  description: def.description,
98
102
  input: z.toJSONSchema(z.object(def.args)),
99
103
  execute: async (input: unknown, toolContext: V2ToolContext) => {
100
- const host: HostToolContext | undefined = trimHostContext(toolContext);
101
- const result: string = await def.execute(input as Record<string, unknown>, runtime.coreContext, host);
102
- return { content: result } as Tool.Result;
104
+ // In-flight counter (read by the cleanup below): v2 tears the old
105
+ // activation down while a call it registered may still be running,
106
+ // so cleanup waits briefly for stragglers before disposing the
107
+ // runtime whose db they use.
108
+ inflightTools++;
109
+ try {
110
+ const host: HostToolContext | undefined = trimHostContext(toolContext);
111
+ const result: string = await def.execute(input as Record<string, unknown>, runtime.coreContext, host);
112
+ return { content: result } as Tool.Result;
113
+ } catch (err) {
114
+ // A db closed mid-call by a concurrent reload's dispose surfaces
115
+ // here even after the wait (the call may have started racing the
116
+ // cleanup itself). Fail the call with an explicit retry hint -
117
+ // the reloaded instance is live by then, so a retry succeeds.
118
+ // (Hard failures are never retried by models - every prior
119
+ // session that hit one just abandoned the call.)
120
+ if (err instanceof Error && /closed database|connection closed/i.test(err.message)) {
121
+ return {
122
+ content:
123
+ "thatch: the plugin was reloaded mid-call and this instance's database handle " +
124
+ "was closed underneath it. The reloaded instance is live - retry the same call.",
125
+ } as Tool.Result;
126
+ }
127
+ throw err;
128
+ } finally {
129
+ inflightTools--;
130
+ }
103
131
  },
104
132
  });
105
133
  }
@@ -296,11 +324,21 @@ export async function setup(context: V2Context): Promise<V2Cleanup | void> {
296
324
  // second setup after a skipped or partial cleanup would double every
297
325
  // poller, pump, and nudge. The pump await is safe: its body is fully
298
326
  // try/caught and abort ends the loop, so it settles promptly.
327
+ //
328
+ // Before disposing the runtime, briefly drain in-flight tool executions:
329
+ // v2 tears this activation down mid-turn, and an execute whose db access
330
+ // would race the close either completes here or (past the bound) hits the
331
+ // lazy reopen + retry hint in the wrapper. The bound keeps a hung tool
332
+ // from holding the reload hostage.
299
333
  let disposed = false;
300
334
  return async () => {
301
335
  if (disposed) return;
302
336
  disposed = true;
303
337
  controller.abort();
338
+ const drainDeadline = Date.now() + 5_000;
339
+ while (inflightTools > 0 && Date.now() < drainDeadline) {
340
+ await new Promise((resolve) => setTimeout(resolve, 25));
341
+ }
304
342
  await pump;
305
343
  registerTools.dispose();
306
344
  registerToolHook.dispose();
@@ -394,7 +432,13 @@ export function translateEvent(located: { type: string; data?: any }): { type: s
394
432
  const data = located.data ?? {};
395
433
  switch (located.type) {
396
434
  case "session.created":
397
- return [{ type: "session.created", properties: { info: { id: data.sessionID, parentID: data.parentID } } }];
435
+ // v2 publishes the v1-compat payload { sessionID, info: SessionInfo };
436
+ // the parent link lives at info.parentID (the task tool dispatches
437
+ // sub-agents with parentID), not at the payload top level. Without
438
+ // this, childToParent never populates on v2 and every child-session
439
+ // behavior (buffer drain, nudge suppression, child cleanup) silently
440
+ // only works on v1.
441
+ return [{ type: "session.created", properties: { info: { id: data.sessionID, parentID: data.info?.parentID ?? data.parentID } } }];
398
442
  case "session.deleted":
399
443
  return [{ type: "session.deleted", properties: { info: { id: data.sessionID } } }];
400
444
  case "session.execution.started":
package/src/prompts.ts CHANGED
@@ -10,6 +10,29 @@ export function predictionVerb(evidenceCount: number): string {
10
10
  return evidenceCount === 0 ? "you may prefer" : "you tend to";
11
11
  }
12
12
 
13
+ /**
14
+ * Shared nudge-line formatter for the prediction and behavior surfaces.
15
+ * The model supplies the matcher and statement text, and both frequently
16
+ * arrive already carrying the render prefixes: matchers phrased as
17
+ * "When ..." (a natural situation phrasing, or copied from an earlier
18
+ * nudge) and statements copied with the evidence verb ("you tend to ...").
19
+ * The unconditional prefixes doubled that text in every rendered nudge,
20
+ * so each prefix is skipped when the supplied text already carries it.
21
+ */
22
+ export function formatWhenLine(
23
+ confidence: number,
24
+ evidenceCount: number,
25
+ matcher: string,
26
+ statement: string,
27
+ verb: string,
28
+ ): string {
29
+ const matcherText = /^when\b/i.test(matcher) ? matcher : `When ${matcher}`;
30
+ const statementText = new RegExp(`^${verb}\b`, "i").test(statement) || /^you (?:tend to|may prefer)\b/i.test(statement)
31
+ ? statement
32
+ : `${verb} ${statement}`;
33
+ return `[${confidence.toFixed(2)} conf, ${evidenceCount} tests] ${matcherText}: ${statementText}`;
34
+ }
35
+
13
36
  /**
14
37
  * Cross-session chat section of the opencode system prompt. Omitted when the
15
38
  * user disabled chat via config (chat.enabled: false), so a disabled feature
@@ -73,6 +96,49 @@ ought to trigger it and widen the wording to the broadest surface of the
73
96
  class - a matcher that only names this incident never fires for the next
74
97
  one.`;
75
98
 
99
+ /**
100
+ * The artifact-pointer bullet of the "How to Write" section. Names no
101
+ * tools, so it is shared verbatim by both prompt variants (the
102
+ * class-lesson convention above).
103
+ */
104
+ const artifactPointerBullet = `- Artifact memories should carry their pointers: file paths, ticket IDs,
105
+ package names. Verification is cheap later only if the memory says where
106
+ to check. A pointer is provenance for durable knowledge, not knowledge
107
+ itself - a bare "where X lives" is exactly what the durability test
108
+ excludes.`;
109
+
110
+ /**
111
+ * "Memories are pointers, not truth" instruction body, shared by all three
112
+ * prompt variants. Takes a ToolNamer because the section names memory tools
113
+ * (thatch_memory_remember on opencode, bare on MCP hosts, whose
114
+ * instructions establish the bare-name convention). Staleness is a
115
+ * property of the claim's relationship to a mutable world, not of the
116
+ * memory's age.
117
+ */
118
+ export function memoryPointersSection(tool: ToolNamer): string {
119
+ return `## Memories Are Pointers, Not Truth
120
+
121
+ ${tool("memory_recall")} surfaces a point-in-time record: a semantic guide
122
+ associating "what I'm looking for" with "where I should look", written when
123
+ the knowledge was current. Memories speed up research - they do not replace
124
+ it, and they are not a source of truth.
125
+
126
+ - **Policy memories** ("the user always wants X", "check CI after merging")
127
+ are self-executing: they are the ground truth for behavior.
128
+ - **Artifact memories** ("feature X lives in <file> and uses <technique>")
129
+ are pointers to checkable claims about code, docs, tickets, or git.
130
+ Follow the pointer and assert from the source, not from the memory.
131
+
132
+ Before asserting an artifact claim from memory, re-derive it from the
133
+ current source. Confirming the reference still exists verifies little: the
134
+ file can survive while the behavior changed (scope drift, priority shifts,
135
+ implementation details that proved too complex in practice).
136
+ The source wins. When reality has moved on, correct the record with
137
+ ${tool("memory_remember")} (overwrite: true) or ${tool("memory_forget")} -
138
+ silently; memory maintenance is not user-facing. For a deep staleness check
139
+ of a specific memory, load the thatch-memory-verify skill.`;
140
+ }
141
+
76
142
  export function systemPrompt(repo: string, chatEnabled = true): string {
77
143
  return `# Persistence
78
144
 
@@ -106,13 +172,16 @@ WIP, PR status). Unscoped memories are project-wide and always included in searc
106
172
 
107
173
  ## Session Startup
108
174
 
109
- 1. thatch_memory_recall "user preferences and personality"
110
- 2. thatch_memory_recall "project architecture and conventions"
111
- 3. If on a non-main branch, thatch_memory_recall with the branch name
175
+ 1. thatch_memory_recall({ query: "user preferences and personality" })
176
+ 2. thatch_memory_recall({ query: "project architecture and conventions" })
177
+ 3. If on a non-main branch, thatch_memory_recall with the branch name as the query
112
178
  4. thatch_memory_list and thatch_store_list to see what's available
113
179
 
114
180
  Before diving into code, thatch_memory_recall with a query relevant to the area
115
- you're working in. Prior sessions may have already investigated it.
181
+ you're working in. Prior sessions may have already investigated it. All thatch
182
+ tools take a single object argument: never a bare positional string. Call them
183
+ directly, or through the Code Mode execute runtime when the harness only
184
+ exposes tools through code.
116
185
 
117
186
  ## Skills
118
187
 
@@ -123,6 +192,13 @@ workflows. The host auto-discovers them, but reach for them proactively:
123
192
  - \`thatch-review-followup\` - alternate entrypoint for follow-up review rounds. Verifies whether the author's responses and code changes since your last review adequately addressed prior findings, offers to reply on resolved items, then optionally re-runs the full structured review.
124
193
  - \`thatch-code-archaeology\` - investigate an existing feature, debug an unfamiliar area, or begin a new ticket. Explores the code base from multiple angles (data model, state flow, git history, sibling features) before proposing changes. The research skill; pair with \`thatch-coding-workflow\`.
125
194
  - \`thatch-plan-refinement\` - refine a plan before implementing it: fresh-context reviewer rounds until consensus, with an optional deep-mode lens fan-out (reuse, alternatives, hidden problems, safe-to-modify, archaeology). Use when a plan for a code change is about to be built or the user asks to stress-test a plan.
195
+ - \`thatch-refine\` - classify the project (audience, lifecycle, surface) and refine a plan under that type's requirements: team codebases owe coordination and intent breadcrumbs, personal projects allow cleverness, shared libraries owe API hygiene. Entry point for the thatch-refine-* family; also the /thatch/refine command.
196
+ - \`thatch-refine-team-app\` - refinement deltas for multi-developer team codebases: consistency outranks cleverness, intent breadcrumbs are deliverables, pre-mortem and safe-to-modify lenses dominate.
197
+ - \`thatch-refine-personal\` - refinement deltas for solo projects with no downstream consumers: relaxed coordination, cleverness and experimentation welcome, premise verification stays honest.
198
+ - \`thatch-refine-shared-lib\` - refinement deltas for changes touching a library's API surface: flexible on input, strict on output, keep special cases out of the API, never foreclose caller options, docs are a deliverable.
199
+ - \`thatch-refine-spike\` - refinement deltas for throwaway prototypes: skip the loop, verify only the premises the experiment's conclusion depends on, state the question and the time box.
200
+ - \`thatch-refine-infra\` - refinement deltas for CI, Terraform, and k8s changes: blast radius across pipelines and environments, rollback plan and staged verification are mandatory, pattern-following is near-absolute.
201
+ - \`thatch-refine-oss-contribution\` - refinement deltas for PRs to repos you do not maintain: the maintainer's design authority governs, minimal diff, upstream discussion gates significant design work.
126
202
  - \`thatch-change-walkthrough\` - produces a teaching walkthrough of a diff with a specific format (SYNOPSIS + per-workflow orient/mechanism/numbered-stages + change overlay mirroring the same numbers) and calibrated prose rules. Load the skill for the format and prose rules, not just for the research method.
127
203
  - \`thatch-code-walkthrough\` - produces a teaching walkthrough for a feature/workflow as it stands today with a specific format (SYNOPSIS + per-workflow orient/mechanism/numbered-stages + Key files) and calibrated prose rules. Also use proactively to draft high-level docs for new or undocumented features (suitable for pkg/README.md or docs/features/<name>.md). Load the skill for the format and prose rules, not just for the research method.
128
204
  - \`thatch-review-context\` - gather project context (PRs, tickets, TODOs, deferred work) before a review.
@@ -134,7 +210,7 @@ workflows. The host auto-discovers them, but reach for them proactively:
134
210
  - \`thatch-ticket-description\` - draft a ticket, issue, or work item (Linear or Jira) with clear sections and bold/italic emphasis for scanning.
135
211
  - \`thatch-split-overlarge-pr\` - split already-completed work from an overlarge PR into human-reviewable, release-safe PRs targeting main.
136
212
  - \`thatch-review-response\` - respond to code review on the user's own PR. Triage findings, fix bugs one by one, reply on each thread, then post a top-level summary comment.
137
- - \`thatch-memory-verify\` - fact-check a single memory against the current codebase and correct stale claims. Use when a memory has an old \`updated_at\` date or when preparing a knowledge export.
213
+ - \`thatch-memory-verify\` - fact-check a single memory against the current codebase and correct stale claims. Use when a memory looks stale or when preparing a knowledge export.
138
214
  - \`thatch-knowledge-export\` - compile everything thatch knows about a topic into a curated markdown file for knowledge transfer. Searches across stores, curates out personal noise, fact-checks code-related memories, and generates a clean export.
139
215
  - \`thatch-clear-writing\` - prose rules for any human-facing text not covered by a dedicated writing skill: PR and ticket comments, documentation, plans, reports, and chat replies. Load before drafting any of those.
140
216
 
@@ -162,15 +238,7 @@ whether you've discovered knowledge worth persisting. Use thatch_memory_recall
162
238
  to check for duplicates, then thatch_memory_remember for new findings. Then
163
239
  deliver your response.
164
240
 
165
- ## Memory Verification
166
-
167
- When thatch_memory_show or thatch_memory_recall returns a memory with an old
168
- updated_at date (more than a few weeks), consider loading the
169
- thatch-memory-verify skill to fact-check its code-related claims against the
170
- current codebase before relying on them. This is not mandatory for every old
171
- memory. Use judgment: a memory about a stable architectural decision may be
172
- fine for months, while a memory about file locations or function names is
173
- more likely to drift.
241
+ ${memoryPointersSection(opencodeToolName)}
174
242
 
175
243
  ## Background Task Completions
176
244
 
@@ -187,6 +255,12 @@ the same prompt. If the re-dispatch also fails or times out, note the gap and
187
255
  move on. Do not retry more than once - repeated failures indicate an
188
256
  intractable prompt, not a transient hang.
189
257
 
258
+ When the fact-extractor sub-agent completes, its "Extraction complete."
259
+ report is a background-task completion, not an extraction nudge: the buffer
260
+ is drained and nothing follows from it. Never re-dispatch an extractor
261
+ because a completion arrived - a new extraction nudge is the only dispatch
262
+ signal.
263
+
190
264
  ## Watchers
191
265
 
192
266
  Use thatch_watch_create to watch a GitHub PR for events (comments, review
@@ -304,6 +378,7 @@ config in memory.
304
378
  - One topic per memory. Write for a future instance with zero current context.
305
379
  - Confidence 1-2: single signal. 5-6: moderate. 9: explicitly stated.
306
380
  10: hard constraint.
381
+ ${artifactPointerBullet}
307
382
 
308
383
  ## What NOT to Store
309
384
 
@@ -422,13 +497,14 @@ WIP, PR status). Unscoped memories are project-wide and always included in searc
422
497
 
423
498
  ## Session Startup
424
499
 
425
- 1. \`memory_recall\` "user preferences and personality" (global store)
426
- 2. \`memory_recall\` "project architecture and conventions" (project store)
427
- 3. If on a non-main branch, \`memory_recall\` with the branch name
500
+ 1. \`memory_recall\` with \`{"query": "user preferences and personality"}\` (global store)
501
+ 2. \`memory_recall\` with \`{"query": "project architecture and conventions"}\` (project store)
502
+ 3. If on a non-main branch, \`memory_recall\` with the branch name as the query
428
503
  4. \`memory_list\` and \`store_list\` to see what's available
429
504
 
430
505
  Before diving into code, \`memory_recall\` with a query relevant to the area
431
- you're working in. Prior sessions may have already investigated it.
506
+ you're working in. Prior sessions may have already investigated it. All thatch
507
+ tools take a single JSON object argument: never a bare positional string.
432
508
 
433
509
  ## Skills
434
510
 
@@ -437,6 +513,13 @@ workflows. The host auto-discovers them, but reach for them proactively:
437
513
 
438
514
  - \`thatch-code-archaeology\` - investigate an existing feature, debug an unfamiliar area, or begin a new ticket. Explores the code base from multiple angles (data model, state flow, git history, sibling features) before proposing changes. The research skill; pair with \`thatch-coding-workflow\`.
439
515
  - \`thatch-plan-refinement\` - refine a plan before implementing it: fresh-context reviewer rounds until consensus, with an optional deep-mode lens fan-out (reuse, alternatives, hidden problems, safe-to-modify, archaeology). Use when a plan for a code change is about to be built or the user asks to stress-test a plan.
516
+ - \`thatch-refine\` - classify the project (audience, lifecycle, surface) and refine a plan under that type's requirements: team codebases owe coordination and intent breadcrumbs, personal projects allow cleverness, shared libraries owe API hygiene. Entry point for the thatch-refine-* family; also the /thatch/refine command.
517
+ - \`thatch-refine-team-app\` - refinement deltas for multi-developer team codebases: consistency outranks cleverness, intent breadcrumbs are deliverables, pre-mortem and safe-to-modify lenses dominate.
518
+ - \`thatch-refine-personal\` - refinement deltas for solo projects with no downstream consumers: relaxed coordination, cleverness and experimentation welcome, premise verification stays honest.
519
+ - \`thatch-refine-shared-lib\` - refinement deltas for changes touching a library's API surface: flexible on input, strict on output, keep special cases out of the API, never foreclose caller options, docs are a deliverable.
520
+ - \`thatch-refine-spike\` - refinement deltas for throwaway prototypes: skip the loop, verify only the premises the experiment's conclusion depends on, state the question and the time box.
521
+ - \`thatch-refine-infra\` - refinement deltas for CI, Terraform, and k8s changes: blast radius across pipelines and environments, rollback plan and staged verification are mandatory, pattern-following is near-absolute.
522
+ - \`thatch-refine-oss-contribution\` - refinement deltas for PRs to repos you do not maintain: the maintainer's design authority governs, minimal diff, upstream discussion gates significant design work.
440
523
  - \`thatch-change-walkthrough\` - produces a teaching walkthrough of a diff with a specific format (SYNOPSIS + per-workflow orient/mechanism/numbered-stages + change overlay mirroring the same numbers) and calibrated prose rules. Load the skill for the format and prose rules, not just for the research method.
441
524
  - \`thatch-code-walkthrough\` - produces a teaching walkthrough for a feature/workflow as it stands today with a specific format (SYNOPSIS + per-workflow orient/mechanism/numbered-stages + Key files) and calibrated prose rules. Also use proactively to draft high-level docs for new or undocumented features (suitable for pkg/README.md or docs/features/<name>.md). Load the skill for the format and prose rules, not just for the research method.
442
525
  - \`thatch-review-context\` - gather project context (PRs, tickets, TODOs, deferred work) before a review.
@@ -450,7 +533,7 @@ workflows. The host auto-discovers them, but reach for them proactively:
450
533
  - \`thatch-ticket-description\` - draft a ticket, issue, or work item (Linear or Jira) with clear sections and bold/italic emphasis for scanning.
451
534
  - \`thatch-split-overlarge-pr\` - split already-completed work from an overlarge PR into human-reviewable, release-safe PRs targeting main.
452
535
  - \`thatch-review-response\` - respond to code review on the user's own PR. Triage findings, fix bugs one by one, reply on each thread, then post a top-level summary comment.
453
- - \`thatch-memory-verify\` - fact-check a single memory against the current codebase and correct stale claims. Use when a memory has an old \`updated_at\` date or when preparing a knowledge export.
536
+ - \`thatch-memory-verify\` - fact-check a single memory against the current codebase and correct stale claims. Use when a memory looks stale or when preparing a knowledge export.
454
537
  - \`thatch-knowledge-export\` - compile everything thatch knows about a topic into a curated markdown file for knowledge transfer. Searches across stores, curates out personal noise, fact-checks code-related memories, and generates a clean export.
455
538
  - \`thatch-clear-writing\` - prose rules for any human-facing text not covered by a dedicated writing skill: PR and ticket comments, documentation, plans, reports, and chat replies. Load before drafting any of those.
456
539
 
@@ -478,14 +561,7 @@ whether you've discovered knowledge worth persisting. Use memory_recall to
478
561
  check for duplicates, then memory_remember for new findings. Then deliver
479
562
  your response.
480
563
 
481
- ## Memory Verification
482
-
483
- When memory_show or memory_recall returns a memory with an old updated_at
484
- date (more than a few weeks), consider loading the thatch-memory-verify skill
485
- to fact-check its code-related claims against the current codebase before
486
- relying on them. This is not mandatory for every old memory. Use judgment: a
487
- memory about a stable architectural decision may be fine for months, while a
488
- memory about file locations or function names is more likely to drift.
564
+ ${memoryPointersSection(bareToolName)}
489
565
 
490
566
  ## User Decision Model
491
567
 
@@ -571,6 +647,7 @@ hand-editable; do not store thatch config in memory.
571
647
  - One topic per memory. Write for a future instance with zero current context.
572
648
  - Confidence 1-2: single signal. 5-6: moderate. 9: explicitly stated.
573
649
  10: hard constraint.
650
+ ${artifactPointerBullet}
574
651
 
575
652
  ## What NOT to Store
576
653
 
@@ -683,6 +760,13 @@ export type ToolNamer = (name: string) => string;
683
760
  export const opencodeToolName: ToolNamer = (name) => `thatch_${name}`;
684
761
  export const mcpToolName: ToolNamer = (name) => `mcp__thatch__${name}`;
685
762
 
763
+ /**
764
+ * The bare name itself. Used by prompt sections embedded in instructions
765
+ * that establish the bare-name convention (mcpInstructions and twins): the
766
+ * model maps bare names to the host spelling at call time.
767
+ */
768
+ export const bareToolName: ToolNamer = (name) => name;
769
+
686
770
  /**
687
771
  * The extraction instruction body. sessionIdArg is either ` with session_id
688
772
  * "X"` (the nudge path: the sub-agent's session differs from the parent's)
@@ -866,7 +950,11 @@ function formatRecallNudge(matches: NudgeMatch[], toolName: string): string {
866
950
  const verb = n === 1 ? "relates" : "relate";
867
951
  const labels = matches.slice(0, 2).map((m) => `"${m.label}"`).join(", ");
868
952
  const etc = n > 2 ? ", etc." : "";
869
- return `[thatch] ${n} ${word} ${verb} to this prompt (${labels}${etc}). Use ${toolName} before responding.`;
953
+ return (
954
+ `[thatch] ${n} ${word} ${verb} to this prompt (${labels}${etc}). ` +
955
+ `Use ${toolName}({ query: "..." }) before responding - the argument is ` +
956
+ `a single object with a "query" key, never a bare positional string.`
957
+ );
870
958
  }
871
959
 
872
960
  // ---------------------------------------------------------------------------
@@ -879,7 +967,7 @@ function formatRecallNudge(matches: NudgeMatch[], toolName: string): string {
879
967
  export function predictionNudge(items: PredictionNudgeItem[]): string {
880
968
  const lines = items.map((p) => {
881
969
  const verb = predictionVerb(p.evidence_count);
882
- return `- [${p.confidence.toFixed(2)} conf, ${p.evidence_count} tests] When ${p.matcher_description}: ${verb} ${p.statement}`;
970
+ return `- ${formatWhenLine(p.confidence, p.evidence_count, p.matcher_description, p.statement, verb)}`;
883
971
  });
884
972
  return `[thatch] User decision model\n${lines.join("\n")}`;
885
973
  }
@@ -893,7 +981,7 @@ export function predictionNudge(items: PredictionNudgeItem[]): string {
893
981
  export function behaviorNudge(items: BehaviorNudgeItem[]): string {
894
982
  const lines = items.map((b) => {
895
983
  const verb = b.evidence_count === 0 ? "consider" : "do";
896
- return `- [${b.confidence.toFixed(2)} conf, ${b.evidence_count} tests] When ${b.matcher_description}: ${verb} ${b.statement}`;
984
+ return `- ${formatWhenLine(b.confidence, b.evidence_count, b.matcher_description, b.statement, verb)}`;
897
985
  });
898
986
  return `[thatch] Situational behaviors\n${lines.join("\n")}`;
899
987
  }