@jeffober/thatch 0.2.4 → 0.2.5

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 (54) hide show
  1. package/README.md +8 -1
  2. package/artifacts/skills/thatch-clear-writing.md +103 -5
  3. package/artifacts/skills/thatch-coordination.md +69 -0
  4. package/artifacts/skills/thatch-pr-description.md +21 -13
  5. package/artifacts/skills/thatch-review-no-slop.md +15 -0
  6. package/bin/thatch +27 -3
  7. package/docs/assets/deepwiki-badge.svg +1 -0
  8. package/docs/dev/README.md +7 -2
  9. package/docs/dev/features/cli.md +2 -0
  10. package/docs/dev/features/cross-session-chat.md +118 -11
  11. package/docs/dev/features/notifications.md +1 -2
  12. package/docs/dev/features/opencode-plugin.md +18 -3
  13. package/docs/dev/features/prediction-engine.md +44 -5
  14. package/docs/dev/features/session-tabs.md +155 -0
  15. package/docs/dev/features/setup.md +5 -0
  16. package/docs/dev/features/watchers.md +62 -6
  17. package/docs/dev/gotchas.md +68 -0
  18. package/docs/dev/mcp-parity.md +2 -2
  19. package/docs/dev/setup-and-hooks.md +7 -2
  20. package/docs/dev/skills.md +1 -1
  21. package/docs/plans/generalized-session-heartbeat.md +212 -0
  22. package/docs/plans/prediction-consolidation.md +88 -68
  23. package/docs/user/README.md +12 -3
  24. package/docs/user/cli.md +3 -0
  25. package/docs/user/commands.md +11 -4
  26. package/docs/user/cross-session-chat.md +31 -8
  27. package/docs/user/prediction-engine.md +11 -0
  28. package/docs/user/setup.md +16 -0
  29. package/docs/user/skills.md +1 -1
  30. package/docs/user/watchers.md +22 -7
  31. package/package.json +10 -2
  32. package/src/alerts.ts +1 -1
  33. package/src/chat.ts +320 -27
  34. package/src/commands.ts +14 -0
  35. package/src/db.ts +51 -1
  36. package/src/git.ts +3 -1
  37. package/src/hygiene.ts +5 -0
  38. package/src/mcp.ts +95 -7
  39. package/src/opencode/tui-plugin.ts +70 -0
  40. package/src/opencode/v2.ts +197 -4
  41. package/src/prediction.ts +90 -0
  42. package/src/prompts.ts +110 -23
  43. package/src/runtime.ts +187 -9
  44. package/src/session-db.ts +4 -4
  45. package/src/session-tab-shared.ts +180 -0
  46. package/src/setup.ts +9 -3
  47. package/src/skills.ts +1 -0
  48. package/src/tool-defs.ts +412 -55
  49. package/src/tools.ts +2 -0
  50. package/src/watchers.ts +263 -26
  51. package/docs/plans/intuition-drives.md +0 -147
  52. package/docs/plans/llm-alerts.md +0 -287
  53. package/docs/plans/thatch-actions.md +0 -179
  54. package/docs/plans/worktree-deletion-recovery.md +0 -246
@@ -138,6 +138,7 @@ Stores are created automatically. No setup required.
138
138
  | `thatch_prediction_update` | Create, reinforce, or weaken a prediction. Takes a matcher (context description), a prediction (preference statement), and a signal (confirm/disconfirm/soft/create). |
139
139
  | `thatch_prediction_list` | List all predictions in a store with matchers, confidence, and provenance. |
140
140
  | `thatch_prediction_delete` | Delete a prediction by semantic match. Edges and provenance are cascade-deleted. |
141
+ | `thatch_prediction_mark_checked` | Record a verdict (duplicate/distinct) on a pair the hygiene nudge flagged, so it stops resurfacing. |
141
142
 
142
143
  ### Behavior tools
143
144
 
@@ -183,12 +184,19 @@ search` - JSONL output designed for piping to `jq` or `grep`.
183
184
  | `thatch_watch_create` | Watch a GitHub PR for events (comments, review comments and replies, thread resolutions, commits, status changes, description edits, CI completions). The plugin polls in the background and prompts the session when a watched event happens. |
184
185
  | `thatch_watch_branch_create` | Watch a GitHub branch (typically main) for commit landings, CI check-run completions, and workflow runs, with an optional workflow-name filter. |
185
186
  | `thatch_watch_command_create` | Wait on any local condition via a shell command that exits 0 when the wait is over. One-shot: one notification on the first exit 0, then it cancels itself. Accepts a `cd` path to run somewhere other than the project directory. |
186
- | `thatch_watch_list` | List this session's active watchers. |
187
+ | `thatch_watch_list` | List this session's active watchers, plus events waiting for idle delivery. |
187
188
  | `thatch_watch_cancel` | Cancel one of this session's watchers by id. |
188
189
 
189
190
  See [watchers.md](watchers.md) for the full behavior, lifetime, and
190
191
  requirements.
191
192
 
193
+ ### Session tab tools (opencode v2 only)
194
+
195
+ | Tool | What it does |
196
+ |------|-------------|
197
+ | `thatch_session_tab` | Spawn a detached subordinate LLM session in a new TUI tab: creates the session, pre-assigns its chat name, delivers your task prompt with your coordinator-identity framing, and opens its tab beside yours (unfocused). Takes `prompt` and `title` (required) plus exactly one of `worktree` (a git worktree of the current repository) or `directory` (any existing directory). Requires opencode v2 and the `thatch-coordination` skill describes the supervisor workflow. |
198
+ | `thatch_session_tab_close` | Close a dispatched subordinate's TUI tab when its task is verified and done. Tab-level only: the session survives and the user can reopen the tab; nothing is deleted. Target: the subordinate's chat name or session id. opencode v2 only. |
199
+
192
200
  ### Chat tools
193
201
 
194
202
  | Tool | What it does |
@@ -247,7 +255,8 @@ Beyond the tools, thatch hooks into opencode itself:
247
255
  section ahead of the checklist (say goodbye or hand off context:
248
256
  `/thatch/exit Good work - see you tomorrow`). Alongside the wrap-ups,
249
257
  on-demand actions (`/thatch/defrag`, `/thatch/extract`, `/thatch/hygiene`,
250
- `/thatch/reflect`) run the memory-maintenance behaviors whenever you want,
258
+ `/thatch/reflect`, `/thatch/whois`) run the memory-maintenance behaviors
259
+ whenever you want,
251
260
  not just when a nudge fires -- see [commands.md](commands.md).
252
261
  A greenlit `/thatch/exit` also unregisters the session from the chat
253
262
  directory, so other sessions stop sending it mail. The commands
@@ -343,7 +352,7 @@ response, walkthrough, and workflow skills that support the review pipeline:
343
352
  | `thatch-pr-description` | Drafts PR descriptions with SYNOPSIS / PURPOSE / DESCRIPTION / WALK-THROUGH / NOTES, project-context research, clarity checks, and bold/italic emphasis for scanning. |
344
353
  | `thatch-ticket-description` | Drafts ticket or issue descriptions (Linear or Jira) with clear sections, project-context research, clarity checks, and bold/italic emphasis for scanning. |
345
354
  | `thatch-split-overlarge-pr` | Splits already-completed work from an overlarge PR into human-reviewable, release-safe PRs targeting main. |
346
- | `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. |
355
+ | `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. |
347
356
 
348
357
  ### opencode-only skills
349
358
 
package/docs/user/cli.md CHANGED
@@ -163,6 +163,9 @@ thatch setup --claude --skills-only # refresh just the skill files
163
163
  | `THATCH_BEHAVIOR_THRESHOLD` | `0.60` | Cosine threshold for behavior auto-fire |
164
164
  | `CLAUDE_PROJECT_DIR` | `process.cwd()` | Claude Code project directory |
165
165
  | `CURSOR_PROJECT_DIR` | (falls through) | Cursor project directory |
166
+ | `THATCH_PROJECT_DIR` | (falls through) | Project dir pinned by project-local MCP configs - first in the server's resolution chain |
167
+ | `THATCH_WATCH_DEATH_MINUTES` | `120` | Pending-age threshold before a dead session's watchers are cancelled and announced |
168
+ | `THATCH_PROJECT_DIR` | (falls through) | Project dir pinned by project-local MCP configs - first in the server's resolution chain |
166
169
  | `CLAUDE_CONFIG_DIR` | `~/.claude` | Claude Code config directory |
167
170
  | `CURSOR_CONFIG_DIR` | `~/.cursor` | Cursor config directory |
168
171
 
@@ -17,6 +17,11 @@ or you invoke it.
17
17
  The agent asks before forgetting anything that may still matter.
18
18
  - `/thatch/reflect` — run the session-reflection skill to persist what the
19
19
  current session learned.
20
+ - `/thatch/whois` — report the current session's chat identity: its handle,
21
+ topic, project, and whether it runs in the project root or a worktree.
22
+ Text after the command looks up another session instead (by handle or
23
+ session id) — this is how you map a handle from the roster to the tab you
24
+ see: the topic is the session's live title. opencode only.
20
25
  - `/thatch/refine` — refine a plan for a code change under the current
21
26
  project type's requirements: the agent classifies the project (team,
22
27
  personal, shared library, spike, infrastructure, or open source
@@ -38,13 +43,15 @@ or you invoke it.
38
43
  | `/thatch/extract` | yes | no | no |
39
44
  | `/thatch/hygiene` | yes | yes | yes (as an MCP prompt) |
40
45
  | `/thatch/reflect` | yes | yes | yes (as an MCP prompt) |
46
+ | `/thatch/whois` | yes | no | no |
41
47
  | `/thatch/refine` | yes | yes | yes (as an MCP prompt) |
42
48
  | `/thatch/compact`, `/thatch/exit` | yes | no | no |
43
49
 
44
- Why the gaps: `/thatch/extract` needs the invoking session's ID, which only
45
- the opencode plugin can supply. The wrap-up commands trigger TUI actions
46
- through the plugin. Cursor has no file-based commands; its prompts come from
47
- the MCP server (`prompts/list` and `prompts/get`).
50
+ Why the gaps: `/thatch/extract` and `/thatch/whois` need the invoking
51
+ session's ID, which only the opencode plugin can supply. The wrap-up
52
+ commands trigger TUI actions through the plugin. Cursor has no file-based
53
+ commands; its prompts come from the MCP server (`prompts/list` and
54
+ `prompts/get`).
48
55
 
49
56
  ## Installation
50
57
 
@@ -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
@@ -29,16 +31,33 @@ explicitly with `thatch_chat_register` (no arguments) - useful after
29
31
  checked in, its project, whether it runs in the project root or a linked
30
32
  git worktree, its topic, and your unread count
31
33
  - `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
34
+ name or session id, and states the recipient's liveness at send time.
35
+ An identical resend within a few seconds is treated as a client retry:
36
+ while the first copy is still unread it succeeds but delivers nothing
37
+ new (the tool output says so); once the recipient has read it, the same
38
+ body delivers again - a deliberate repeat, not a retry. A body over
39
+ 2,000 characters is not refused: it is auto-split into several messages,
40
+ and the tool output reports the split ("SENT AS N PARTS"). The whole
41
+ message is capped at 20,000 characters; longer is refused with the
42
+ attempted count
43
+ - `thatch_chat_read` drains the session's inbox. A message longer than
44
+ one page (2,000 characters) is shown clipped, with a marker naming the
45
+ characters ("[clipped: showing 2000 of 2047 characters]"); page through
46
+ the rest with the `message` and `offset` arguments (each drain line
47
+ carries the message id). Paging never marks anything read
34
48
  - `thatch_chat_broadcast` delivers one message to every other live
35
- registered session at once
49
+ registered session at once (a long body is split per recipient, exactly
50
+ like a split `chat_send`)
36
51
  - `thatch_chat_unregister` leaves the directory
37
52
 
38
53
  When mail arrives for an idle session, thatch wakes it with a notification -
39
54
  the agent gets a model turn even with nobody at the keyboard, reads its
40
55
  inbox, and decides what to do. The notification names the senders and the
41
- count only; the agent reads the actual messages itself.
56
+ count only; the agent reads the actual messages itself. A split message
57
+ arrives as one grouped notification ("N unread messages from X"), and the
58
+ parts carry their own markers so the reader assembles the whole before
59
+ acting: the first part is prefixed "(message 1 of N)" and every part but
60
+ the last is suffixed "(continued in next message)".
42
61
 
43
62
  Broadcasts are for announcements and open questions ("which of you is
44
63
  working on the payments code?", "main just moved, rebase if you are based
@@ -198,10 +217,14 @@ construction. Three mitigations:
198
217
  the true session id from the host and record which process spawned them;
199
218
  the MCP server resolves its own parent process against that record, so
200
219
  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
220
+ tools trust the `as` argument the caller supplies (hooks and MCP
202
221
  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.
222
+ but the claim is directory-bound: a session may only claim an identity
223
+ registered to its own project, and a cross-project claim is refused.
224
+ The refusal teaches the fix - register a fresh identity for the project
225
+ once, then reuse it; do not re-register every turn. opencode is immune
226
+ (the host supplies identity). The threat model is one user's machine;
227
+ same-project spoofing on Cursor remains possible and accepted.
205
228
  - **No external content via wake.** The wake notification names senders
206
229
  and counts only - bodies flow exclusively through the framed
207
230
  `chat_read` surface.
@@ -59,6 +59,7 @@ surface it to you, or ignore it. You never see the nudge directly.
59
59
  | `thatch_prediction_update` | Create, reinforce, or weaken a prediction. Takes a matcher (situation), prediction (preference), signal (confirm/disconfirm/soft/create), and rationale. |
60
60
  | `thatch_prediction_list` | List all predictions with matchers, confidence, evidence count, and provenance history. |
61
61
  | `thatch_prediction_delete` | Delete a prediction by semantic match. Edges and provenance are cascade-deleted. |
62
+ | `thatch_prediction_mark_checked` | Record a verdict (duplicate/distinct) on a pair the hygiene nudge flagged, so it stops resurfacing. |
62
63
 
63
64
  ## Configuration
64
65
 
@@ -83,5 +84,15 @@ surface it to you, or ignore it. You never see the nudge directly.
83
84
  store does not automatically apply to other projects. The global
84
85
  store is shared across projects.
85
86
 
87
+ ## Keeping the model tidy
88
+
89
+ If the same preference gets recorded twice in different words, the hygiene
90
+ nudge reports `prediction duplicate pairs pending review`. The agent reads
91
+ both, then either merges them (moves the loser's matchers onto the winner
92
+ with `thatch_prediction_update`, deletes the loser with
93
+ `thatch_prediction_delete`) or judges them distinct — and records the
94
+ verdict with `thatch_prediction_mark_checked` either way, so an adjudicated
95
+ pair never nags again.
96
+
86
97
  See [memory.md](memory.md) for the base memory system and
87
98
  [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
@@ -68,7 +68,7 @@ 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
 
@@ -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.5",
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