@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.
- package/README.md +56 -26
- package/artifacts/skills/common.md +1 -0
- package/artifacts/skills/thatch-clear-writing.md +103 -5
- package/artifacts/skills/thatch-code-archaeology.md +9 -0
- package/artifacts/skills/thatch-code-review.md +16 -8
- package/artifacts/skills/thatch-coordination.md +69 -0
- package/artifacts/skills/thatch-plan-refinement.md +33 -4
- package/artifacts/skills/thatch-pr-description.md +21 -13
- 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-no-slop.md +15 -0
- package/artifacts/skills/thatch-review-response.md +1 -1
- package/artifacts/skills/thatch-review-synthesizer.md +21 -1
- package/bin/thatch +48 -15
- package/docs/assets/deepwiki-badge.svg +1 -0
- package/docs/dev/README.md +18 -10
- package/docs/dev/features/README.md +1 -1
- package/docs/dev/features/behavior-engine.md +10 -0
- package/docs/dev/features/cli.md +4 -1
- package/docs/dev/features/commands.md +40 -9
- package/docs/dev/features/cross-session-chat.md +194 -23
- package/docs/dev/features/database.md +4 -4
- package/docs/dev/features/notifications.md +1 -2
- package/docs/dev/features/nudge-pipeline.md +14 -0
- package/docs/dev/features/opencode-plugin.md +56 -15
- package/docs/dev/features/prediction-engine.md +71 -6
- 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 +161 -0
- package/docs/dev/features/setup.md +5 -0
- package/docs/dev/features/watchers.md +62 -6
- package/docs/dev/gotchas.md +89 -0
- package/docs/dev/mcp-parity.md +5 -5
- package/docs/dev/setup-and-hooks.md +7 -2
- package/docs/dev/skills.md +12 -9
- package/docs/plans/generalized-session-heartbeat.md +212 -0
- package/docs/plans/prediction-consolidation.md +88 -68
- package/docs/user/README.md +29 -13
- package/docs/user/behavior-engine.md +7 -1
- package/docs/user/cli.md +5 -1
- package/docs/user/code-review.md +43 -2
- package/docs/user/commands.md +37 -10
- package/docs/user/cross-session-chat.md +76 -26
- package/docs/user/prediction-engine.md +23 -1
- package/docs/user/setup.md +16 -0
- package/docs/user/skills.md +9 -7
- package/docs/user/watchers.md +22 -7
- package/package.json +10 -2
- package/src/alerts.ts +1 -1
- package/src/capabilities.ts +20 -6
- package/src/chat.ts +358 -43
- package/src/commands.ts +123 -18
- package/src/db.ts +74 -3
- package/src/git.ts +3 -1
- package/src/hygiene.ts +5 -0
- package/src/mcp.ts +101 -9
- package/src/opencode/tui-plugin.ts +90 -0
- package/src/opencode/v2.ts +440 -66
- package/src/prediction.ts +90 -0
- package/src/prompts.ts +164 -29
- package/src/runtime.ts +298 -15
- package/src/scoring-engine.ts +40 -9
- package/src/session-db.ts +4 -4
- package/src/session-tab-shared.ts +231 -0
- package/src/setup.ts +9 -3
- package/src/skills.ts +6 -9
- package/src/tab-state.ts +125 -0
- package/src/tool-defs.ts +454 -76
- package/src/tools.ts +2 -0
- package/src/watchers.ts +263 -26
- package/docs/plans/intuition-drives.md +0 -147
- package/docs/plans/llm-alerts.md +0 -287
- package/docs/plans/thatch-actions.md +0 -179
- 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.
|
|
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
|
|
27
|
-
have missed two heartbeats, about a minute (the opencode process
|
|
28
|
-
them has probably stopped). Each row carries
|
|
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
|
-
|
|
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
|
|
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.
|
|
41
|
-
|
|
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
|
|
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.
|
|
48
|
-
never
|
|
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
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
its
|
|
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.
|
|
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
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
addressing mail to a
|
|
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
|
|
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
|
-
|
|
204
|
-
|
|
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.
|
package/docs/user/setup.md
CHANGED
|
@@ -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
|
package/docs/user/skills.md
CHANGED
|
@@ -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
|
|
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).
|
package/docs/user/watchers.md
CHANGED
|
@@ -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
|
|
133
|
-
|
|
134
|
-
session
|
|
135
|
-
-
|
|
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
|
|
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.
|
|
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/
|
|
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
|
package/src/capabilities.ts
CHANGED
|
@@ -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
|
-
/**
|
|
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
|
|
94
|
-
*
|
|
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
|
-
*
|
|
99
|
-
*
|
|
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
|
/**
|