@jeffober/thatch 0.2.4 → 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.
Files changed (76) hide show
  1. package/README.md +56 -26
  2. package/artifacts/skills/common.md +1 -0
  3. package/artifacts/skills/thatch-clear-writing.md +103 -5
  4. package/artifacts/skills/thatch-code-archaeology.md +9 -0
  5. package/artifacts/skills/thatch-code-review.md +16 -8
  6. package/artifacts/skills/thatch-coordination.md +69 -0
  7. package/artifacts/skills/thatch-plan-refinement.md +33 -4
  8. package/artifacts/skills/thatch-pr-description.md +21 -13
  9. package/artifacts/skills/thatch-refine-personal.md +5 -0
  10. package/artifacts/skills/thatch-refine.md +7 -1
  11. package/artifacts/skills/thatch-review-context.md +55 -2
  12. package/artifacts/skills/thatch-review-followup.md +4 -4
  13. package/artifacts/skills/thatch-review-no-slop.md +15 -0
  14. package/artifacts/skills/thatch-review-response.md +1 -1
  15. package/artifacts/skills/thatch-review-synthesizer.md +21 -1
  16. package/bin/thatch +48 -15
  17. package/docs/assets/deepwiki-badge.svg +1 -0
  18. package/docs/dev/README.md +18 -10
  19. package/docs/dev/features/README.md +1 -1
  20. package/docs/dev/features/behavior-engine.md +10 -0
  21. package/docs/dev/features/cli.md +4 -1
  22. package/docs/dev/features/commands.md +40 -9
  23. package/docs/dev/features/cross-session-chat.md +194 -23
  24. package/docs/dev/features/database.md +4 -4
  25. package/docs/dev/features/notifications.md +1 -2
  26. package/docs/dev/features/nudge-pipeline.md +14 -0
  27. package/docs/dev/features/opencode-plugin.md +56 -15
  28. package/docs/dev/features/prediction-engine.md +71 -6
  29. package/docs/dev/features/qa-system.md +19 -3
  30. package/docs/dev/features/session-lifecycle.md +6 -6
  31. package/docs/dev/features/session-tabs.md +161 -0
  32. package/docs/dev/features/setup.md +5 -0
  33. package/docs/dev/features/watchers.md +62 -6
  34. package/docs/dev/gotchas.md +89 -0
  35. package/docs/dev/mcp-parity.md +5 -5
  36. package/docs/dev/setup-and-hooks.md +7 -2
  37. package/docs/dev/skills.md +12 -9
  38. package/docs/plans/generalized-session-heartbeat.md +212 -0
  39. package/docs/plans/prediction-consolidation.md +88 -68
  40. package/docs/user/README.md +29 -13
  41. package/docs/user/behavior-engine.md +7 -1
  42. package/docs/user/cli.md +5 -1
  43. package/docs/user/code-review.md +43 -2
  44. package/docs/user/commands.md +37 -10
  45. package/docs/user/cross-session-chat.md +76 -26
  46. package/docs/user/prediction-engine.md +23 -1
  47. package/docs/user/setup.md +16 -0
  48. package/docs/user/skills.md +9 -7
  49. package/docs/user/watchers.md +22 -7
  50. package/package.json +10 -2
  51. package/src/alerts.ts +1 -1
  52. package/src/capabilities.ts +20 -6
  53. package/src/chat.ts +358 -43
  54. package/src/commands.ts +123 -18
  55. package/src/db.ts +74 -3
  56. package/src/git.ts +3 -1
  57. package/src/hygiene.ts +5 -0
  58. package/src/mcp.ts +101 -9
  59. package/src/opencode/tui-plugin.ts +90 -0
  60. package/src/opencode/v2.ts +440 -66
  61. package/src/prediction.ts +90 -0
  62. package/src/prompts.ts +164 -29
  63. package/src/runtime.ts +298 -15
  64. package/src/scoring-engine.ts +40 -9
  65. package/src/session-db.ts +4 -4
  66. package/src/session-tab-shared.ts +231 -0
  67. package/src/setup.ts +9 -3
  68. package/src/skills.ts +6 -9
  69. package/src/tab-state.ts +125 -0
  70. package/src/tool-defs.ts +454 -76
  71. package/src/tools.ts +2 -0
  72. package/src/watchers.ts +263 -26
  73. package/docs/plans/intuition-drives.md +0 -147
  74. package/docs/plans/llm-alerts.md +0 -287
  75. package/docs/plans/thatch-actions.md +0 -179
  76. package/docs/plans/worktree-deletion-recovery.md +0 -246
@@ -11,7 +11,9 @@ between tabs all afternoon.
11
11
  opencode sessions join the directory automatically, the moment their
12
12
  first user message arrives (before the assistant responds), and a session
13
13
  resumed with `opencode -c` or `opencode -s <id>` is back in the directory
14
- at harness start with its old name. Names are assigned by thatch, never chosen: a name drawn from a
14
+ at harness start with its old name. Sessions thatch itself creates in the
15
+ background - like the fact-extraction runs - are machinery, not chat
16
+ participants, and never appear in the roster. Names are assigned by thatch, never chosen: a name drawn from a
15
17
  built-in pool plus a counter (`al-go-rithm-00001`). The counter only ever
16
18
  increments, so a name is
17
19
  minted exactly once per machine - pruning an old session never reissues its
@@ -23,29 +25,60 @@ explicitly with `thatch_chat_register` (no arguments) - useful after
23
25
  `chat_unregister`, or just to check your assigned name. Once registered:
24
26
 
25
27
  - `thatch_chat_list` shows every registered session on the machine, grouped
26
- into Active and Stale sections: active sessions can be woken; stale ones
27
- have missed two heartbeats, about a minute (the opencode process hosting
28
- them has probably stopped). Each row carries how long ago the session last
28
+ into Active and Stale sections: active sessions' harness is alive; stale
29
+ ones have missed two heartbeats, about a minute (the opencode process
30
+ hosting them has probably stopped). Each active row also carries a
31
+ liveness verdict: `open` means the session has an opencode tab open
32
+ right now, `recent` means the harness is alive but no tab is open. Each
33
+ row carries how long ago the session last
29
34
  checked in, its project, whether it runs in the project root or a linked
30
35
  git worktree, its topic, and your unread count
31
36
  - `thatch_chat_send` delivers a message to another registered session, by
32
- name or session id, and states the recipient's liveness at send time
33
- - `thatch_chat_read` drains the session's inbox
37
+ name or session id, and states the recipient's liveness at send time.
38
+ For a recipient with no open tab it says so plainly: the message waits
39
+ in the inbox, but the wake is not guaranteed to start a turn
40
+ (daemon-resident sessions like subordinates usually still pick the
41
+ message up on their next activity).
42
+ An identical resend within a few seconds is treated as a client retry:
43
+ while the first copy is still unread it succeeds but delivers nothing
44
+ new (the tool output says so); once the recipient has read it, the same
45
+ body delivers again - a deliberate repeat, not a retry. A body over
46
+ 2,000 characters is not refused: it is auto-split into several messages,
47
+ and the tool output reports the split ("SENT AS N PARTS"). The whole
48
+ message is capped at 20,000 characters; longer is refused with the
49
+ attempted count
50
+ - `thatch_chat_read` drains the session's inbox. A message longer than
51
+ one page (2,000 characters) is shown clipped, with a marker naming the
52
+ characters ("[clipped: showing 2000 of 2047 characters]"); page through
53
+ the rest with the `message` and `offset` arguments (each drain line
54
+ carries the message id). Paging never marks anything read
34
55
  - `thatch_chat_broadcast` delivers one message to every other live
35
- registered session at once
56
+ registered session that has a tab open, plus every MCP session (a long
57
+ body is split per recipient, exactly like a split `chat_send`)
36
58
  - `thatch_chat_unregister` leaves the directory
37
59
 
38
60
  When mail arrives for an idle session, thatch wakes it with a notification -
39
61
  the agent gets a model turn even with nobody at the keyboard, reads its
40
- inbox, and decides what to do. The notification names the senders and the
41
- count only; the agent reads the actual messages itself.
62
+ inbox, and decides what to do. If the session's directory has been deleted
63
+ (a merged branch's worktree, for example), the wake is skipped quietly:
64
+ no error appears, the mail stays queued, and it delivers normally once the
65
+ directory exists again. The notification names the senders and the
66
+ count only; the agent reads the actual messages itself. A split message
67
+ arrives as one grouped notification ("N unread messages from X"), and the
68
+ parts carry their own markers so the reader assembles the whole before
69
+ acting: the first part is prefixed "(message 1 of N)" and every part but
70
+ the last is suffixed "(continued in next message)".
42
71
 
43
72
  Broadcasts are for announcements and open questions ("which of you is
44
73
  working on the payments code?", "main just moved, rebase if you are based
45
- on it"). Every live session gets the message and spends a model turn on it,
74
+ on it"). Every OPEN session gets the message and spends a model turn on it,
46
75
  so broadcast sparingly - for anything with a known audience, `chat_send` is
47
- the polite tool. Stale sessions are skipped and reported: a dead session
48
- never reads a broadcast.
76
+ the polite tool. Sessions without an open tab are skipped and reported by
77
+ name: a stale session will never read the mail, and a fresh-but-tab-less
78
+ session (a closed tab, or a headless subordinate) is not reliably wakeable,
79
+ so messaging it would spend a turn on a delivery that likely never runs.
80
+ With opencode tabs disabled, broadcasts reach only MCP sessions - that is
81
+ the honest reading of "no tab signal", never a false fan-out.
49
82
 
50
83
  ## Watching the conversation
51
84
 
@@ -130,21 +163,34 @@ host the sessions run in.
130
163
 
131
164
  ## Liveness
132
165
 
133
- A session shows as fresh while the opencode process hosting it is
134
- running: hosted sessions keep beating (idle ones included), so the
135
- freshness is real. When that process dies, its sessions stop beating and
136
- show as stale about a minute later (two missed heartbeats), grouped into
137
- a separate Stale section. A laptop that slept reads the same way until
138
- its processes catch up.
166
+ Liveness has three levels, and the roster shows which one a row is in.
167
+
168
+ The base signal is the heartbeat: a session shows as fresh while the
169
+ opencode process hosting it is running - hosted sessions keep beating
170
+ (idle ones included), so the freshness is real. When that process dies,
171
+ its sessions stop beating and show as stale about a minute later (two
172
+ missed heartbeats), grouped into a separate Stale section. A laptop that
173
+ slept reads the same way until its processes catch up.
174
+
175
+ The second signal, read at roster time, is opencode's own tab state: a
176
+ fresh row whose session has a tab open right now shows as `open`, one
177
+ without shows as `recent`. `recent` covers finished-but-recently-active
178
+ conversations, closed tabs, and daemon-resident sessions that never had a
179
+ window (subordinates) - the harness is alive, but presence is unknown.
180
+ With tabs disabled (or no opencode state on the machine) every fresh row
181
+ shows as `recent`: an unknown tab state never reads as open.
182
+
139
183
  Messages to a stale session wait in its inbox unread - a dead session
140
- never reads them, and `chat_send` says so at send time. Continuing a
184
+ never reads them, and `chat_send` says so at send time. Messages to a
185
+ `recent` session are delivered, but `chat_send` warns that the wake is
186
+ not guaranteed without an open tab. Continuing a
141
187
  session with `opencode -s <id>` reclaims its name and row, and any mail
142
188
  that queued while it was down is delivered at startup. Explicitly
143
189
  closing a session (deleting it in the TUI) unregisters it immediately,
144
- and a greenlit `/thatch/exit` unregisters the session too: the wrap-up
145
- checklist calls `chat_unregister`, and the plugin removes the row itself
146
- when the exit token passes the greenlight check, so other sessions stop
147
- addressing mail to a process that is about to vanish.
190
+ and a greenlit `/thatch/exit` unregisters the session too: the plugin
191
+ removes the row itself when the exit token passes the greenlight check,
192
+ so other sessions stop
193
+ addressing mail to a session that is closing out.
148
194
 
149
195
  ## Turning chat off
150
196
 
@@ -198,10 +244,14 @@ construction. Three mitigations:
198
244
  the true session id from the host and record which process spawned them;
199
245
  the MCP server resolves its own parent process against that record, so
200
246
  the model cannot claim another session's identity there. On Cursor, chat
201
- tools still trust the `as` argument the caller supplies (hooks and MCP
247
+ tools trust the `as` argument the caller supplies (hooks and MCP
202
248
  servers share one workspace process, so process identity is ambiguous) -
203
- a local session that knows a name can act as it. opencode is immune (the
204
- host supplies identity). The threat model is one user's machine.
249
+ but the claim is directory-bound: a session may only claim an identity
250
+ registered to its own project, and a cross-project claim is refused.
251
+ The refusal teaches the fix - register a fresh identity for the project
252
+ once, then reuse it; do not re-register every turn. opencode is immune
253
+ (the host supplies identity). The threat model is one user's machine;
254
+ same-project spoofing on Cursor remains possible and accepted.
205
255
  - **No external content via wake.** The wake notification names senders
206
256
  and counts only - bodies flow exclusively through the framed
207
257
  `chat_read` surface.
@@ -46,10 +46,21 @@ like:
46
46
  flag tech debt before reviewing the diff
47
47
  - [0.61 conf, 2 tests] When choosing test scope: you may prefer
48
48
  integration tests over unit tests
49
+ Snapshot: these labels reflect the prediction store as of this message's
50
+ submission. Predictions updated after submission (including during this
51
+ turn) do not refresh it; re-check with thatch_prediction_query before
52
+ relying on them.
49
53
  ```
50
54
 
51
55
  The agent reads this and decides whether to follow the prediction,
52
- surface it to you, or ignore it. You never see the nudge directly.
56
+ surface it to you, or ignore it. You never see the nudge directly. The
57
+ snapshot line matters when the agent corrects the model mid-conversation
58
+ (you say "no, that's wrong" and it calls `thatch_prediction_delete`):
59
+ the block it saw at the start of the turn is a submission-time snapshot
60
+ and does not update. The agent should re-query with
61
+ `thatch_prediction_query` instead of trusting the block's labels. On
62
+ Claude Code and Cursor the tool name appears with its MCP spelling
63
+ (`mcp__thatch__prediction_query`).
53
64
 
54
65
  ## Tools
55
66
 
@@ -59,6 +70,7 @@ surface it to you, or ignore it. You never see the nudge directly.
59
70
  | `thatch_prediction_update` | Create, reinforce, or weaken a prediction. Takes a matcher (situation), prediction (preference), signal (confirm/disconfirm/soft/create), and rationale. |
60
71
  | `thatch_prediction_list` | List all predictions with matchers, confidence, evidence count, and provenance history. |
61
72
  | `thatch_prediction_delete` | Delete a prediction by semantic match. Edges and provenance are cascade-deleted. |
73
+ | `thatch_prediction_mark_checked` | Record a verdict (duplicate/distinct) on a pair the hygiene nudge flagged, so it stops resurfacing. |
62
74
 
63
75
  ## Configuration
64
76
 
@@ -83,5 +95,15 @@ surface it to you, or ignore it. You never see the nudge directly.
83
95
  store does not automatically apply to other projects. The global
84
96
  store is shared across projects.
85
97
 
98
+ ## Keeping the model tidy
99
+
100
+ If the same preference gets recorded twice in different words, the hygiene
101
+ nudge reports `prediction duplicate pairs pending review`. The agent reads
102
+ both, then either merges them (moves the loser's matchers onto the winner
103
+ with `thatch_prediction_update`, deletes the loser with
104
+ `thatch_prediction_delete`) or judges them distinct — and records the
105
+ verdict with `thatch_prediction_mark_checked` either way, so an adjudicated
106
+ pair never nags again.
107
+
86
108
  See [memory.md](memory.md) for the base memory system and
87
109
  [behavior-engine.md](behavior-engine.md) for the self-discipline rules.
@@ -44,6 +44,22 @@ The same file loads on both opencode lines: opencode 1.x reads its `server`
44
44
  export, opencode 2.x reads its default export (the merged plugin
45
45
  definition). No path change when you switch opencode versions.
46
46
 
47
+ For opencode 2.x session-tab support, the plugin must be a DIRECTORY shim
48
+ so the TUI can load the `./tui` entrypoint:
49
+
50
+ ```text
51
+ ~/.config/opencode/plugins/thatch/
52
+ index.ts export { server } from "<thatch-checkout>/src/index";
53
+ export { default } from "<thatch-checkout>/src/index";
54
+ tui.ts export { default } from "<thatch-checkout>/src/opencode/tui-plugin";
55
+ ```
56
+
57
+ Delete the old `thatch.ts` file shim in the same step - a file and a
58
+ directory shim coexisting stops plugin loading entirely (duplicate plugin
59
+ id, detected before any plugin code runs). The TUI plugin is what makes
60
+ spawned subordinate sessions appear as tabs; without it the tool still
61
+ works but no tab opens.
62
+
47
63
  Or place the thatch repo in `.opencode/plugins/` for auto-loading.
48
64
 
49
65
  ## Claude Code
@@ -48,11 +48,11 @@ Skills are split into shared (all hosts) and opencode-only:
48
48
  | `thatch-review-highlights` | Positive finding detection: notably clever solutions, cleanup done along the way, documentation that helps. Medium-high bar. |
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, never blocking. |
50
50
  | `thatch-review-synthesizer` | Verifies and synthesizes findings from multiple specialists into a deduplicated, severity-grouped report. |
51
- | `thatch-review-context` | Gathers project context (PRs, tickets, linked docs/designs followed from the change, TODOs, deferred work) before a review. Prevents false positives about intentionally deferred work. |
51
+ | `thatch-review-context` | Gathers project context (PRs, tickets, linked docs/designs followed from the change, TODOs, deferred work, 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. |
52
52
  | `thatch-code-archaeology` | Investigates an existing feature, debugs an unfamiliar area, or begins a new ticket. The research skill; pair with `thatch-coding-workflow`. |
53
53
  | `thatch-review-followup` | Verifies whether the author's responses and code changes since your last review adequately addressed your prior findings. |
54
54
  | `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. |
55
- | `thatch-code-review` | Multi-agent review coordinator (opencode-only). Dispatches parallel sub-agents for specialist fan-out and synthesis. |
55
+ | `thatch-code-review` | Multi-agent review coordinator (opencode-only). Dispatches parallel sub-agents for specialist fan-out and synthesis. Also the machinery behind the `/thatch/self-review` and `/thatch/peer-review` mode commands, which couch it with a weighting overlay and run the specialists inline on hosts where the coordinator is not installed. |
56
56
 
57
57
  ### Walkthrough skills
58
58
 
@@ -68,22 +68,22 @@ Skills are split into shared (all hosts) and opencode-only:
68
68
  | `thatch-pr-description` | Drafts PR descriptions with SYNOPSIS / PURPOSE / DESCRIPTION / WALK-THROUGH / NOTES, using bold and italic emphasis for scanning. |
69
69
  | `thatch-ticket-description` | Drafts ticket or issue descriptions (Linear or Jira) with clear sections and bold/italic emphasis for scanning. |
70
70
  | `thatch-split-overlarge-pr` | Splits already-completed work from an overlarge PR into human-reviewable, release-safe PRs targeting main. |
71
- | `thatch-clear-writing` | Prose rules for every human-facing text the dedicated writing skills do not cover: PR and ticket comments, documentation, plans, reports, and chat replies. Clarity over compression. |
71
+ | `thatch-clear-writing` | Prose rules for every human-facing text the dedicated writing skills do not cover: PR and ticket comments, documentation, plans, reports, chat replies, and code comments. Clarity over compression. |
72
72
 
73
73
  ### Workflow skills
74
74
 
75
75
  | Skill | Purpose |
76
76
  |-------|---------|
77
77
  | `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). |
78
- | `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). |
78
+ | `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. |
79
79
 
80
80
  ### Plan refinement skills
81
81
 
82
82
  | Skill | Purpose |
83
83
  |-------|---------|
84
- | `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. |
84
+ | `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. |
85
85
  | `thatch-refine-team-app` | Team codebases: consistency outranks cleverness, intent breadcrumbs are deliverables, pre-mortem and safe-to-modify lenses dominate. |
86
- | `thatch-refine-personal` | Solo projects: relaxed coordination, cleverness and experimentation welcome, premise verification stays honest. |
86
+ | `thatch-refine-personal` | Solo projects: relaxed coordination, cleverness and experimentation welcome, premise verification stays honest. Relaxes round counts, never mode selection. |
87
87
  | `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. |
88
88
  | `thatch-refine-spike` | Throwaway prototypes: skip the loop, verify only the premises the experiment's conclusion depends on. |
89
89
  | `thatch-refine-infra` | CI, Terraform, and k8s changes: blast radius, mandatory rollback plan and staged verification, pattern-following is near-absolute. |
@@ -104,7 +104,9 @@ window.
104
104
  re-installs from its canonical source). Edit the canonical source
105
105
  in the thatch repo instead.
106
106
  - The opencode-only coordinator (`thatch-code-review`) requires
107
- sub-agent support. It is not available on Claude Code or Cursor.
107
+ sub-agent support. It is not available on Claude Code or Cursor; the
108
+ `/thatch/self-review` and `/thatch/peer-review` commands run the
109
+ same specialists inline there instead.
108
110
  - Skill content is markdown read at runtime, not compiled into the
109
111
  binary. If the skill file is missing or corrupted, the skill will
110
112
  not load (silent failure).
@@ -112,7 +112,8 @@ Other tools:
112
112
  commits, CI check runs, and workflow runs
113
113
  - `thatch_watch_command_create` - wait on any local condition via a
114
114
  shell command that exits 0 when the wait is over
115
- - `thatch_watch_list` - shows the session's active watchers
115
+ - `thatch_watch_list` - shows the session's active watchers, plus any
116
+ detected events waiting for the session to go idle before delivery
116
117
  - `thatch_watch_cancel` - cancels one by id
117
118
 
118
119
  ## Lifetime
@@ -129,17 +130,31 @@ restarts:
129
130
  watch is still there.
130
131
  - Watch definitions whose 8-hour TTL expired while the session was
131
132
  away are not re-armed; the re-arm notice reports how many expired.
132
- - If a watched session is never resumed, a live session in the same
133
- project hears a one-line notice that the watch died with its
134
- session, so a watch that will never fire is at least explained.
135
- - A watcher expires after 8 hours by default.
133
+ - If a watched session's tab is closed (or the watcher's deliveries fail
134
+ past the death threshold), its watchers are cancelled, and a live
135
+ session in the same project hears a one-line notice that the watch
136
+ died with its session - the notice says the watches were cancelled,
137
+ not re-armed, so a watch that will never fire is at least explained.
138
+ - A watcher expires after 8 hours by default, and reports its own
139
+ expiry: the session gets a final notification naming the watched
140
+ target and how long the watch lived (for example, "watch on
141
+ acme/widgets#7 expired after 480 min"). The notification states
142
+ that the watched condition never occurred - it is not a signal
143
+ that whatever the watch was gating finished. Re-register the watch
144
+ if notifications are still wanted.
145
+ - A watch also dies with its session: on opencode v2, a closed tab's
146
+ watchers are cancelled automatically once its undelivered events
147
+ have aged past 2 hours (THATCH_WATCH_DEATH_MINUTES) while delivery
148
+ keeps failing, and live sessions in the same project hear a death
149
+ notice naming the dead session and its watch targets. A coordinator
150
+ can use that notice to mark a subordinate's task failed and respawn.
136
151
  - Each session can hold up to 5 active watchers (all sources share
137
152
  the budget).
138
153
  - A one-shot watch cancels itself after its first event - a single
139
154
  "tell me when this run finishes" request leaves nothing polling
140
155
  afterwards. Standing watches (the default) keep going until they
141
- are cancelled, expire, or the process ends. Command watches are
142
- always one-shot.
156
+ are cancelled, expire (and say so), or the process ends. Command
157
+ watches are always one-shot.
143
158
 
144
159
  ## Requirements and limitations
145
160
 
package/package.json CHANGED
@@ -1,10 +1,14 @@
1
1
  {
2
2
  "name": "@jeffober/thatch",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "description": "Persistent memory for AI coding agents — local embeddings, SQLite stores, zero config. Works with OpenCode (as a plugin), Claude Code (as an MCP server), and Cursor (as an MCP server).",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
7
7
  "types": "src/index.ts",
8
+ "exports": {
9
+ ".": "./src/index.ts",
10
+ "./tui": "./src/opencode/tui-plugin.ts"
11
+ },
8
12
  "bin": {
9
13
  "thatch": "./bin/thatch"
10
14
  },
@@ -38,11 +42,15 @@
38
42
  "yaml": "^2.7.0"
39
43
  },
40
44
  "peerDependencies": {
41
- "@opencode-ai/plugin": "^1.17.0"
45
+ "@opencode-ai/plugin": "^1.17.0",
46
+ "@opencode/plugin": "^2.0.15"
42
47
  },
43
48
  "peerDependenciesMeta": {
44
49
  "@opencode-ai/plugin": {
45
50
  "optional": true
51
+ },
52
+ "@opencode/plugin": {
53
+ "optional": true
46
54
  }
47
55
  },
48
56
  "keywords": [
package/src/alerts.ts CHANGED
@@ -10,7 +10,7 @@
10
10
  // deduplication is exact by construction - delivering from the TUI side
11
11
  // instead would fire once per attached TUI. State is rebuilt empty on
12
12
  // plugin setup: a v2 reload mid-turn loses that turn's transition and the
13
- // alert stays silent (accepted; see docs/plans/llm-alerts.md).
13
+ // alert stays silent (accepted; see docs/dev/features/notifications.md).
14
14
  //
15
15
  // What makes a round "real work" is split across two sources, on purpose:
16
16
  // - the TOOL side comes from live bookkeeping: the runtime marks real work
@@ -54,7 +54,13 @@ export interface HostCapabilities {
54
54
  * only the title carries over.
55
55
  */
56
56
  sessionCreate(input: { parentID: string; title: string }): Promise<{ id: string }>;
57
- /** Delete a session (child-session cleanup). Degrades to a no-op resolve on v2. */
57
+ /**
58
+ * Delete a session (extraction-child cleanup, so the picker stays clean
59
+ * and `-c` resume does not land in a child). v1: client delete. v2:
60
+ * session.remove on the promise domain (upstream #52387), runtime-guarded
61
+ * because the pinned dev types predate the surface - an older host
62
+ * degrades to a no-op (best-effort by the caller's contract).
63
+ */
58
64
  sessionDelete(id: string): Promise<void>;
59
65
  /**
60
66
  * Prompt a session with text parts. `sync` mode blocks until the child
@@ -90,15 +96,23 @@ export interface HostCapabilities {
90
96
  /**
91
97
  * Trigger the host's compaction for a session (the wrap-up compact
92
98
  * action). v1 dispatches the TUI's session_compact command (the legacy
93
- * alias route; the id is ignored). v2 has no compaction trigger reachable
94
- * from the promise context and degrades to a logged no-op.
99
+ * alias route; the id is ignored). v2 calls session.compact on the
100
+ * promise domain (upstream #52385), runtime-guarded because the pinned
101
+ * dev types predate that surface; an older host degrades to a logged
102
+ * no-op.
95
103
  */
96
104
  compactSession(sessionID: string): Promise<void>;
97
105
  /**
98
- * Exit the host application (the wrap-up exit action). v1 publishes the
99
- * TUI's app.exit command; v2 has no TUI surface and degrades to a no-op.
106
+ * End the session's presence on the host (the wrap-up exit action). The
107
+ * runtime records the session's watcher deaths and unregisters it from
108
+ * chat BEFORE calling here, so implementations only close out the host
109
+ * side. v1 publishes the TUI's app.exit command - the id is unused
110
+ * because the whole app is leaving. v2 emits the session-tab bridge's
111
+ * exit-tab-closed event, a TUI-only close of the session's OWN tab (the
112
+ * daemon hosts every tab, so an app-wide exit would take down unrelated
113
+ * sessions too); a host with no bridge degrades to a logged no-op.
100
114
  */
101
- exitHost(): Promise<void>;
115
+ exitHost(sessionID: string): Promise<void>;
102
116
  }
103
117
 
104
118
  /**