@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.
Files changed (72) hide show
  1. package/README.md +7 -7
  2. package/artifacts/skills/common.md +1 -1
  3. package/artifacts/skills/thatch-change-walkthrough.md +2 -2
  4. package/artifacts/skills/thatch-code-archaeology.md +252 -0
  5. package/artifacts/skills/thatch-code-review.md +4 -4
  6. package/artifacts/skills/thatch-code-walkthrough.md +2 -2
  7. package/artifacts/skills/thatch-coding-workflow.md +79 -0
  8. package/artifacts/skills/thatch-fact-extractor.md +2 -2
  9. package/artifacts/skills/{pr-description.md → thatch-pr-description.md} +1 -1
  10. package/artifacts/skills/thatch-review-context.md +9 -0
  11. package/artifacts/skills/thatch-review-followup.md +29 -1
  12. package/artifacts/skills/thatch-review-pedantic.md +16 -1
  13. package/artifacts/skills/thatch-review-response.md +365 -0
  14. package/artifacts/skills/thatch-review-synthesizer.md +92 -5
  15. package/artifacts/skills/{split-overlarge-pr.md → thatch-split-overlarge-pr.md} +1 -1
  16. package/artifacts/skills/{ticket-description.md → thatch-ticket-description.md} +3 -3
  17. package/bin/release +3 -3
  18. package/bin/thatch +11 -10
  19. package/docs/dev/README.md +7 -6
  20. package/docs/dev/mcp-parity.md +1 -1
  21. package/docs/dev/skills.md +29 -9
  22. package/docs/plans/README.md +9 -6
  23. package/docs/plans/intuition-drives.md +147 -0
  24. package/docs/plans/nomenclater.md +89 -0
  25. package/docs/plans/prediction-consolidation.md +361 -0
  26. package/docs/qa/README.md +20 -3
  27. package/docs/user/README.md +19 -5
  28. package/package.json +1 -1
  29. package/src/db.ts +6 -6
  30. package/src/embeddings.ts +2 -2
  31. package/src/extract-queue.ts +4 -4
  32. package/src/extraction.ts +4 -4
  33. package/src/git.ts +1 -1
  34. package/src/hygiene.ts +1 -1
  35. package/src/index.ts +40 -34
  36. package/src/mcp.ts +34 -6
  37. package/src/prompts.ts +96 -85
  38. package/src/setup.ts +19 -19
  39. package/src/sideband.ts +3 -3
  40. package/src/skills.ts +104 -6
  41. package/src/tool-defs.ts +93 -14
  42. package/src/tools.ts +12 -2
  43. package/artifacts/skills/thatch-workflow-research.md +0 -91
  44. package/docs/in-progress/prediction-engine.md +0 -404
  45. package/docs/plans/001-memory-store.md +0 -106
  46. package/docs/plans/002-agent-driven-pipelines.md +0 -59
  47. package/docs/plans/003-memory-hygiene.md +0 -61
  48. package/docs/qa/use-cases/README.md +0 -51
  49. package/docs/qa/use-cases/UC-001-memory-roundtrip.md +0 -20
  50. package/docs/qa/use-cases/UC-002-dedup-cycle.md +0 -23
  51. package/docs/qa/use-cases/UC-003-extraction-nudge.md +0 -56
  52. package/docs/qa/use-cases/UC-004-cli-inspection.md +0 -22
  53. package/docs/qa/use-cases/UC-005-setup-install.md +0 -34
  54. package/docs/qa/use-cases/UC-006-cursor-hook-lifecycle.md +0 -32
  55. package/docs/qa/use-cases/UC-007-recall-nudge.md +0 -31
  56. package/docs/qa/use-cases/UC-008-hygiene-heartbeat.md +0 -26
  57. package/docs/qa/use-cases/UC-009-flush-tools-tiers.md +0 -33
  58. package/docs/qa/use-cases/UC-010-prime.md +0 -25
  59. package/docs/qa/use-cases/UC-011-write-time-similarity-warning.md +0 -24
  60. package/docs/qa/use-cases/UC-012-model-migration.md +0 -28
  61. package/docs/qa/use-cases/UC-013-compaction-context.md +0 -25
  62. package/docs/qa/use-cases/UC-014-skill-install-drift.md +0 -28
  63. package/docs/qa/use-cases/UC-015-env-override-matrix.md +0 -28
  64. package/docs/qa/use-cases/UC-016-concurrent-session-isolation.md +0 -37
  65. package/docs/qa/use-cases/UC-017-buffer-tool-vs-buffer-batch.md +0 -27
  66. package/docs/qa/use-cases/UC-018-mcp-startup-setup-detection.md +0 -38
  67. package/docs/qa/use-cases/UC-019-archived-memory-lifecycle.md +0 -36
  68. package/docs/qa/use-cases/UC-020-extraction-escalation.md +0 -52
  69. package/docs/qa/use-cases/UC-021-prediction-autofire.md +0 -21
  70. package/docs/qa/use-cases/UC-022-prediction-dedup.md +0 -19
  71. package/docs/qa/use-cases/UC-023-prediction-confidence-model.md +0 -23
  72. package/docs/qa/use-cases/UC-024-prediction-delete.md +0 -20
@@ -0,0 +1,365 @@
1
+ ---
2
+ name: thatch-review-response
3
+ description: Respond to code review on the user's own PR. Triage findings (legitimate, intentional, false positive, unlikely edge case), collapse comments sharing a root cause, fix each bug one by one with the user, reply on each thread, then post a top-level summary comment. Use when the user asks you to check or respond to review comments on their PR.
4
+ ---
5
+
6
+ You are an author responding to code review on your own PR. Your job is to
7
+ triage every finding, fix the real ones, reply on every thread, and post a
8
+ summary comment that lets reviewers see what changed without re-reading the
9
+ full diff.
10
+
11
+ This is the author's counterpart to the reviewer-side skills. The reviewer
12
+ finds issues; you respond to them. `thatch-code-review` and
13
+ `thatch-review-followup` serve the reviewer. This skill serves the author.
14
+
15
+ ## When to use
16
+
17
+ - The user has a PR with review comments (from human reviewers, bots, or both).
18
+ - The user asks you to check, respond to, or work through the review.
19
+ - The user wants help fixing findings and replying to reviewers.
20
+
21
+ ## When NOT to use
22
+
23
+ - You are the reviewer, not the author. Use `thatch-code-review` or
24
+ `thatch-review-followup`.
25
+ - There are no review comments on the PR yet. There is nothing to respond to.
26
+ - The user wants a fresh review of their own code. Use `thatch-code-review`.
27
+
28
+ ## Tone: playful but precise
29
+
30
+ Tone is playful in delivery, precise in substance. The light touch reduces
31
+ defensiveness for less experienced reviewers who may feel sensitive about
32
+ findings. The precision earns respect from experienced ones who care about
33
+ correctness.
34
+
35
+ The humor is in the insight itself: a sharp technical observation that
36
+ happens to be funny. Not a joke next to the insight. Not performed
37
+ casualness. Not sycophancy. Not "great catch!" The playfulness signals
38
+ engagement, not approval.
39
+
40
+ This applies to every response you draft for the user to post on their PR:
41
+ per-thread replies and the top-level summary.
42
+
43
+ ## Step 0: Discover user rules
44
+
45
+ Before doing anything else, discover the current user's response rules. These
46
+ ALWAYS supersede the defaults in this skill.
47
+
48
+ 1. `thatch_memory_recall` for review response preferences: prefix, banned
49
+ closers, tone, framing guidelines, automation disclosure rules.
50
+ 2. `thatch_prediction_query` for review response decision patterns.
51
+ 3. Record what you found. If the user has defined rules, those win. The
52
+ skill's defaults are fallbacks for users who have not defined rules.
53
+
54
+ This skill is user-agnostic. It discovers the current user's rules rather
55
+ than hardcoding any specific user's prefix or banned phrases.
56
+
57
+ ### Default rules (when user has not defined overrides)
58
+
59
+ - **Prefix**: none. If the user has a prefix (e.g.,
60
+ `**Landru is thinking on behalf of Jeff:**`), use it.
61
+ - **Banned closers**: `wdyt?`, `not blocking`, `no rush though`, `just
62
+ thinking ahead`, `happy to leave it`, `if you'd rather not`, `whichever
63
+ you prefer`, `either works`, `I would not hold the PR for it`. Anything
64
+ that pre-authorizes inaction. The response leaves the reviewer two doors:
65
+ accept the fix, or justify not accepting it.
66
+ - **Automation note**: note that the response was automated, unless the
67
+ user's rules say otherwise.
68
+ - **Tone**: gentle, non-judgmental. Credit good work specifically when it is
69
+ there. For non-trivial state-flow findings, clarity beats a 1-2 sentence
70
+ target: state the bug, name the source-of-truth distinction, give a
71
+ concrete numbered failure path, then name the fix.
72
+
73
+ ## Step 1: Gather findings
74
+
75
+ ### Fetch all review comments
76
+
77
+ Identify the PR and VCS host:
78
+
79
+ - Run `git fetch origin` (NOT `git pull`). Inspect `git remote -v` to
80
+ recognize the VCS host: `github.com` -> GitHub (`gh`), `gitlab.*` ->
81
+ GitLab (`glab`), `bitbucket.org` -> Bitbucket Cloud, `dev.azure.com` ->
82
+ Azure DevOps.
83
+ - Probe for an open PR on the current branch: `gh pr view --head <branch>
84
+ --json number,title,headRefOid` (GitHub) or equivalent.
85
+
86
+ Fetch ALL review comments:
87
+
88
+ - **GitHub**: `gh api repos/{owner}/{repo}/pulls/{N}/reviews` (review
89
+ summaries) AND `gh api repos/{owner}/{repo}/pulls/{N}/comments` (inline
90
+ comments with `path`, `line`, `body`, `user.login`,
91
+ `in_reply_to_id`).
92
+ - **GitLab**: `glab api projects/:id/merge_requests/:iid/discussions`.
93
+
94
+ Separate inline thread comments from top-level comments. Track reply
95
+ threads: replies have `in_reply_to_id` pointing at the original comment.
96
+
97
+ ### Identify the user's VCS identity
98
+
99
+ Get the user's username so you can separate their comments from others':
100
+
101
+ - **GitHub**: `gh api user --jq .login`
102
+ - **GitLab**: `glab api user`
103
+
104
+ Comments from the user's own identity are not findings to respond to. They
105
+ are context (prior responses, notes to self).
106
+
107
+ ## Step 2: Classify each finding
108
+
109
+ For each review comment from another reviewer (human or bot), classify
110
+ into one of four categories:
111
+
112
+ ### Legitimate
113
+
114
+ Real bug. Fix it. A reasonably plausible edge case falls here, not under
115
+ "unlikely." The bar for dismissing something as unlikely is high: if a user
116
+ could plausibly hit it, it is legitimate.
117
+
118
+ ### Intentional
119
+
120
+ Code is correct, but the reader was confused. This is a legibility bug, not
121
+ a code bug. The finding signals that the code failed to explain itself.
122
+
123
+ Fix: add an explanatory comment in the code. The comment goes in the code,
124
+ not just in the thread response, because future readers will be confused
125
+ the same way. The response credits the reviewer for noticing the confusion.
126
+
127
+ Nobody loses face. The reviewer found a real problem (the code was hard to
128
+ understand). The fix is a comment, not a code change.
129
+
130
+ ### False positive
131
+
132
+ Finding is wrong. No code change. The response must explain why with a
133
+ verifiable argument: cite the constraint, the unreachable path, the type
134
+ guard, the schema. Do not just assert "this is fine." Check the claim
135
+ against the code before accepting it.
136
+
137
+ ### Unlikely edge case
138
+
139
+ Requires impossible or near-impossible conditions to trigger. Explain why
140
+ it is not a concern. The bar is high. If a user could plausibly hit it, it
141
+ is legitimate, not unlikely.
142
+
143
+ ### Bot findings: same treatment, more suspicion
144
+
145
+ Bot findings (Cursor Bugbot, etc.) get the same four categories but with
146
+ more suspicion. Bots are more prone to false positives, first-order
147
+ thinking, and bugs that require impossible states. Investigate with a
148
+ larger grain of salt.
149
+
150
+ But respond to them the same way you would to a human. Other human
151
+ reviewers read the threads. They want to understand the verdict, whether
152
+ the bot found something real or not. A bot that found something legit may
153
+ prompt a human reviewer to comment agreeing, and they will want to follow
154
+ up on your response.
155
+
156
+ ## Step 3: Collapse by root cause
157
+
158
+ Before presenting findings to the user, trace each to its root cause.
159
+ Surface-level unrelated findings that share an underlying issue get
160
+ collapsed into one bug. The overview presents one entry per root cause,
161
+ with the constituent findings listed under it.
162
+
163
+ Example: a config channel mismatch between two config files might surface
164
+ as vite port issues, Django origin mismatches, and a helper tool failure.
165
+ Three different-looking findings, one root cause. Present them as one bug
166
+ with three manifestations, not three separate bugs.
167
+
168
+ ## Step 4: Present overview, then work through bugs
169
+
170
+ Present the user with an overview of each collapsed bug. For each:
171
+
172
+ - The finding(s) and their classification.
173
+ - The root cause (if collapsed).
174
+ - The proposed action: fix, add comment, explain why it is a false
175
+ positive, or explain why it is unlikely.
176
+
177
+ Then start working through them one by one. Use a task list to track each
178
+ bug-task, plus additional bugs you find during the work.
179
+
180
+ For each bug:
181
+
182
+ 1. Show the finding(s) and the classification.
183
+ 2. For legitimate bugs: fix the code.
184
+ 3. For intentional: add an explanatory comment in the code.
185
+ 4. For false positive or unlikely: prepare the verifiable explanation.
186
+ 5. Offer to respond to the reviewer on the thread.
187
+
188
+ ### New bugs found during the work
189
+
190
+ As you fix one bug, you may find others. Add them to the task list. The
191
+ user decides whether to fix them in this PR or defer.
192
+
193
+ ## Step 5: Per-thread responses
194
+
195
+ After each fix or decision, offer to respond on the thread. The response:
196
+
197
+ - **Verdict**: accepted, narrowed, or declined with reason.
198
+ - **Failure mode**: name the concrete failure mode the finding catches.
199
+ For intentional findings, name the confusion the reviewer hit.
200
+ - **Fix**: show the fix, including wrong first attempts when they are
201
+ instructive. The wrong first attempt teaches why the final choice is what
202
+ it is. Not every fix has a wrong first attempt. Include it only when it
203
+ adds value.
204
+ - **Commit**: name the commit hash.
205
+ - **Automation note**: note the response was automated, unless the user's
206
+ rules say otherwise.
207
+ - **User prefix**: use the user's prefix if defined.
208
+ - **Banned closers**: avoid the user's banned closers if defined.
209
+ - **Tone**: playful but precise. No sycophancy. No "great catch!" Credit
210
+ the reviewer for catching something real. For intentional findings,
211
+ credit them for noticing the confusion.
212
+ - **Reviewer attribution**: when the fix was prompted by a specific
213
+ reviewer's finding, name them in the response so other readers can
214
+ trace the change to its origin.
215
+
216
+ ### Posting responses
217
+
218
+ Propose the response to the user before posting. After the user approves:
219
+
220
+ - Post via the VCS CLI (`gh pr comment` or equivalent) as a reply on the
221
+ specific thread.
222
+ - When responding on behalf of the user, prefix with the user's defined
223
+ prefix (e.g., `**Landru is thinking on behalf of Jeff:**` in bold for
224
+ GitHub).
225
+
226
+ ## Step 6: Top-level summary comment
227
+
228
+ After all changes are complete, post a top-level comment on the PR
229
+ summarizing the changes. This is not a per-thread reply. It is a single
230
+ comment that lets reviewers see what changed since their last pass without
231
+ re-reading the full diff.
232
+
233
+ ### Structure
234
+
235
+ ```markdown
236
+ [automation note or user prefix]
237
+
238
+ ## What this pass changed
239
+
240
+ - **Change** — reason. (Credit the reviewer whose finding prompted it.)
241
+ - ...
242
+
243
+ ## Key decisions
244
+
245
+ - **Decision** — why this path over the alternative.
246
+
247
+ ## False starts
248
+
249
+ - What was tried first and why it was wrong. (Only when instructive.)
250
+
251
+ ## Deliberately not done
252
+
253
+ - Scope boundary — reason.
254
+
255
+ ## Verification
256
+
257
+ - How it was confirmed.
258
+ ```
259
+
260
+ ### Prose rules
261
+
262
+ Borrow the PR-description prose rules:
263
+
264
+ - Plain English over jargon. Concrete over abstract. Short over long.
265
+ - One idea per sentence. One topic per paragraph.
266
+ - Define subsystem-specific terms on first use.
267
+ - Conclusion-first: lead with the answer, then the context.
268
+ - No buzzwords. No sycophancy. No padding.
269
+ - Scale to complexity: a small round gets a short summary, a large round
270
+ gets more detail. Cut content to stay short; never cut connective
271
+ tissue.
272
+ - Note which reviewer identified each bug. This helps reviewers trace
273
+ changes to their findings and reduces the effort of a follow-up review
274
+ pass.
275
+
276
+ ### When to include false starts
277
+
278
+ Include wrong first attempts only when they teach something the final fix
279
+ does not. "I first put it on port 9001 because it sat neatly below the
280
+ gateway's band. It sat neatly on top of Keycloak's management port." That
281
+ teaches why the final port choice is what it is. Not every fix has a wrong
282
+ first attempt. Omit it when the fix was straightforward.
283
+
284
+ ### When to omit sections
285
+
286
+ Omit "False starts" when empty. Omit "Deliberately not done" when nothing
287
+ was deferred. "What this pass changed" and "Verification" are always
288
+ present.
289
+
290
+ ## Step 7: Update PR description footer
291
+
292
+ After posting the summary comment, update the PR description's footer with
293
+ a bullet or two linking to the summary comment. This makes the pivots
294
+ discoverable from the PR body, not buried in the comment thread.
295
+
296
+ Use `gh pr edit` (GitHub) or equivalent to append to the PR body. Add a
297
+ section like:
298
+
299
+ ```markdown
300
+ ## How this PR got here
301
+
302
+ - [Summary of changes from review round](link-to-comment) — what changed
303
+ and why.
304
+ ```
305
+
306
+ If the PR description already has a "How this PR got here" footer, add the
307
+ new bullet to the existing list.
308
+
309
+ ## Retrospective mode
310
+
311
+ When a PR has been through multiple review rounds, a per-round summary may
312
+ not be enough. A retrospective synthesizes the full evolution of the PR
313
+ across all rounds into a single narrative.
314
+
315
+ ### When to use
316
+
317
+ The user requests it explicitly, or after the third or later review round
318
+ on the same PR. A retrospective is a deliberate synthesis, not an automatic
319
+ output. Offer it. Do not produce it without the user's go-ahead.
320
+
321
+ ### What it produces
322
+
323
+ A top-level comment (or a section appended to the PR description) titled
324
+ "How this PR got to this shape." It covers:
325
+
326
+ - **The reverted predecessor or earlier attempt** (if any) — what was
327
+ tried, why it failed, what it taught.
328
+ - **Each review round's effect** — what the review found, what changed as a
329
+ result, and which design decisions pivoted.
330
+ - **False starts across rounds** — fixes that were themselves wrong and
331
+ had to be revised again. These are the most instructive parts.
332
+ - **Deliberately not done** — scope boundaries that held across all rounds.
333
+ - **The tests as deliverable** — if the review process produced or fixed
334
+ tests, note what they cover and why.
335
+
336
+ ### Prose rules
337
+
338
+ Same as the per-round summary: plain English, one idea per sentence, no
339
+ buzzwords, define jargon on first use, conclusion-first. But the
340
+ retrospective is longer because the value is in the evolution, not just the
341
+ final state. Scale to the number of rounds and the complexity of the
342
+ pivots. A PR with one pivot gets a paragraph. A PR with five pivots gets
343
+ sections.
344
+
345
+ ### Relationship to per-round summaries
346
+
347
+ The retrospective does not replace per-round summaries. It synthesizes
348
+ them. If per-round summaries exist as comments, the retrospective links to
349
+ them. If they do not (because the user skipped them in earlier rounds), the
350
+ retrospective covers those rounds from git history and review comments.
351
+
352
+ ## Prediction material
353
+
354
+ Review response discussions are high-signal material for the user decision
355
+ model. When the user tells you how to classify a finding, how to phrase a
356
+ response, or what to include in the summary, watch for preferences about:
357
+
358
+ - Classification threshold (what counts as legitimate vs. unlikely)
359
+ - Response tone and length
360
+ - What to include in the summary (false starts? deliberately not done?)
361
+ - Whether to respond on bot findings or just fix silently
362
+ - When to push back vs. accept a finding
363
+
364
+ Before creating a new prediction, query for an existing one and reinforce
365
+ or adjust it when possible.
@@ -37,10 +37,10 @@ For each finding from the specialists:
37
37
  - Use thatch_memory_recall to search for documented rationale.
38
38
  If the behavior is intentional or an accepted limitation, reject the finding.
39
39
 
40
- 4m. **Mechanical verification (pedantic, no-slop, breadcrumbs, docs, naming, style, comments).** For mechanical findings, verification means:
41
- - The cited text exists at the cited location (already confirmed in step 2).
42
- - The finding is branch-introduced or newly made relevant by the change. Pre-existing issues go in the pre-existing appendix, not rejected.
43
- - The cited text violates the stated guideline, specialist taxonomy, or project norm. Identify the source of truth: the specific guideline, convention, or writing norm being violated.
40
+ 4m. **Mechanical verification (pedantic, no-slop, breadcrumbs, docs, naming, style, comments, structural conventions).** For mechanical findings, verification means:
41
+ - The cited text exists at the cited location (already confirmed in step 2). For PLACEMENT findings, the cited evidence is the file path and the existing directory structure, not quoted code. Verify the file exists at the claimed path and that the existing structure shows the convention the specialist cited.
42
+ - The finding is branch-introduced or newly made relevant by the change. Pre-existing issues go in the pre-existing appendix, not rejected.
43
+ - The cited text violates the stated guideline, specialist taxonomy, or project norm. Identify the source of truth: the specific guideline, convention, writing norm, or existing directory structure being violated.
44
44
  Runtime reachability, producer chains, and intent verification do not apply. A mechanical finding is not a "bug" and does not need a "trigger scenario."
45
45
 
46
46
  4h. **Highlights verification (highlights).** For highlights, verification means:
@@ -95,7 +95,7 @@ A data-shape or reachability finding that lacks the governing-constraint citatio
95
95
 
96
96
  You MUST produce the full report structure below. Do not simplify or omit sections. Every confirmed finding must include all numbered fields. If a section has no entries, write "None" — do not skip the section.
97
97
 
98
- Confirmed LOW findings are mandatory. Do not summarize them away or omit them for being non-functional. Pedantic, no-slop, breadcrumbs, docs, naming, style, and comment findings are first-class review findings when confirmed. Group them under LOW, but include every one.
98
+ Confirmed LOW findings are mandatory. Do not summarize them away or omit them for being non-functional. Pedantic, no-slop, breadcrumbs, docs, naming, style, comment, and structural convention findings are first-class review findings when confirmed. Group them under LOW, but include every one.
99
99
 
100
100
  Confirmed highlights are optional in the sense that most PRs will have none. Do not pad the section with borderline calls. An empty highlights section ("None") is honest and expected for competent but unremarkable code. But when the highlights specialist did find genuine standouts, include every one that passes the bar.
101
101
 
@@ -110,6 +110,93 @@ Keep this section short. Aim for 3-6 steps per workflow. If there are multiple a
110
110
 
111
111
  Apply the same clarity rules as the writing skills: prefer concrete terms over ambiguous ones, translate project-private labels before using them, name object/action/effect for process words, show both sides of contrasts, and show causal middle steps. Do not copy code-comment shorthand when reader-facing behavior is clearer.
112
112
 
113
+ ### Writing review comments
114
+
115
+ The author is already holding the bug mechanics, the state flow, and the
116
+ code context in their head when they read your comment. Write so the
117
+ prose itself takes no effort to parse. The author should spend their
118
+ slow deliberate thinking on the bug, not on decoding your sentences.
119
+
120
+ Think of it as two cognitive systems (Kahneman, *Thinking, Fast and
121
+ Slow*). The author reads the comment with System 1: fast, automatic,
122
+ low-effort pattern matching. They think through the bug mechanics with
123
+ System 2: slow, deliberate, effortful reasoning. Your job is to make
124
+ the comment consumable by System 1 so the author's System 2 budget is
125
+ spent entirely on the technical problem, not on parsing prose.
126
+
127
+ Rules:
128
+
129
+ - **Plain English.** Short sentences. One idea per sentence. If a
130
+ sentence has three clauses, split it into three sentences.
131
+ - **No convoluted conditionals.** If the logic branches, write each
132
+ branch as its own sentence. "If X then Y, unless Z, in which case W"
133
+ forces the reader to hold the entire tree in their head. Three
134
+ sentences let them read sequentially.
135
+ - **Name the file and line.** If the root cause is outside the PR diff,
136
+ say where it is and what the code there does. The author cannot check
137
+ a claim against code they cannot find. "See the guard in
138
+ `auth.go:142` that skips validation when `mode == cached`" is
139
+ scannable. "There's a guard elsewhere that skips validation" is not.
140
+ - **State the bug, then the evidence, then the fix.** Do not make the
141
+ author read three paragraphs to learn what is wrong. Lead with the
142
+ claim. Follow with the proof. End with the remedy.
143
+ - **No buzzwords.** No hedges ("worth considering", "arguably"). No
144
+ escape hatches ("wdyt?", "not blocking", "happy to leave it"). The
145
+ author gets two doors: make the change, or justify not making it.
146
+ - **Translate project-private labels.** If you reference a component by
147
+ its internal name, say what it does in plain English on first use.
148
+ "The reaper (the background goroutine that deletes expired sessions)"
149
+ not just "the reaper."
150
+ - **Playful in delivery, precise in substance.** The humor is in the
151
+ insight itself: a sharp technical observation that happens to be
152
+ funny. Not a joke next to the insight. Not sycophancy. Not "great
153
+ catch!" The playfulness signals engagement, not approval.
154
+
155
+ ### Author sensitivity
156
+
157
+ Authors react to review comments differently. Some are juniors who have
158
+ never had a senior say "this is broken" and may read a blunt finding as
159
+ a judgment on their competence. Some are experienced engineers who are
160
+ simply happy the bug was caught before production. Some are bots that
161
+ want dense, verb-free technical text with maximum semantic content and
162
+ zero social warmth.
163
+
164
+ Before drafting comments, check your memory for observations about the
165
+ PR author:
166
+
167
+ 1. `thatch_memory_recall` with the author's username and "review
168
+ comment reaction" or "review tone sensitivity."
169
+ 2. If you have prior observations, calibrate the comment's social
170
+ framing to the author. The technical content stays the same: the
171
+ finding, the evidence, and the fix do not change. What changes is
172
+ the wrapper: a sentence of context for a junior who may not know
173
+ why the pattern matters; a direct open for a senior who wants you
174
+ to get to the point; dense technical prose for a bot.
175
+ 3. If you have no prior observations about this author, ask the user:
176
+ "I haven't reviewed <author>'s PRs before. Do you know how
177
+ experienced they are and how they tend to react to review findings?"
178
+ Use the answer to calibrate. Save the observation after the review
179
+ round (see below).
180
+ 4. After the author responds to your comments, record what you
181
+ observed. Did they engage with the technical content directly? Did
182
+ they push back on tone? Did they fix it without comment? Save a
183
+ brief observation via `thatch_memory_remember` so future reviews of
184
+ this author's PRs can calibrate from the start:
185
+
186
+ - Label: `review-author-<username>` (in the project store for the
187
+ repo, or global if the author appears across repos).
188
+ - Content: one or two sentences about how this author reacts to
189
+ review findings. Note their seniority if apparent, but focus on
190
+ behavior: do they engage with the finding, push back on framing,
191
+ fix silently, or ask for more context?
192
+ - Confidence: 5-6 for a single observation, higher as you
193
+ accumulate evidence across reviews.
194
+
195
+ Do not record a judgment of the author's skill. Record their
196
+ communication style. "Engages thoroughly with findings, often exceeds
197
+ the ask" is useful. "Is a junior" is not. Skill level changes, and
198
+ the label will outlive the context that created it.
199
+
113
200
  ### Scope
114
201
 
115
202
  - Branch/range reviewed
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: split-overlarge-pr
2
+ name: thatch-split-overlarge-pr
3
3
  description: Split already-completed work from an overlarge PR into human-reviewable, release-safe PRs. Use when the user asks to split a large existing branch/PR, break up completed work, or turn an oversized PR into multiple PRs. Do NOT use for planning brand-new work before code exists.
4
4
  ---
5
5
 
@@ -1,6 +1,6 @@
1
1
  ---
2
- name: ticket-description
3
- description: Draft a ticket description (SYNOPSIS / PROBLEM / BACKGROUND / PROPOSED APPROACH / ACCEPTANCE CRITERIA / RELEASE PLAN / VALIDATION / DEPENDENCIES / NOTES) with bold and italic emphasis on the phrases that carry the meaning, so a reader skimming only the emphasized fragments still gets the story. Use when the user asks you to write, draft, or file a ticket, issue, or work item (Linear or Jira). Do NOT use for PR descriptions (use pr-description), commit messages, or design docs.
2
+ name: thatch-ticket-description
3
+ description: Draft a ticket description (SYNOPSIS / PROBLEM / BACKGROUND / PROPOSED APPROACH / ACCEPTANCE CRITERIA / RELEASE PLAN / VALIDATION / DEPENDENCIES / NOTES) with bold and italic emphasis on the phrases that carry the meaning, so a reader skimming only the emphasized fragments still gets the story. Use when the user asks you to write, draft, or file a ticket, issue, or work item (Linear or Jira). Do NOT use for PR descriptions (use thatch-pr-description), commit messages, or design docs.
4
4
  ---
5
5
 
6
6
  # Writing ticket descriptions
@@ -273,7 +273,7 @@ Before filing or updating a ticket, verify every link you can. Tickets are usual
273
273
 
274
274
  ## Linear labels
275
275
 
276
- When filing a Linear ticket, add obvious labels when they clarify triage. Labels are filing metadata, not ticket-description prose.
276
+ When filing a Linear ticket, add obvious labels when they clarify triage. Labels are filing metadata, not thatch-ticket-description prose.
277
277
 
278
278
  - **Bug** - user-visible broken behavior, incorrect result, regression, or production defect.
279
279
  - **tech-debt** - cleanup, drift, dead code, consistency work, or known design debt with no direct behavior change.
package/bin/release CHANGED
@@ -35,7 +35,7 @@ if git rev-parse -q --verify "refs/tags/v$curr" >/dev/null 2>&1; then
35
35
  echo " To re-tag: git tag -d v$curr && git tag v$curr <commit> && git push origin :refs/tags/v$curr && git push origin v$curr"
36
36
  exit 1
37
37
  else
38
- echo "WARN: npm registry check failed (not a 404 — may be a network issue or npm outage):"
38
+ echo "WARN: npm registry check failed (not a 404 - may be a network issue or npm outage):"
39
39
  echo " $pub_status"
40
40
  echo " Proceeding anyway. Verify v$curr is published before relying on this."
41
41
  echo
@@ -56,7 +56,7 @@ echo
56
56
  read -p "▸ commit, tag, and push v$next? [y/N] " -r
57
57
  echo
58
58
  if [[ ! $REPLY =~ ^[Yy]$ ]]; then
59
- echo "aborted — reverting package.json"
59
+ echo "aborted - reverting package.json"
60
60
  git checkout package.json
61
61
  exit 0
62
62
  fi
@@ -73,4 +73,4 @@ git push --tags
73
73
  echo " ✓ pushed"
74
74
  echo
75
75
 
76
- echo "done — CI will publish v$next to npm"
76
+ echo "done - CI will publish v$next to npm"
package/bin/thatch CHANGED
@@ -105,7 +105,7 @@ switch (command) {
105
105
  case "search": {
106
106
  if (!args[0]) usage();
107
107
  if (args.length > 2) {
108
- console.error(`error: too many arguments — quote multi-word queries: thatch search "${args.join(" ")}"`);
108
+ console.error(`error: too many arguments - quote multi-word queries: thatch search "${args.join(" ")}"`);
109
109
  process.exit(1);
110
110
  }
111
111
  const query = args[0];
@@ -221,7 +221,7 @@ switch (command) {
221
221
 
222
222
  // Resolve the thatch binary path for hooks and MCP config.
223
223
  // If `thatch` is on PATH (npm global install, opencode plugin install),
224
- // use the bare name — it survives updates and works across machines.
224
+ // use the bare name - it survives updates and works across machines.
225
225
  // If not on PATH (local dev checkout), use the absolute path to this script.
226
226
  const thatchOnPath = Bun.which("thatch");
227
227
  const thatchBin = thatchOnPath ?? resolve(process.argv[1] ?? process.execPath);
@@ -271,7 +271,7 @@ switch (command) {
271
271
 
272
272
  case "buffer-batch": {
273
273
  // PostToolBatch hook: append the batch's tool_calls to the session's
274
- // JSONL queue. Silent on success — no stdout — so the next model call
274
+ // JSONL queue. Silent on success - no stdout - so the next model call
275
275
  // is not delayed waiting for an exit code-driven message.
276
276
  try {
277
277
  const raw = await Bun.stdin.text();
@@ -327,12 +327,12 @@ switch (command) {
327
327
  case "flush-tools": {
328
328
  // UserPromptSubmit (Claude Code) / beforeSubmitPrompt (Cursor) hook.
329
329
  // Three priority tiers:
330
- // 1. Extraction nudge — buffered tool interactions are queued for fact
330
+ // 1. Extraction nudge - buffered tool interactions are queued for fact
331
331
  // extraction (carries the JSON payload for thatch-fact-extractor).
332
- // 2. Recall nudge — the user's prompt semantically matches existing
332
+ // 2. Recall nudge - the user's prompt semantically matches existing
333
333
  // memories. Uses the sideband socket to ask the warm MCP server to
334
334
  // embed the prompt and search, avoiding a cold model load here.
335
- // 3. Write nudge — static "did you learn anything?" fallback for
335
+ // 3. Write nudge - static "did you learn anything?" fallback for
336
336
  // conversational turns with no tool use and no memory matches.
337
337
  // --json wraps output as { additional_context: "..." } for Cursor hooks.
338
338
  // Without --json, stdout is plain text (Claude Code style).
@@ -351,20 +351,21 @@ switch (command) {
351
351
  const sessionID = data.session_id ?? data.conversation_id;
352
352
 
353
353
  const { peekQueue, getMissedCount, incrementMissedCount } = await import("../src/extract-queue");
354
- const { buildExtractionPayload } = await import("../src/extraction");
355
354
  const { extractionNudge, claudeWriteNudge, claudeRecallNudge, predictionNudge } = await import("../src/prompts");
356
355
 
357
356
  let repo = "unknown";
358
357
  try { repo = await detectRepo(); } catch {}
359
358
 
360
- // Tier 1: extraction nudge. Peek (don't drain) — the queue persists
359
+ // Tier 1: extraction nudge. Peek (don't drain) - the queue persists
361
360
  // until the agent calls memory_remember, which triggers consumeQueue.
362
361
  // If the agent ignores the nudge, the payload grows on the next flush.
362
+ // The nudge carries the session ID, not the full payload — the
363
+ // sub-agent fetches it via get_extraction_payload.
363
364
  const interactions = sessionID ? peekQueue(sessionID) : [];
364
365
  if (interactions.length > 0) {
365
366
  const missed = sessionID ? getMissedCount(sessionID) : 0;
366
367
  if (sessionID) incrementMissedCount(sessionID);
367
- const output = extractionNudge(interactions.length, missed, "mcp__thatch__memory_remember", buildExtractionPayload(interactions, repo));
368
+ const output = extractionNudge(interactions.length, missed, "mcp__thatch__memory_remember", sessionID ?? "");
368
369
  if (isJson) console.log(JSON.stringify({ additional_context: output }));
369
370
  else console.log(output);
370
371
  break;
@@ -416,7 +417,7 @@ switch (command) {
416
417
  console.log(fallback);
417
418
  }
418
419
  } catch {
419
- // If even the fallback import fails, do nothing — silent failure is
420
+ // If even the fallback import fails, do nothing - silent failure is
420
421
  // preferable to blocking the user's prompt over an unrelated hook.
421
422
  }
422
423
  }
@@ -60,11 +60,11 @@ bin/thatch → CLI: stores|list|show|forget|search|mcp|reminder|hygi
60
60
  | `git.ts` | Parse `owner/repo` from git remote. Worktree-safe fallback chain. |
61
61
  | `db.ts` | SQLite schema, CRUD for entries/stores, brute-force cosine search (`search` = pure scoring, `recall` = search + telemetry stamping), dedup-pair verdict tracking. Prediction tables: matchers, predictions, edges, provenance. `scorePredictionNudge` is the shared auto-fire entry point for both host paths. |
62
62
  | `embeddings.ts` | Lazy-load the embedding model. Expose `queryEmbed`/`passageEmbed` and the model `name` (stored as an informational tag). `MockEmbeddingModel` for tests. |
63
- | `extraction.ts` | Per-session in-memory ring buffer (cap 20) that buffers non-thatch tool interactions and serializes them into the JSON payload the extraction nudge carries. The in-memory pipeline is opencode-only, but the payload builders (`buildExtractionPayload`, `deriveTitle`) are shared by both paths — `extract-queue.ts` imports `deriveTitle`, `bin/thatch` imports `buildExtractionPayload`. `summarizeArgs` is used internally by `buildExtractionPayload`. |
63
+ | `extraction.ts` | Per-session in-memory ring buffer (cap 20) that buffers non-thatch tool interactions and serializes them into the JSON payload the `get_extraction_payload` tool returns. The in-memory pipeline is opencode-only, but the payload builders (`buildExtractionPayload`, `deriveTitle`) are shared by both paths — `extract-queue.ts` imports `deriveTitle`, `mcp.ts` imports `buildExtractionPayload` for the extraction payload provider. `summarizeArgs` is used internally by `buildExtractionPayload`. |
64
64
  | `extract-queue.ts` | File-backed per-session JSONL queue (caps 20, oldest dropped). Shared by the Claude Code and Cursor hook paths, which fire one-shot per event with no cross-call state. This is the MCP-side equivalent of `extraction.ts`. |
65
65
  | `sideband.ts` | Unix domain socket server + client. The MCP server (long-lived, warm model) runs `SidebandServer` so one-shot hook processes can ask it to embed a prompt and search for matches without loading the model themselves. Handles two methods: `match` (recall nudge) and `predictions` (prediction auto-fire). Socket path is a hash of the DB path — both processes compute it independently. |
66
66
  | `prompts.ts` | Text constants: opencode system prompt, compaction context, session-start reminder, prompt-aware recall nudge (`recallNudge` / `claudeRecallNudge`), Claude Code CLAUDE.md instructions, Cursor AGENTS.md instructions, Claude Code hook text. |
67
- | `skills.ts` | `SKILL.md` content for all thatch skills, plus the installer. Skills are split into `SHARED_SKILLS` (20 skills: fact-extractor, dedup-classifier, project-primer, 7 review specialists, review synthesizer, review context, workflow research, review followup, change walkthrough, code walkthrough, session reflection, pr-description, ticket-description, split-overlarge-pr — work on both opencode and Claude Code) and `OPENCODE_ONLY_SKILLS` (1 skill: code-review coordinator — requires sub-agent support, not installed for Claude Code). `installSkills(dir, skills)` defaults to `SHARED_SKILLS`; the opencode plugin passes `[...SHARED_SKILLS, ...OPENCODE_ONLY_SKILLS]`. |
67
+ | `skills.ts` | `SKILL.md` content for all thatch skills, plus the installer. Skills are split into `SHARED_SKILLS` (22 skills: fact-extractor, dedup-classifier, project-primer, 7 review specialists, review synthesizer, review context, code archaeology, review followup, review response, change walkthrough, code walkthrough, session reflection, coding-workflow, thatch-pr-description, thatch-ticket-description, thatch-split-overlarge-pr — work on both opencode and Claude Code) and `OPENCODE_ONLY_SKILLS` (1 skill: code-review coordinator — requires sub-agent support, not installed for Claude Code). `installSkills(dir, skills)` defaults to `SHARED_SKILLS`; the opencode plugin passes `[...SHARED_SKILLS, ...OPENCODE_ONLY_SKILLS]`. |
68
68
 
69
69
  ## Plugin hooks
70
70
 
@@ -76,7 +76,7 @@ bin/thatch → CLI: stores|list|show|forget|search|mcp|reminder|hygi
76
76
  | `experimental.session.compacting` | Marks the session as compacting and appends re-familiarization context so a compacted session still knows thatch exists. |
77
77
  | `experimental.compaction.autocontinue` | Clears the compacting flag so `chat.message` nudges resume. Without this, nudges that instruct tool calls would fire during summary generation where tools are blocked. The `chat.message` hook also clears the flag if it fires for a session still in the compacting set but the incoming message has no compaction-type part — this handles compaction failure, where the session would otherwise be stuck with nudges off forever. |
78
78
  | `tool.execute.after` | Buffers every non-`thatch_*`, non-`skill`, non-`task` tool call into the session's extraction buffer. (Skill/task are excluded — buffering them creates a feedback loop where the nudge triggers a skill load, which gets buffered, which triggers another nudge.) Memory writes (`thatch_memory_remember`) and `thatch_extraction_done` drain the buffer and reset the missed-nudge counter. For child sessions (`childToParent.has(sessionID)`), also tracks metrics: `remember` with `overwrite:false` → new++, `overwrite:true` → updated++, `forget` → deleted++. This is a plugin hook, NOT a bus event — do not move it into the `event` handler; the event bus has no such event and it will silently never fire. |
79
- | `chat.message` | Two priority tiers: (a) if extraction buffer has interactions and the session is NOT in the `extracting` set (direct extraction in progress), **peeks** the buffer (does NOT drain it) and injects a synthetic text part carrying the extraction nudge + JSON payload — the buffer persists until the agent writes a memory or calls `thatch_extraction_done`, so ignored nudges repeat and escalate (polite → insistent → ALL-CAPS) via the `missedNudges` counter; (b) otherwise, embeds the user's prompt text with the in-process warm model, searches `db.search()` across repo + global, and pushes a recall nudge if matches exceed the threshold (default 0.55). The same embedding also feeds the prediction auto-fire: `db.scorePredictionNudge` scores the prompt against prediction matchers and injects a separate `[thatch] User decision model` nudge if any matchers clear the 0.45 threshold. Recall and prediction nudges fire independently (separate try/catch blocks, separate synthetic parts). Skipped entirely while the session is compacting (tool calls are blocked during summary generation). |
79
+ | `chat.message` | Two priority tiers: (a) if extraction buffer has interactions and the session is NOT in the `extracting` set (direct extraction in progress), **peeks** the buffer (does NOT drain it) and injects a synthetic text part carrying the extraction nudge with the session ID and fetch tool name (not the full payload) — the sub-agent calls `thatch_get_extraction_payload` to retrieve the interactions as a tool response, keeping the full payload out of the main session's context window. The buffer persists until the agent writes a memory or calls `thatch_extraction_done`, so ignored nudges repeat and escalate (polite → insistent → ALL-CAPS) via the `missedNudges` counter; (b) otherwise, embeds the user's prompt text with the in-process warm model, searches `db.search()` across repo + global, and pushes a recall nudge if matches exceed the threshold (default 0.55). The same embedding also feeds the prediction auto-fire: `db.scorePredictionNudge` scores the prompt against prediction matchers and injects a separate `[thatch] User decision model` nudge if any matchers clear the 0.45 threshold. Recall and prediction nudges fire independently (separate try/catch blocks, separate synthetic parts). Skipped entirely while the session is compacting (tool calls are blocked during summary generation). |
80
80
  | `event` | Subscribes to all session bus events. `session.created`: records `childToParent` + `parentSnapshots` (shallow copy of the parent's buffer for snapshot-aware drain), then sends the session-start reminder via `client.session.prompt` carrying the hygiene heartbeat (pending dedup pairs, stale count, orphaned branch memories) when any signal is non-zero. `session.status` (idle): if the session is a child, drains its snapshot, fires a toast with `childMetrics`, and deletes the child session; if the session is a parent with pending tool interactions and not already extracting, calls `triggerExtraction` to create a direct-extraction child. `session.error`: requeues the parent's buffer (child died without draining). `session.deleted`: cleans up `childToParent`, `parentSnapshots`, `childMetrics`, and `extracting`. `session.compacted`: clears the compacting flag so `chat.message` nudges resume. |
81
81
  | `dispose` | Closes the DB. |
82
82
 
@@ -186,12 +186,13 @@ extraction cycle
186
186
  → nudge path (fallback): if triggerExtraction throws (create or prompt
187
187
  fails), the `extracting` set clears and the next chat.message sees
188
188
  pending entries with no extracting flag → peeks the buffer and injects
189
- a nudge part with JSON payload; missed nudges escalate (polite →
189
+ a nudge part with session ID and fetch tool name; missed nudges escalate (polite →
190
190
  insistent → ALL-CAPS) via the missedNudges counter
191
191
  → drain: thatch_memory_remember (or thatch_extraction_done) clears the
192
192
  buffer and resets the missed-nudge counter
193
- → agent loads thatch-fact-extractor skill, saves facts via
194
- thatch_memory_remember
193
+ → agent dispatches a sub-agent that calls thatch_get_extraction_payload
194
+ to fetch the queued interactions, loads thatch-fact-extractor skill,
195
+ saves facts via thatch_memory_remember
195
196
  → MCP path (Claude Code/Cursor): unchanged — no SDK client, no child
196
197
  sessions; extract-queue.ts + flush-tools drives the nudge via hooks
197
198