@jeffober/thatch 0.2.5 → 0.2.6
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 +48 -25
- package/artifacts/skills/common.md +1 -0
- package/artifacts/skills/thatch-code-archaeology.md +9 -0
- package/artifacts/skills/thatch-code-review.md +16 -8
- package/artifacts/skills/thatch-plan-refinement.md +33 -4
- package/artifacts/skills/thatch-refine-personal.md +5 -0
- package/artifacts/skills/thatch-refine.md +7 -1
- package/artifacts/skills/thatch-review-context.md +55 -2
- package/artifacts/skills/thatch-review-followup.md +4 -4
- package/artifacts/skills/thatch-review-response.md +1 -1
- package/artifacts/skills/thatch-review-synthesizer.md +21 -1
- package/bin/thatch +21 -12
- package/docs/dev/README.md +11 -8
- package/docs/dev/features/README.md +1 -1
- package/docs/dev/features/behavior-engine.md +10 -0
- package/docs/dev/features/cli.md +2 -1
- package/docs/dev/features/commands.md +40 -9
- package/docs/dev/features/cross-session-chat.md +85 -21
- package/docs/dev/features/database.md +4 -4
- package/docs/dev/features/nudge-pipeline.md +14 -0
- package/docs/dev/features/opencode-plugin.md +41 -15
- package/docs/dev/features/prediction-engine.md +27 -1
- package/docs/dev/features/qa-system.md +19 -3
- package/docs/dev/features/session-lifecycle.md +6 -6
- package/docs/dev/features/session-tabs.md +11 -5
- package/docs/dev/gotchas.md +21 -0
- package/docs/dev/mcp-parity.md +3 -3
- package/docs/dev/skills.md +11 -8
- package/docs/user/README.md +19 -12
- package/docs/user/behavior-engine.md +7 -1
- package/docs/user/cli.md +2 -1
- package/docs/user/code-review.md +43 -2
- package/docs/user/commands.md +27 -7
- package/docs/user/cross-session-chat.md +47 -20
- package/docs/user/prediction-engine.md +12 -1
- package/docs/user/skills.md +8 -6
- package/package.json +1 -1
- package/src/capabilities.ts +20 -6
- package/src/chat.ts +39 -17
- package/src/commands.ts +109 -18
- package/src/db.ts +23 -2
- package/src/mcp.ts +6 -2
- package/src/opencode/tui-plugin.ts +29 -9
- package/src/opencode/v2.ts +247 -66
- package/src/prompts.ts +54 -6
- package/src/runtime.ts +122 -17
- package/src/scoring-engine.ts +40 -9
- package/src/session-tab-shared.ts +55 -4
- package/src/skills.ts +5 -9
- package/src/tab-state.ts +125 -0
- package/src/tool-defs.ts +42 -21
|
@@ -58,7 +58,7 @@ the shared runtime needs. It is the lifecycle-level sibling of `CoreContext`
|
|
|
58
58
|
| Capability | v1 source | v2 source | v2 strategy |
|
|
59
59
|
|---|---|---|---|
|
|
60
60
|
| bus events | `event` hook | `event.subscribe` (raw SSE) | client-side location filter; location-less events resolve via `session.get` (`{sessionID}` input) and drop unresolvable (mirrors v1's server-side filter); events for plugin-created extraction children pass by ID (they live in the project directory, which differs from the instance directory on below-root launches) |
|
|
61
|
-
| incoming message | `chat.message` hook | `session.hook("prompt")` | the hook awaits the runtime; nudge injections append to the OUTBOUND request's
|
|
61
|
+
| incoming message | `chat.message` hook | `session.hook("prompt")` | the hook awaits the runtime; nudge injections append to the OUTBOUND request's user message whose text matches the entry's stored prompt, in the context hook (v2 wire `Message` carries parts in `content`, not `parts`). Entries form a per-session set (cap 3, oldest evicted) so a prompt queued mid-turn keeps its own entry instead of evicting the running turn's; a prompt whose nudge computation comes up empty clears the session's whole set, so the running turn's remaining rounds lose their block in that one case (smaller than the staleness it prevents: an entry left behind would match its own prompt text in later requests' session history and replay a stale block there) |
|
|
62
62
|
| system prompt | `experimental.chat.system.transform` | `session.hook("context")` | mutates the request's system array; the runtime's plain strings convert to `{type: "text", text}` SystemPart objects at the boundary |
|
|
63
63
|
| compaction | `experimental.session.compacting` + `.autocontinue` + `session.compacted` | `session.hook("compaction")` | flag lands; context-injection surface unverified |
|
|
64
64
|
| wrap-up commands | `command.execute.before` + command files | `command.transform` + `CommandEditor.add` | registered in code: `execute` arms the greenlight check (`onCommandExecuteBefore`) and delivers the same prompt body the v1 file carries, substituting the invocation's prompt text for `$ARGUMENTS` (v2's `session.prompt` does no template expansion); the runtime installs only ACTION command files on v2 and REMOVES stale wrap-up files from earlier v1 runs (a file and a registered command with the same name would collide) |
|
|
@@ -66,14 +66,14 @@ the shared runtime needs. It is the lifecycle-level sibling of `CoreContext`
|
|
|
66
66
|
| tool buffering | `tool.execute.after` hook | `tool.hook("execute.after")` | wired: feeds the same extraction buffer; `Tool.Result.content` is flattened (string passes through, content-part arrays contribute their text parts); the title is derived from the tool name and args (`deriveTitle`), since v2's `Tool.Result.output` is a typed output value, not a title |
|
|
67
67
|
| noReply deliveries | `promptAsync` with `noReply` | none | gated off via `HostCapabilities.noReplyDelivery` -- chat echoes and the session-start reminder are skipped on v2 (delivering them would start real model turns: a feedback loop) |
|
|
68
68
|
| synthetic wake deliveries | `promptAsync` with `synthetic` parts | `session.synthetic` endpoint | watcher + chat wake nudges route to v2's synthetic endpoint (TUI-hidden), matching v1 |
|
|
69
|
-
| child sessions | `client.session.create/promptAsync/prompt/delete` | `session.create/prompt` | create+prompt supported (a missing id throws into the extraction fallback); delete
|
|
69
|
+
| child sessions | `client.session.create/promptAsync/prompt/delete` | `session.create/prompt` | create+prompt supported (a missing id throws into the extraction fallback); delete rides `session.remove` (upstream #52387, in v2.0.22), runtime-guarded like compact -- an older host keeps the documented gap (the session picker accumulates one `thatch-extraction` entry per extraction, and `-c` "continue last session" logic that picks the newest top-level session will land in an extraction child after any session that triggered extraction) |
|
|
70
70
|
| session status | `client.session.status` | none | returns `{}`; the wake gate treats unknown as idle and the event-fed status map does the gating |
|
|
71
71
|
| session messages | `client.session.messages` | `session.context` | mapped into the v1 `{info: {role}, parts}` shape, so the wrap-up greenlight check works (the promise domain has no `message` accessor) |
|
|
72
72
|
| session list | `client.session.list` | none | degrade (`-c` resume listing loses its data source) |
|
|
73
|
-
| compaction trigger | `client.tui.executeCommand("session_compact")` |
|
|
74
|
-
| toasts | `client.tui.showToast` |
|
|
75
|
-
|
|
|
76
|
-
| session tabs | n/a | rpc event bridge |
|
|
73
|
+
| compaction trigger | `client.tui.executeCommand("session_compact")` | `session.compact({sessionID})` | wired behind a runtime guard: upstream [#52385](https://github.com/anomalyco/opencode/pull/52385) exposed `session.compact` on the promise domain (in the installed v2.0.25), newer than the pinned dev types - an older host degrades to a logged no-op (the wrap-up checklist and flush still run, the automatic compaction is skipped) |
|
|
74
|
+
| toasts | `client.tui.showToast` | toast rpc event | wired: every runtime toast call site (alerts, extraction metrics, chat registration, blocked wrap-ups, watcher rearm notices) emits the bridge's `toast` event, which the `./tui` plugin shows via `ui.toast.show`; the pump never translates it (TUI-only by nature). Best-effort by contract: a TUI-less environment or an rpc-less SDK floor shows nothing, silently |
|
|
75
|
+
| wrap-up exit | `client.tui.publish` | exit-tab-closed rpc event | the wrap-up exit records the session's watcher deaths synchronously (the runtime, before the chat unregister - `sessionDied` places the death row by the chat row, so the order is load-bearing) and then emits `rpc.thatch-tabs.exit-tab-closed`, a TUI-ONLY close of the session's OWN tab (the `./tui` plugin's `ui.tabs.close`): the tab-scoped semantic upstream [anomalyco/opencode#50984](https://github.com/anomalyco/opencode/issues/50984) asks for, since the daemon hosts every tab and `app.exit` would take down unrelated sessions. The pump deliberately never translates this event (a second death pass would erase the durable death row the first one wrote; the session_tab_close tool's `tab-closed` is the only death-translating event). Degrades to a logged no-op without the rpc surface; a TUI with no tab for the session (headless run, tabs off) never acts - the event is ephemeral |
|
|
76
|
+
| session tabs | n/a | rpc event bridge | `thatch_session_tab` (v2Only) spawns a detached subordinate session (create -> move -> chat-register -> emit `rpc.thatch-tabs.tab-opened` -> prompt); the `./tui` CLI plugin consumes the event and calls `ui.tabs.open` (open-without-focus, idempotent). `thatch_session_tab_close` emits `rpc.thatch-tabs.tab-closed {sessionID, chatName}` - the pump translates it into the runtime's confirmed-death path. The registration's third event, `exit-tab-closed` (the wrap-up exit's), is TUI-only - see the wrap-up exit row |
|
|
77
77
|
| tab close | n/a | no session.deleted on v2 tab close | bounded 2026-10-07: watcher death detection cancels a closed tab's watchers once its pending events age past `THATCH_WATCH_DEATH_MINUTES` (default 120) while deliveries throw 404-class errors, and announces the death to live same-project sessions; the roster row itself still lingers until the reaper. NOTE: an earlier version of this row claimed deliveries "fail their idle gate" - wrong: the v2 gate opens (the status stub reads as idle) and the PROMPT fails; same leak, different link |
|
|
78
78
|
| wake gate after reload | `session.status` event map | map rehydrates empty | known gap: v2's fetchStatuses stub is structural (the promise context has no session.status), so in the reload window - until the session's next status event - the proactive-prompt gate is fully open |
|
|
79
79
|
| dispose | `dispose` hook | cleanup returned from `setup` | must be idempotent: v2 auto-reloads plugins on file change, and a non-idempotent cleanup doubles pollers/pumps/nudges. Volatile runtime state (extraction buffer + accepted entries, child bookkeeping, wrap-up arms, watcher definitions) is journaled to the `runtime_state` table - instance-scoped by the writing location's directory - and rehydrated on the next `setup()`: rows from the same process AND directory (a reload) all restore; child rows carry a kind marker (extraction vs task-dispatched sub-agent, defaulting to extraction for pre-marker rows) so only extraction children restore the `extracting`/`extractionChildren` sets; rows from a dead process are pruned unless the session is the `-c`/`-s` startup resume, which inherits recovery-safe state only (buffer requeued, no live child bookkeeping - the child died with the old process). Exception: a dead process's watcher definitions are kept as DORMANT rows (no polling, no delivery) instead of pruned - resuming the session re-arms them on its first message (a `chat.message` scan: own rows re-arm, same-directory rows of still-dead sessions earn the live session a one-line death notice gated on the owning pid being dead, rows past the watcher TTL plus a 24h grace age out). Restored sessions on v1 get a synthetic noReply re-attach notice (v2 has no turn-free delivery and skips it). The chat poller re-hosts the instance's journaled hosted set on reload, so pending chat mail wakes sessions the reload left asleep and heartbeats keep the reaper from reaping live registrations. The embedding model is refcounted per db path, so N location instances share one resident model |
|
|
@@ -90,20 +90,46 @@ suite runs green against a v2 serve.
|
|
|
90
90
|
Still unverified against the live binary (static analysis only):
|
|
91
91
|
|
|
92
92
|
- Whether `session.hook("prompt")` fires for `session.synthetic` inbox
|
|
93
|
-
items. If it does not, `pendingInjections` from the prior real turn
|
|
94
|
-
appended to the synthetic turn's outbound request.
|
|
93
|
+
items. If it does not, `pendingInjections` from the prior real turn would
|
|
94
|
+
be appended to the synthetic turn's outbound request. Structurally
|
|
95
|
+
bounded 2026-10-12: each `pendingInjections` entry is bound to the prompt
|
|
96
|
+
text it was computed from, and the context hook injects it only into a
|
|
97
|
+
request user message whose joined text equals that prompt - a replay onto
|
|
98
|
+
any other message (synthetic turn, missed prompt hook, failed
|
|
99
|
+
`onChatMessage`) is skipped. Text equality is the tightest binding
|
|
100
|
+
available, not an airtight one: an identical prompt re-submitted after a
|
|
101
|
+
missed prompt hook still matches and replays; the next prompt-hook run
|
|
102
|
+
that fires is the recovery. The prompt-time block still re-fires on every
|
|
103
|
+
round of its OWN turn (v1's persistent-synthetic-part semantics), and the
|
|
104
|
+
block carries a snapshot footer stating the labels are a submission-time
|
|
105
|
+
snapshot and pointing at the live re-check tool.
|
|
95
106
|
- Whether `SessionGenerate.messages` are fresh objects per model call within
|
|
96
|
-
a turn.
|
|
97
|
-
|
|
107
|
+
a turn. The context hook appends injections to the matched message's
|
|
108
|
+
content, which changes the text the binding check compares. With fresh
|
|
109
|
+
objects per call (what the tests assume), the prompt-time block re-fires
|
|
110
|
+
on every round of its own turn. With reused objects, round 1's
|
|
111
|
+
injections would change the matched message's text, so the binding check
|
|
112
|
+
would skip rounds 2 and on - within-turn replay would break after round
|
|
113
|
+
1 instead of duplicating.
|
|
114
|
+
- Entries outliving their session: `pendingInjections` is keyed by session
|
|
115
|
+
id and never swept on session end. Bounded - a per-session set capped at
|
|
116
|
+
3 entries, and the map itself dies with the adapter activation - but a
|
|
117
|
+
session's entries do persist after the session is gone, pre-existing.
|
|
118
|
+
- Cross-directory hook dispatch scope: the event pump filters events by
|
|
119
|
+
directory, but the session hooks (`prompt`, `context`, `compaction`)
|
|
120
|
+
have no such filter - whether a hook can fire for another directory's
|
|
121
|
+
session is unverified, pre-existing.
|
|
98
122
|
- An npm-installed copy (not the dev checkout, which has devDependencies
|
|
99
123
|
present) loading under v2 - the isolation rule's real-world proof.
|
|
100
124
|
|
|
101
|
-
The
|
|
102
|
-
|
|
103
|
-
[anomalyco/opencode#50984](https://github.com/anomalyco/opencode/issues/50984)
|
|
125
|
+
The wrap-up exit closes the session's own tab over the session-tab rpc
|
|
126
|
+
bridge - the tab-scoped close upstream
|
|
127
|
+
[anomalyco/opencode#50984](https://github.com/anomalyco/opencode/issues/50984)
|
|
128
|
+
asks for. Known degrade: a TUI with no tab for the session (headless run,
|
|
129
|
+
tabs off) never acts - the event is ephemeral, nobody is listening.
|
|
104
130
|
Install opencode-v2 (brew formula `anomalyco/tap/opencode-v2`; conflicts
|
|
105
|
-
with the v1 formula's binary name, so it cannot coexist) and re-run the
|
|
106
|
-
live suite against it.
|
|
131
|
+
with the v1 formula's binary name, so it cannot coexist) and re-run the
|
|
132
|
+
QA live suite against it.
|
|
107
133
|
|
|
108
134
|
## User setup compatibility
|
|
109
135
|
|
|
@@ -141,6 +141,8 @@ later — land below that floor and accumulate. The dedup pass finds them:
|
|
|
141
141
|
`[thatch] User decision model` header.
|
|
142
142
|
- 0-evidence predictions use "you may prefer"; predictions with evidence use
|
|
143
143
|
"you tend to".
|
|
144
|
+
- The block ends with a snapshot footer (see the block example below): the
|
|
145
|
+
labels are a submission-time snapshot, not a live view.
|
|
144
146
|
- Independent try/catch — a prediction failure does not block recall or
|
|
145
147
|
behavior.
|
|
146
148
|
- Toast: `[thatch] N predictions surfaced`.
|
|
@@ -159,11 +161,27 @@ later — land below that floor and accumulate. The dedup pass finds them:
|
|
|
159
161
|
The auto-fire block looks like:
|
|
160
162
|
|
|
161
163
|
```text
|
|
162
|
-
|
|
164
|
+
[thatch] User decision model
|
|
163
165
|
- [0.72 conf, 4 tests] When {matcher}: you tend to {prediction}
|
|
164
166
|
- [0.61 conf, 2 tests] When {matcher2}: you may prefer {prediction2}
|
|
167
|
+
Snapshot: these labels reflect the prediction store as of this message's
|
|
168
|
+
submission. Predictions updated after submission (including during this
|
|
169
|
+
turn) do not refresh it; re-check with thatch_prediction_query before
|
|
170
|
+
relying on them.
|
|
165
171
|
```
|
|
166
172
|
|
|
173
|
+
The footer's tool name follows the host: opencode sees
|
|
174
|
+
`thatch_prediction_query`; MCP hosts (Claude Code, Cursor) see
|
|
175
|
+
`mcp__thatch__prediction_query`. The renderer resolves the spelling
|
|
176
|
+
through the caller's `ToolNamer`.
|
|
177
|
+
|
|
178
|
+
The snapshot footer exists because the block is computed once when the
|
|
179
|
+
message is submitted and stays attached to it for the whole turn (v1:
|
|
180
|
+
synthetic part on the stored message; v2: `pendingInjections` re-injected
|
|
181
|
+
into every outbound request of the turn). Predictions the conversation
|
|
182
|
+
mutates mid-turn do not refresh the block; the footer tells the consumer
|
|
183
|
+
the labels are a submission-time snapshot and points at the live tool.
|
|
184
|
+
|
|
167
185
|
Prompt instructions: follow strong predictions silently, surface weak or
|
|
168
186
|
competing predictions to the user, update the model when the user responds.
|
|
169
187
|
|
|
@@ -280,6 +298,14 @@ prediction_dedup_pairs(
|
|
|
280
298
|
)
|
|
281
299
|
```
|
|
282
300
|
|
|
301
|
+
Timestamp semantics: a matcher's `updated_at` is the last change to the
|
|
302
|
+
matcher row OR its edge set — `createEdge` bumps it when a new edge lands,
|
|
303
|
+
and deleting an item bumps the matchers whose edges cascade away. The
|
|
304
|
+
matcher's own text may legitimately be older than its links (a corrected
|
|
305
|
+
prediction re-links the existing matcher without touching its text).
|
|
306
|
+
An item's `updated_at` is written at creation and then moved by the last
|
|
307
|
+
confidence change.
|
|
308
|
+
|
|
283
309
|
## Key invariants
|
|
284
310
|
|
|
285
311
|
1. **The firewall principle.** The model that uses predictions cannot grade
|
|
@@ -70,9 +70,11 @@ execution directly via bun assertions. These are fast and run in parallel.
|
|
|
70
70
|
Spawn real opencode sessions. Test the full pipeline with a real model. These
|
|
71
71
|
cost model tokens and have a 20-minute per-use-case timeout. Live use cases
|
|
72
72
|
check for `opencode` on PATH at runtime: if it is present they run; if not,
|
|
73
|
-
they skip with `[MANUAL]`.
|
|
73
|
+
they skip with `[MANUAL]`. Six (UC-006, UC-010, UC-013, UC-083, UC-084,
|
|
74
|
+
and UC-085) are
|
|
74
75
|
`manualOnly: true` for infrastructure reasons (Cursor hook lifecycle, nested
|
|
75
|
-
opencode session timeout, compaction trigger
|
|
76
|
+
opencode session timeout, compaction trigger, sub-agent support, skill
|
|
77
|
+
loading, and a PR with prior review comments). Run them locally with
|
|
76
78
|
`mise run qa-live`.
|
|
77
79
|
|
|
78
80
|
| UC | Name | What it tests |
|
|
@@ -86,6 +88,20 @@ opencode session timeout, compaction trigger). Run them locally with
|
|
|
86
88
|
| UC-013 | compaction-context | compaction hook injects context |
|
|
87
89
|
| UC-019 | archived-memory-lifecycle | archive -> search excludes -> unarchive -> search includes |
|
|
88
90
|
| UC-020 | extraction-escalation | polite -> insistent -> ALL-CAPS over missed nudges |
|
|
91
|
+
| UC-038 | no-save-run | an extraction child that finds nothing worth saving (no toast) |
|
|
92
|
+
| UC-043 | compaction-suppression | extraction nudges suppressed while an extraction child runs |
|
|
93
|
+
| UC-077 | child-session-drain | direct extraction: the child drains the parent's queue |
|
|
94
|
+
| UC-078 | child-session-error | direct extraction: the child errors, held entries requeue |
|
|
95
|
+
| UC-079 | child-session-deletion | direct extraction: the child is deleted, entries requeue |
|
|
96
|
+
| UC-080 | parent-session-deletion | parent deleted mid-extraction, cleanup completes |
|
|
97
|
+
| UC-083 | review-coordinator | the multi-agent review coordinator pipeline |
|
|
98
|
+
| UC-084 | review-walkthrough | the change-walkthrough skill on a real diff |
|
|
99
|
+
| UC-085 | review-followup | follow-up round verification against prior review comments |
|
|
100
|
+
| UC-086 | review-response | author-side response to incoming review comments |
|
|
101
|
+
| UC-087 | review-no-subagents | the solo review path on hosts without sub-agents |
|
|
102
|
+
| UC-098 | chat-cross-session | two real opencode sessions exchange chat messages |
|
|
103
|
+
| UC-119 | session-tab-spawn | the session_tab spawn flow end to end |
|
|
104
|
+
| UC-122 | review-mode-commands | /thatch/self-review and /thatch/peer-review overlay efficacy (seeded fixture) |
|
|
89
105
|
|
|
90
106
|
### mise tasks
|
|
91
107
|
|
|
@@ -235,7 +251,7 @@ No separate QA docs directory exists. QA use cases live in `tests/qa/` as execut
|
|
|
235
251
|
1. **Automatable use cases never spawn an LLM.** They test CLI, hooks, DB, and
|
|
236
252
|
tool execution directly.
|
|
237
253
|
2. **Live use cases spawn real opencode sessions and cost model tokens.** They
|
|
238
|
-
skip with `[MANUAL]` when `opencode` is not on PATH;
|
|
254
|
+
skip with `[MANUAL]` when `opencode` is not on PATH; six are
|
|
239
255
|
`manualOnly: true` for infrastructure reasons.
|
|
240
256
|
3. **`QA_DRY_RUN=1` lists use cases without executing.** Use this for CI and
|
|
241
257
|
coverage review.
|
|
@@ -73,12 +73,12 @@ When a parent session (no parentID) goes idle:
|
|
|
73
73
|
|
|
74
74
|
User-invoked slash commands, shipped as command markdown synced by the plugin (see `src/commands.ts`). The template instructs the model to flush pending fact extraction (`thatch_get_extraction_payload` + `thatch_extraction_done`), finish promised memory writes, and surface unaddressed todos -- then end its response with a greenlight token (`THATCH_COMPACT_READY` / `THATCH_EXIT_READY`) only when safe to proceed. Text typed after the command is forwarded into a labeled `# User Message` section ahead of the `# Pre-*-wrap-up` checklist section (opencode's `$ARGUMENTS` substitution): the headers give the user's words provenance, so they cannot be misread as part of the wrap-up instructions. opencode's substitution is purely mechanical with no default-value form (verified against v1.18.30), so the template cannot switch on emptiness; instead the section always carries a fallback line telling the model to treat an empty section as n/a (bare command, no user text).
|
|
75
75
|
|
|
76
|
-
1. `command.execute.before` arms the session in `pendingWrapUp` when the command runs
|
|
77
|
-
2. On the session's next idle, the plugin fetches the session's messages
|
|
78
|
-
3. Token present: trigger the
|
|
79
|
-
- compact: `client.tui.executeCommand
|
|
80
|
-
- exit: unregister the session from the chat directory
|
|
81
|
-
4. Token absent: warning toast pointing at the blockers in the response, then fall through -- the model may have buffered tool interactions that still need extraction
|
|
76
|
+
1. `command.execute.before` arms the session in `pendingWrapUp` when the command runs (on v2 the registered command's `execute` runs the same arming through `onCommandExecuteBefore`)
|
|
77
|
+
2. On the session's next idle, the plugin fetches the session's messages (v1: SDK client; v2: `session.context`, mapped into the v1 shape) and checks the final assistant message's trailing text for the token (trimmed, exact match)
|
|
78
|
+
3. Token present: trigger the host action and return early -- compaction is starting (the checklist drained the buffer) or the session is closing
|
|
79
|
+
- compact: v1 dispatches the TUI's `session_compact` command (`client.tui.executeCommand` -- the execute-command route only accepts legacy alias names; `session_compact` maps to the TUI's `session.compact` action, the same thing the built-in `/compact` runs). v2 calls `session.compact({sessionID})` on the promise domain directly (upstream #52385), runtime-guarded because the pinned dev types predate the surface -- an older host degrades to a logged no-op
|
|
80
|
+
- exit: record the session's watcher deaths first (`watchers.sessionDied` -- BEFORE the unregister below, because the death row is placed by the session's chat row and the unregister deletes it; the runtime also captures the project there, so the async death-notice delivery still places after the row is gone), unregister the session from the chat directory (a greenlit exit's host is about to vanish; other sessions must stop addressing mail to it), then close the session out on the host. v1 publishes the `app.exit` TUI keymap command directly (`client.tui.publish` -- no exit alias exists). v2 emits the session-tab bridge's `exit-tab-closed` event, a TUI-ONLY close of the session's OWN tab: the daemon hosts every tab, so `app.exit` would take down unrelated sessions, and the pump deliberately never translates this event (the deaths were already recorded; a second pass would erase the durable death row). The unregister is plugin-side only, after the death recording: the template deliberately does NOT ask the model to call `thatch_chat_unregister`, because a mid-turn unregister would delete the chat row before the death placement could use it. Compaction deliberately does NOT unregister - the session continues
|
|
81
|
+
4. Token absent: warning toast pointing at the blockers in the response, then fall through -- the model may have buffered tool interactions that still need extraction (on v2 the toast rides the bridge's toast event; a TUI-less environment shows nothing - the blockers themselves are in the model's response)
|
|
82
82
|
|
|
83
83
|
### Command file install
|
|
84
84
|
|
|
@@ -82,11 +82,17 @@ Key properties:
|
|
|
82
82
|
## The cross-entry contract
|
|
83
83
|
|
|
84
84
|
`src/session-tab-shared.ts` (SDK-free, per the isolation rule in
|
|
85
|
-
`src/index.ts`) holds the rpc definition (`thatch-tabs`: `tab-opened
|
|
86
|
-
`tab-closed` -
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
85
|
+
`src/index.ts`) holds the rpc definition (`thatch-tabs`: `tab-opened`;
|
|
86
|
+
`tab-closed` - emitted by the close tool (below) and translated by the v2
|
|
87
|
+
pump into the runtime's confirmed-death path, which also records a
|
|
88
|
+
`chat_tab_closed` marker the chat roster's open-tab reader uses to demote
|
|
89
|
+
the row instantly ([cross-session-chat.md](cross-session-chat.md));
|
|
90
|
+
`exit-tab-closed` - the
|
|
91
|
+
wrap-up exit's TUI-only close, which the runtime records the exit's deaths
|
|
92
|
+
itself before emitting, so the pump deliberately never translates that
|
|
93
|
+
one; and `toast` - every runtime toast's TUI-only feedback channel), the
|
|
94
|
+
subordinate prompt builder, and the validators. The TUI entrypoint may
|
|
95
|
+
import only `@opencode/plugin/tui` and this module.
|
|
90
96
|
|
|
91
97
|
## Interactions
|
|
92
98
|
|
package/docs/dev/gotchas.md
CHANGED
|
@@ -160,6 +160,12 @@ here first. These are the things that have already cost time.
|
|
|
160
160
|
- **`bun test` does not typecheck**, and `tsconfig.json` excludes `tests`. Test
|
|
161
161
|
type errors are editor-only noise unless you run `tsc` on the test files
|
|
162
162
|
directly. Keep test files type-clean anyway so the editor stays quiet.
|
|
163
|
+
- **`mise run test` (bare `bun test`) also executes the QA barrels**
|
|
164
|
+
(`tests/qa/*/index.test.ts`), which spawn REAL opencode sessions and spend
|
|
165
|
+
model tokens, one use case at a time - a full run can hang for many minutes.
|
|
166
|
+
The unit gate is `mise run check` (`bun test tests/*.test.ts`); QA runs
|
|
167
|
+
belong to the `mise run qa-auto` / `qa-live` tasks, which select a UC
|
|
168
|
+
subset by name.
|
|
163
169
|
|
|
164
170
|
## Config and notifications
|
|
165
171
|
|
|
@@ -361,3 +367,18 @@ instead of throwing "Cannot use a closed database". Two rules keep this intact:
|
|
|
361
367
|
chat canary). Local plugin specs handed to a loader explicitly
|
|
362
368
|
(`"plugin": ["./path"]`) can point at a directory on v1 - it is only the
|
|
363
369
|
AUTO-discovery scan that is files-only.
|
|
370
|
+
|
|
371
|
+
## A promptSession wrapper must stay synchronous or fire-and-forget delivery assertions misfire
|
|
372
|
+
|
|
373
|
+
- **Symptom**: tests that assert on wake delivery immediately after a hook
|
|
374
|
+
invocation see zero calls; deliveries land one microtask late.
|
|
375
|
+
- **Cause**: the delivery guard (`guardDelivery`, src/runtime.ts) sits on
|
|
376
|
+
every wake path, and its directory-existence check was first written as
|
|
377
|
+
`await stat(...)`. Fire-and-forget callers (`void caps.promptSession(...)`)
|
|
378
|
+
and the repo's mock contract (calls recorded "synchronously inside the
|
|
379
|
+
mock", asserted with no intervening await) observe the wrapped call
|
|
380
|
+
before an extra microtask resolves.
|
|
381
|
+
- **Fix**: the existence check is synchronous (`existsSync`), so the guard
|
|
382
|
+
adds no microtask between the gate and the wrapped call. If you ever
|
|
383
|
+
replace it with an async check, the callers' fire-and-forget timing -
|
|
384
|
+
and the tests that pin it - break again. (2026-10-12, wake-delivery-guard)
|
package/docs/dev/mcp-parity.md
CHANGED
|
@@ -22,10 +22,10 @@ This document maps the feature parity and documents the remaining gaps.
|
|
|
22
22
|
| **System prompt** (store names, usage rules) | `experimental.chat.system.transform` — dynamic, repo baked in at runtime | CLAUDE.md static text appended by `thatch setup`; repo auto-detected by the MCP server at startup | AGENTS.md static text appended by `thatch setup`; repo auto-detected at startup | **Approximate** — static text persists through compaction, so the agent retains the usage instructions, but the dynamic per-turn refresh is lost |
|
|
23
23
|
| **Session-start reminder** (recall nudge + hygiene heartbeat) | `session.created` event → `client.session.prompt` injects a synthetic message | `SessionStart` hook → `thatch reminder`; stdout becomes context | `sessionStart` hook → `thatch reminder --json`; output is `additional_context` JSON | **Full** |
|
|
24
24
|
| **Compaction context** (re-familiarize after compaction) | `experimental.session.compacting` hook appends context to the compaction output | `PostCompact` hook — side-effects only, **cannot inject context** | No equivalent hook | **Gapped** — see below |
|
|
25
|
-
| **Wrap-up commands** (`/thatch/compact`, `/thatch/exit`) | Command markdown synced to `$XDG_CONFIG_HOME/opencode/command/thatch/` at plugin init; `command.execute.before` arms the session and the next idle resolves the greenlight token into
|
|
26
|
-
| **On-demand actions** (`/thatch/defrag`, `extract`, `hygiene`, `
|
|
25
|
+
| **Wrap-up commands** (`/thatch/compact`, `/thatch/exit`) | Command markdown synced to `$XDG_CONFIG_HOME/opencode/command/thatch/` at plugin init; `command.execute.before` arms the session and the next idle resolves the greenlight token into the host action (compact: the TUI compact command on v1, `session.compact` on v2; exit: `app.exit` on v1, the session's own tab closing on v2) | No equivalent — no plugin host to arm the check or trigger the action | No equivalent | **Gapped** — needs the plugin's SDK client and host action routes |
|
|
26
|
+
| **On-demand actions** (`/thatch/defrag`, `extract`, `hygiene`, `peer-review`, `refine`, `reflect`, `self-review`, `whois`) | Command markdown synced at plugin init; nudge-backed bodies rendered from the shared prompt cores with `thatch_` tool spellings, self-contained bodies (refine, whois, and the review-mode couchings peer-review/self-review) inline in the registry; `extract` and `whois` are opencode-only (they need session identity) | Command markdown synced by `thatch setup --claude` into `<claudeDir>/commands/thatch/`; same bodies with `mcp__thatch__` spellings; `extract` and `whois` excluded | MCP Prompts (`prompts/list` + `prompts/get`) from the same bodies; Cursor surfaces them as slash commands; `extract` and `whois` excluded | **Full** for the shared actions (same bodies, per-host tool spellings); `extract` and `whois` opencode-only |
|
|
27
27
|
| **Extraction** (buffer tool calls, extract durable facts) | `tool.execute.after` buffers non-meta tool calls in-process (`isMetaToolName` in `src/extraction.ts`: thatch_*, skill, task, agent, subagent — execute calls unwrapping only thatch tools are also excluded); **direct extraction** is the primary path — when the parent session goes idle with pending buffer entries, the plugin creates a child session via the SDK (`client.session.create` with `parentID`) and prompts it to call `get_extraction_payload` (nudge suppressed via the `extracting` set); the `chat.message` nudge path is a **fallback** used when direct extraction throws | `PostToolBatch` → `thatch buffer-batch` (file-backed JSONL queue); `UserPromptSubmit` → `thatch flush-tools` peeks the queue and prints the nudge with session ID + fetch tool name (does not drain — the queue persists until a memory write or `extraction_done`) | `postToolUse` → `thatch buffer-tool` (single-tool, file-backed queue); `beforeSubmitPrompt` → `thatch flush-tools --json` | **Partial** — opencode uses direct extraction (parent idle → child session) as the primary path, with the nudge as fallback; MCP hosts are nudge-only. Both paths use `get_extraction_payload` to fetch the queued interactions as a tool response, keeping the full payload out of the main session's context window. Nudge timing differs: the nudge arrives at the **start** of the next turn in Claude Code/Cursor; opencode's direct extraction fires at **end of turn** (session idle), and the nudge fallback fires at the start of the next turn like MCP |
|
|
28
|
-
| **Extraction feedback** (toast notifications) | `client.tui.showToast` fires when the extraction child goes idle
|
|
28
|
+
| **Extraction feedback** (toast notifications) | `client.tui.showToast` on v1; the session-tab bridge's toast event -> the TUI plugin's `ui.toast.show` on v2 — fires when the extraction child goes idle, but ONLY when memories were actually written (no toast for no-save runs, to avoid notification fatigue): shows `[thatch] new: N, updated: M, deleted: K` (success); best-effort, silently ignored in headless mode | No TUI connection — MCP hosts have no equivalent | No TUI connection | **Gapped** — MCP hosts cannot show toast notifications |
|
|
29
29
|
| **Automatic alerts** (notify on LLM pause / turn end) | Plugin event hook drives the `alerts.ts` state machine: `question.asked`/`permission.asked` → pause alert; `session.status` idle with a real-work round → done alert; unrecovered errors → needs-attention. Delivered via the shared `notify.ts` dispatcher, channel per event from the `alerts` config section | No event stream — only `thatch_notify_user` when the agent chooses | No event stream — only `thatch_notify_user` when the agent chooses | **Gapped** — MCP hosts have no plugin event hook to watch; Claude Code's `Notification`/`Stop` hooks could feed the same classifier later |
|
|
30
30
|
| **Prompt-aware recall nudge** | `chat.message` hook embeds the prompt with the in-process warm model, searches `db.search()`, pushes a nudge part if matches ≥ threshold | `UserPromptSubmit` hook → `thatch flush-tools` connects to the MCP server's sideband socket; the warm server embeds + searches; hook prints the nudge or falls back to the write nudge | `beforeSubmitPrompt` hook → `thatch flush-tools --json` (same sideband path) | **Full** — sideband socket gives cold hook processes access to the warm MCP server's model |
|
|
31
31
|
| **Prediction auto-fire** (user decision model) | `chat.message` hook scores the prompt embedding against prediction matchers; injects a `[thatch] User decision model` nudge alongside the recall nudge (separate synthetic part, same embedding call) | `UserPromptSubmit` hook → `thatch flush-tools` fires prediction query via the sideband socket's `predictions` method in parallel with the recall nudge | `beforeSubmitPrompt` hook → `thatch flush-tools --json` (same sideband path) | **Full** — same `scorePredictionNudge` entry point in both paths |
|
package/docs/dev/skills.md
CHANGED
|
@@ -47,18 +47,18 @@ description: Extract durable project facts ... Use when ...
|
|
|
47
47
|
| `thatch-review-mark-and-sweep` | Mechanical change completeness: whole-repo sweep for stragglers after renames, flag removals, API substitutions. |
|
|
48
48
|
| `thatch-review-highlights` | Positive finding detection: notably clever solutions, cleanup done along the way, documentation that helps. Medium-high bar against generic praise. |
|
|
49
49
|
| `thatch-review-technique` | Technique lens: teaching-grade notes on unseen helpers/internal packages, companion techniques, house patterns, test-craft, API-shape principles, next-reader discoverability. Informational only, verified citations, never blocking. |
|
|
50
|
-
| `thatch-review-synthesizer` | Verify specialist findings against code, dedupe, classify, calibrate severity. Starts the final report with a workflow-change preface, then cross-references findings against prior review comments when a follow-up round register is provided. |
|
|
51
|
-
| `thatch-review-context` | Gather project/feature context (PR descriptions, git archaeology, ticket references, linked docs/tickets followed from the change, memory) before fan-out. Prevents false positives about intentionally deferred work, and checks whether main moved past the merge-base touching the same paths (staleness signal for the synthesizer). Also fetches prior review comments on a connected PR/MR for follow-up-round detection and builds a register with preliminary addressed-check status per comment. |
|
|
50
|
+
| `thatch-review-synthesizer` | Verify specialist findings against code, dedupe, classify, calibrate severity. Starts the final report with a workflow-change preface, then cross-references findings against prior review comments when a follow-up round register is provided, and against sibling PR registers when related PRs were detected (cross-PR notes appendix). |
|
|
51
|
+
| `thatch-review-context` | Gather project/feature context (PR descriptions, git archaeology, ticket references, linked docs/tickets followed from the change, memory) before fan-out. Prevents false positives about intentionally deferred work, and checks whether main moved past the merge-base touching the same paths (staleness signal for the synthesizer). Also fetches prior review comments on a connected PR/MR for follow-up-round detection and builds a register with preliminary addressed-check status per comment. Also discovers related PRs (stacks chained through base branches, release trains ordered by tracker relations) and builds a sibling register with targeted consistency verdicts. |
|
|
52
52
|
| `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, skeletons) before proposing changes. The research skill; pair with `thatch-coding-workflow`. |
|
|
53
53
|
| `thatch-review-followup` | Alternate entrypoint for follow-up review rounds. Verifies whether the author's responses and code changes since your last review round adequately addressed your prior findings, offers to reply on resolved items, then optionally re-runs the full structured review. |
|
|
54
54
|
| `thatch-change-walkthrough` | Explain a change to the user as a teaching walkthrough: resolve the delta, research each affected workflow at the merge-base, teach current behavior, then overlay the modifications with file:line citations and analogies. |
|
|
55
55
|
| `thatch-code-walkthrough` | Explain a feature, module, or workflow to the user as a teaching walkthrough: identify the code area (optionally from a branch or PR), research how it works, teach it with file:line citations and analogies, list the key files. |
|
|
56
56
|
| `thatch-session-reflection` | End-of-session memory recording (project, user, tools, self). |
|
|
57
57
|
| `thatch-coding-workflow` | Plan and execute code changes with a task-list-driven workflow. Use when implementing features, fixing bugs, or making multi-file changes. |
|
|
58
|
-
| `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). The generic core loop. |
|
|
59
|
-
| `thatch-refine` | Classify the project (audience, lifecycle, surface) and refine a plan under that type's requirements. Entry point for the `thatch-refine-*` family; also the `/thatch/refine` command. |
|
|
58
|
+
| `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). The generic core loop. Plans with UI or session state carry a state-and-transition matrix; template ports carry a deltas-from-template ledger; a revision self-check runs between revise and re-review; user-extended runs rotate reviewer lenses and stop on a round with no build-changing findings. Deep-mode triggers are binding. |
|
|
59
|
+
| `thatch-refine` | Classify the project (audience, lifecycle, surface) and refine a plan under that type's requirements. Entry point for the `thatch-refine-*` family; also the `/thatch/refine` command. Deep-mode triggers in the core skill are binding; type deltas relax round counts, never mode selection. |
|
|
60
60
|
| `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. |
|
|
61
|
-
| `thatch-refine-personal` | Refinement deltas for solo projects with no downstream consumers: relaxed coordination, cleverness and experimentation welcome, premise verification stays honest. |
|
|
61
|
+
| `thatch-refine-personal` | Refinement deltas for solo projects with no downstream consumers: relaxed coordination, cleverness and experimentation welcome, premise verification stays honest. Relaxes round counts only, never mode selection. |
|
|
62
62
|
| `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. |
|
|
63
63
|
| `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. |
|
|
64
64
|
| `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. |
|
|
@@ -75,11 +75,11 @@ description: Extract durable project facts ... Use when ...
|
|
|
75
75
|
|
|
76
76
|
| Skill | Role |
|
|
77
77
|
|-------|------|
|
|
78
|
-
| `thatch-code-review` | Resolve review target (incl. VCS detection and connected PR/MR lookup for follow-up round detection), gather project context, research affected workflows, estimate complexity, partition, dispatch the review specialists in parallel, synthesize with a workflow-change preface
|
|
78
|
+
| `thatch-code-review` | Resolve review target (incl. VCS detection and connected PR/MR lookup for follow-up round detection), gather project context, research affected workflows, estimate complexity, partition, dispatch the review specialists in parallel, synthesize with a workflow-change preface, prior-comment cross-reference, and sibling-register cross-reference. Also the machinery behind the `/thatch/self-review` and `/thatch/peer-review` mode commands, which run it with a weighting overlay (inline, without sub-agents, on hosts that lack the coordinator). |
|
|
79
79
|
|
|
80
80
|
## REVIEW_COMMON
|
|
81
81
|
|
|
82
|
-
The seven problem-finding review specialists share a framework interpolated via `${REVIEW_COMMON}` (the
|
|
82
|
+
The seven problem-finding review specialists share a framework interpolated via `${REVIEW_COMMON}` (the technique and highlights specialists do not use it: technique reports informational notes and highlights finds positive things, each with its own structure):
|
|
83
83
|
|
|
84
84
|
- **Static analysis only** — no running tests/linters/compilers.
|
|
85
85
|
- **Scope gathering** — resolve the git range, `git diff --stat`, read changed
|
|
@@ -93,6 +93,9 @@ The seven problem-finding review specialists share a framework interpolated via
|
|
|
93
93
|
- **Project context awareness** — if a context brief is provided (from the
|
|
94
94
|
coordinator or from loading `thatch-review-context`), use it to avoid false
|
|
95
95
|
positives about intentionally deferred work.
|
|
96
|
+
- **Sibling PR context** — when the brief's sibling register is present
|
|
97
|
+
(related PRs detected), check it before flagging anything as referenced but
|
|
98
|
+
missing; a stack member or release-train sibling may deliver the piece.
|
|
96
99
|
- **Workflow context awareness** — if a workflow guide is provided (from the
|
|
97
100
|
coordinator or from loading `thatch-code-archaeology`), use it to understand
|
|
98
101
|
the purpose and evolution of the code, distinguishing intentional behavior
|
|
@@ -143,7 +146,7 @@ and `--cursor` pass only the shared set.
|
|
|
143
146
|
|
|
144
147
|
## Adding a skill
|
|
145
148
|
|
|
146
|
-
1. Write `artifacts/skills/thatch-<name>.md` with YAML frontmatter. If it's a problem-finding review specialist, include `${REVIEW_COMMON}` (interpolated from `artifacts/skills/common.md`). The highlights
|
|
149
|
+
1. Write `artifacts/skills/thatch-<name>.md` with YAML frontmatter. If it's a problem-finding review specialist, include `${REVIEW_COMMON}` (interpolated from `artifacts/skills/common.md`). The technique and highlights specialists do not use REVIEW_COMMON — technique reports informational notes and highlights finds positive things, so the problem-finding framework does not apply to either.
|
|
147
150
|
2. Add the skill name to the `names` array in `loadSharedSkills()` (or `loadOpencodeOnlySkills()` if sub-agents are needed). The loader in `src/skills.ts` reads `.md` files at init.
|
|
148
151
|
3. If the skill name appears in a tool's workflow (e.g. `thatch-fact-extractor`), update `src/prompts.ts`.
|
|
149
152
|
4. Run `mise run check` — tests verify counts; `installSkills` picks up new files on next init.
|
package/docs/user/README.md
CHANGED
|
@@ -206,7 +206,7 @@ requirements.
|
|
|
206
206
|
| `thatch_chat_send` | Send a message to another registered session, by name or session id. The recipient is nudged when its session is idle. |
|
|
207
207
|
| `thatch_chat_read` | Drain your inbox: all unread messages oldest-first, marked read. Senders identified by display name. |
|
|
208
208
|
| `thatch_chat_unregister` | Leave the chat directory. |
|
|
209
|
-
| `thatch_chat_broadcast` | Send one message to every other live registered session at once.
|
|
209
|
+
| `thatch_chat_broadcast` | Send one message to every other live registered session at once. Opencode sessions without an open tab (stale, or fresh but tab-less) are skipped and reported by name. Use sparingly: every open session pays a model turn for a broadcast. |
|
|
210
210
|
|
|
211
211
|
See [cross-session-chat.md](cross-session-chat.md) for the full behavior,
|
|
212
212
|
liveness model, and limitations.
|
|
@@ -248,16 +248,23 @@ Beyond the tools, thatch hooks into opencode itself:
|
|
|
248
248
|
flushes pending fact extraction, finishes any promised memory writes, and
|
|
249
249
|
surfaces todos or follow-ups it never addressed. The agent ends its
|
|
250
250
|
response with a greenlight token only when the checklist is clean. Thatch
|
|
251
|
-
watches for the token and then runs the compaction or
|
|
251
|
+
watches for the token and then runs the compaction or closes the session
|
|
252
|
+
(opencode 1 exits the whole app; opencode 2 closes the session's own tab.
|
|
253
|
+
Either way the session's watches are cancelled, and reopening or resuming
|
|
254
|
+
the session does not re-arm them - re-create watches you still want with
|
|
255
|
+
`thatch_watch_create`).
|
|
252
256
|
Without the token nothing fires -- a toast points you at the items the
|
|
253
|
-
agent listed, and you re-run the command once they're
|
|
257
|
+
agent listed in its response, and you re-run the command once they're
|
|
258
|
+
addressed. Text typed
|
|
254
259
|
after the command reaches the agent first, labeled as a `User Message`
|
|
255
260
|
section ahead of the checklist (say goodbye or hand off context:
|
|
256
261
|
`/thatch/exit Good work - see you tomorrow`). Alongside the wrap-ups,
|
|
257
|
-
on-demand actions (`/thatch/defrag`,
|
|
258
|
-
`/thatch/
|
|
259
|
-
|
|
260
|
-
|
|
262
|
+
on-demand actions cover the memory-maintenance behaviors (`/thatch/defrag`,
|
|
263
|
+
`/thatch/extract`, `/thatch/hygiene`, `/thatch/reflect`) whenever you
|
|
264
|
+
want, not just when a nudge fires, plus the standalone commands: plan
|
|
265
|
+
refinement (`/thatch/refine`), the review modes (`/thatch/self-review`,
|
|
266
|
+
`/thatch/peer-review`), and the chat identity report (`/thatch/whois`)
|
|
267
|
+
-- see [commands.md](commands.md).
|
|
261
268
|
A greenlit `/thatch/exit` also unregisters the session from the chat
|
|
262
269
|
directory, so other sessions stop sending it mail. The commands
|
|
263
270
|
self-install into `~/.config/opencode/command/thatch/` at plugin startup;
|
|
@@ -329,17 +336,17 @@ response, walkthrough, and workflow skills that support the review pipeline:
|
|
|
329
336
|
| `thatch-review-highlights` | Positive finding detection: notably clever solutions, cleanup done along the way, documentation that helps. Medium-high bar against generic praise. |
|
|
330
337
|
| `thatch-review-technique` | Technique lens: teaching-grade notes on unseen helpers/internal packages, companion techniques, house patterns, test-craft, API-shape principles, next-reader discoverability. Informational only, never blocking. |
|
|
331
338
|
| `thatch-review-synthesizer` | Verifies and synthesizes findings from multiple specialists into a report that starts with workflow changes, then highlights, then deduplicated severity-grouped findings. |
|
|
332
|
-
| `thatch-review-context` | Gathers project context (PR descriptions, git archaeology, ticket references, linked docs/tickets, memory) before a review. Prevents false positives about intentionally deferred work. |
|
|
339
|
+
| `thatch-review-context` | Gathers project context (PR descriptions, git archaeology, ticket references, linked docs/tickets, memory, related PRs) before a review. Prevents false positives about intentionally deferred work and about pieces delivered by sibling PRs in a stack or release train. |
|
|
333
340
|
| `thatch-code-archaeology` | Investigates an existing feature, debugs an unfamiliar area, or begins a new ticket. Explores the code base from multiple angles (data model, state flow, git history, sibling features, skeletons) before proposing changes. Pairs with `thatch-coding-workflow`. |
|
|
334
341
|
| `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 your prior findings, offers to reply on resolved items, then optionally re-runs the full structured review. |
|
|
335
342
|
| `thatch-review-response` | Author-side review response: triage findings on your own PR, fix bugs one by one, reply on each thread, post a top-level summary comment. |
|
|
336
343
|
| `thatch-change-walkthrough` | Explains a change to the user as a teaching walkthrough: researches each affected workflow at the merge-base, teaches current behavior, then overlays the modifications with file:line citations. |
|
|
337
344
|
| `thatch-code-walkthrough` | Explains a feature, module, or workflow to the user as a teaching walkthrough with file:line citations. Also used to draft high-level docs for new or undocumented features. |
|
|
338
345
|
| `thatch-coding-workflow` | Plans and executes code changes with a task-list-driven workflow: complexity triage, milestone planning, research before coding, post-coding verification. Pairs with `thatch-code-archaeology` (research first, then plan). |
|
|
339
|
-
| `thatch-plan-refinement` | Refines a plan before it is implemented: a fresh-context reviewer subagent re-checks the plan each round until consensus; deep mode adds specialist lenses (reuse, alternatives, hidden problems, safe-to-modify, intent archaeology). |
|
|
340
|
-
| `thatch-refine` | Classifies the project (audience, lifecycle, surface) and refines a plan under that type's requirements. The entry point for the `thatch-refine-*` family; also the `/thatch/refine` command. |
|
|
346
|
+
| `thatch-plan-refinement` | Refines a plan before it is implemented: a fresh-context reviewer subagent re-checks the plan each round until consensus; deep mode adds specialist lenses (reuse, alternatives, hidden problems, safe-to-modify, intent archaeology). Plans with UI or session state must include a state-and-transition matrix; a revision self-check runs after each revision; extended runs rotate reviewer lenses and stop on a round with no build-changing findings. |
|
|
347
|
+
| `thatch-refine` | Classifies the project (audience, lifecycle, surface) and refines a plan under that type's requirements. The entry point for the `thatch-refine-*` family; also the `/thatch/refine` command. Deep-mode triggers in the core skill are binding; type deltas relax round counts, never mode selection. |
|
|
341
348
|
| `thatch-refine-team-app` | Team codebases: consistency outranks cleverness, intent breadcrumbs are deliverables, pre-mortem and safe-to-modify lenses dominate. |
|
|
342
|
-
| `thatch-refine-personal` | Solo projects: relaxed coordination, cleverness and experimentation welcome, premise verification stays honest. |
|
|
349
|
+
| `thatch-refine-personal` | Solo projects: relaxed coordination, cleverness and experimentation welcome, premise verification stays honest. Relaxes round counts, never mode selection. |
|
|
343
350
|
| `thatch-refine-shared-lib` | Library API surfaces: flexible on input, strict on output, keep special cases out of the API, never foreclose caller options, docs are a deliverable. |
|
|
344
351
|
| `thatch-refine-spike` | Throwaway prototypes: skip the loop, verify only the premises the experiment's conclusion depends on. |
|
|
345
352
|
| `thatch-refine-infra` | CI, Terraform, and k8s changes: blast radius, mandatory rollback plan and staged verification, pattern-following is near-absolute. |
|
|
@@ -358,7 +365,7 @@ response, walkthrough, and workflow skills that support the review pipeline:
|
|
|
358
365
|
|
|
359
366
|
| Skill | Purpose |
|
|
360
367
|
|-------|---------|
|
|
361
|
-
| `thatch-code-review` | Multi-agent review coordinator. Dispatches parallel sub-agents for triage, decomposition, specialist fan-out, and synthesis with a workflow-change preface. Not available in Claude Code (requires sub-agent support). |
|
|
368
|
+
| `thatch-code-review` | Multi-agent review coordinator. Dispatches parallel sub-agents for triage, decomposition, specialist fan-out, and synthesis with a workflow-change preface. Not available in Claude Code (requires sub-agent support), though the `/thatch/self-review` and `/thatch/peer-review` commands run the same specialists inline there. |
|
|
362
369
|
|
|
363
370
|
### Host availability
|
|
364
371
|
|
|
@@ -24,13 +24,19 @@ the agent's context. The block looks like:
|
|
|
24
24
|
- [0.58 conf, 1 tests] When about to commit changes to the repo:
|
|
25
25
|
do Run mise run check (tsc + bun test + markdownlint) before
|
|
26
26
|
committing. Never commit code that fails the quality gate.
|
|
27
|
+
Snapshot: these labels reflect the behavior store as of this message's
|
|
28
|
+
submission. Behaviors updated after submission (including during this
|
|
29
|
+
turn) do not refresh it; re-check with thatch_behavior_list before
|
|
30
|
+
relying on them.
|
|
27
31
|
```
|
|
28
32
|
|
|
29
33
|
The agent evaluates each surfaced rule against the current situation.
|
|
30
34
|
If relevant (ham), it follows the rule and calls `behavior_feedback`
|
|
31
35
|
with `relevant: true`. If not relevant (spam), it calls
|
|
32
36
|
`behavior_feedback` with `relevant: false`. This trains the classifier
|
|
33
|
-
so future nudges are more accurate.
|
|
37
|
+
so future nudges are more accurate. On Claude Code and Cursor the
|
|
38
|
+
footer's tool name appears with its MCP spelling
|
|
39
|
+
(`mcp__thatch__behavior_list`).
|
|
34
40
|
|
|
35
41
|
## Default behaviors
|
|
36
42
|
|
package/docs/user/cli.md
CHANGED
|
@@ -57,7 +57,8 @@ thatch chat tail --since 2026-09-12 --until "2026-09-13 09:00" # a window
|
|
|
57
57
|
|
|
58
58
|
`chat list` shows every registered session in two sections, Active and
|
|
59
59
|
Stale, each as aligned columns under a header row: how long since its
|
|
60
|
-
last heartbeat, its status (`
|
|
60
|
+
last heartbeat, its status (`open` - a tab is open now - or `recent` -
|
|
61
|
+
host alive, no open tab - for opencode sessions;
|
|
61
62
|
`active` or `idle` for Claude Code and Cursor sessions, which only beat
|
|
62
63
|
when a prompt runs), its project, and its topic (the session's title) -
|
|
63
64
|
the fastest way to answer "which session should I talk to about X?"
|
package/docs/user/code-review.md
CHANGED
|
@@ -16,7 +16,10 @@ the review:
|
|
|
16
16
|
2. **Gather project context**: PR descriptions, git archaeology,
|
|
17
17
|
ticket references, links to docs/designs/tickets found in the
|
|
18
18
|
change (followed one hop for scope and design context), TODO
|
|
19
|
-
markers, prior reviews, project memory. Also
|
|
19
|
+
markers, prior reviews, project memory. Also discovers related
|
|
20
|
+
PRs (stacks chained through base branches, release trains
|
|
21
|
+
ordered by ticket relations) and checks consistency between
|
|
22
|
+
them and the change. Also checks whether main
|
|
20
23
|
has moved past the merge-base touching the same paths — a stale
|
|
21
24
|
branch can make findings true only at the merge-base.
|
|
22
25
|
3. **Research affected workflows**: load `thatch-code-archaeology`,
|
|
@@ -30,6 +33,10 @@ the review:
|
|
|
30
33
|
the final report. When the branch is stale relative to main, it
|
|
31
34
|
re-verifies behavioral findings against current main and reports
|
|
32
35
|
merge-base-only findings as rebase notes rather than defects.
|
|
36
|
+
When related PRs were detected, it cross-references findings
|
|
37
|
+
against the sibling register: a missing piece delivered by a
|
|
38
|
+
sibling is not a defect, and inconsistencies between connected
|
|
39
|
+
PRs surface as cross-PR notes.
|
|
33
40
|
|
|
34
41
|
## The nine specialists
|
|
35
42
|
|
|
@@ -48,6 +55,34 @@ the review:
|
|
|
48
55
|
Each specialist is a self-contained static-analysis pass. They can run
|
|
49
56
|
individually or as part of the coordinated review.
|
|
50
57
|
|
|
58
|
+
## Review modes: /thatch/self-review and /thatch/peer-review
|
|
59
|
+
|
|
60
|
+
Two slash commands run the same pipeline with the findings weighted for
|
|
61
|
+
whose work is under review.
|
|
62
|
+
|
|
63
|
+
- `/thatch/self-review` reviews your own completed work before you
|
|
64
|
+
submit it. The slop, technique, and breadcrumbs lenses weigh
|
|
65
|
+
heaviest, simplicity is overweighted on shared projects, and
|
|
66
|
+
technique or highlights findings that would simplify the design are
|
|
67
|
+
surfaced for an adopt-or-dismiss decision rather than left as
|
|
68
|
+
footnotes. The review iterates: after each round you fix or decline
|
|
69
|
+
each finding, and the next round re-reviews the updated state until
|
|
70
|
+
a round produces no meaningful findings (at most three rounds).
|
|
71
|
+
- `/thatch/peer-review` reviews another author's change. Findings are
|
|
72
|
+
weighted for org velocity: simpler ways to the same outcome, existing
|
|
73
|
+
helpers that already do the work, extremely clear documentation, and
|
|
74
|
+
unintended consequences for callers and siblings. The review walks
|
|
75
|
+
the project's persistent memory for incidents, constraints, and
|
|
76
|
+
footguns the author could not have known, and checks them against
|
|
77
|
+
the change. Settled designs are not re-litigated, and "if someone
|
|
78
|
+
changes this later it breaks" findings are never reported; instead
|
|
79
|
+
the review verifies the change leaves a breadcrumb trail to wherever
|
|
80
|
+
the project records how the feature works.
|
|
81
|
+
|
|
82
|
+
Both commands work on every host. On opencode they drive the
|
|
83
|
+
coordinator with sub-agents. On Claude Code and Cursor the same
|
|
84
|
+
procedure runs the specialists inline, in sequence.
|
|
85
|
+
|
|
51
86
|
## Follow-up rounds
|
|
52
87
|
|
|
53
88
|
After the author responds or pushes changes, `thatch-review-followup`
|
|
@@ -55,6 +90,10 @@ verifies whether prior findings were adequately addressed before
|
|
|
55
90
|
deciding whether to re-run the full review. The author must either
|
|
56
91
|
fix the code, prove the finding is not a concern with a checkable
|
|
57
92
|
argument, or file a follow-up ticket with a risk explanation.
|
|
93
|
+
Between rounds the author may merge the default branch — or, on a
|
|
94
|
+
stacked PR, merge or rebase onto the base branch — into the branch;
|
|
95
|
+
the followup excludes those commits so only the author's own changes
|
|
96
|
+
are verified.
|
|
58
97
|
|
|
59
98
|
## Author-side response
|
|
60
99
|
|
|
@@ -67,7 +106,9 @@ one, reply on each thread, and post a top-level summary comment.
|
|
|
67
106
|
|
|
68
107
|
- The coordinator (`thatch-code-review`) is opencode-only. It
|
|
69
108
|
requires sub-agent support to dispatch parallel specialists. On
|
|
70
|
-
Claude Code or Cursor, run specialists individually instead
|
|
109
|
+
Claude Code or Cursor, run specialists individually instead; the
|
|
110
|
+
`/thatch/self-review` and `/thatch/peer-review` commands package
|
|
111
|
+
exactly that inline sequence, with a mode overlay on top.
|
|
71
112
|
- The review quality depends on the model. Weak models may
|
|
72
113
|
hallucinate findings or miss subtle issues.
|
|
73
114
|
- The synthesizer verifies cited text exists, but it does not
|