@jeffober/thatch 0.2.6 → 0.2.8
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/artifacts/skills/thatch-clear-writing.md +11 -0
- package/artifacts/skills/thatch-code-review.md +10 -8
- package/artifacts/skills/thatch-review-clarity.md +101 -0
- package/artifacts/skills/thatch-review-followup.md +1 -1
- package/artifacts/skills/thatch-review-synthesizer.md +1 -1
- package/bin/thatch +90 -29
- package/docs/dev/features/cli.md +24 -7
- package/docs/dev/features/commands.md +2 -2
- package/docs/dev/features/cross-session-chat.md +21 -10
- package/docs/dev/features/notifications.md +1 -1
- package/docs/dev/features/opencode-plugin.md +6 -4
- package/docs/dev/features/session-lifecycle.md +35 -0
- package/docs/dev/features/session-tabs.md +3 -2
- package/docs/dev/features/watchers.md +4 -2
- package/docs/dev/skills.md +2 -1
- package/docs/user/README.md +3 -2
- package/docs/user/cli.md +28 -16
- package/docs/user/code-review.md +4 -3
- package/docs/user/cross-session-chat.md +5 -4
- package/docs/user/skills.md +1 -0
- package/package.json +1 -1
- package/src/capabilities.ts +36 -0
- package/src/chat.ts +17 -7
- package/src/commands.ts +1 -1
- package/src/opencode/v2.ts +15 -0
- package/src/prompts.ts +1 -0
- package/src/runtime.ts +191 -1
- package/src/session-db.ts +99 -7
- package/src/tool-defs.ts +14 -2
|
@@ -13,6 +13,7 @@ opencode emits bus events for session lifecycle changes. Thatch subscribes to th
|
|
|
13
13
|
- Sub-agent lifecycle: task-dispatched sub-agents complete accepted entries but are not deleted
|
|
14
14
|
- Session error recovery: child errors requeue parent's accepted entries
|
|
15
15
|
- Session deletion recovery: child deletion requeues; parent deletion completes accepted entries
|
|
16
|
+
- Stranded-session self-heal: sessions whose recorded directory is gone (a merged + deleted worktree) are deleted when machinery (`thatch-extraction` corpses) and moved to the project's canonical checkout when real
|
|
16
17
|
- Wrap-up commands: `/thatch/compact` and `/thatch/exit` arm a greenlight check resolved on the next idle
|
|
17
18
|
|
|
18
19
|
## How it works
|
|
@@ -118,6 +119,40 @@ first-ever install is invisible until the next server start.
|
|
|
118
119
|
- Clear the `compacting` flag so chat.message nudges resume
|
|
119
120
|
- See [compaction-recovery.md](compaction-recovery.md)
|
|
120
121
|
|
|
122
|
+
### Stranded-session self-heal sweep
|
|
123
|
+
|
|
124
|
+
Why it exists: opencode v2 records a directory on every session row, and
|
|
125
|
+
opening a session whose directory is gone (a merged + deleted worktree) boots
|
|
126
|
+
a dead path and shows the "session location unavailable" recovery dialog.
|
|
127
|
+
Sessions get stranded there in two ways: extraction children leaked by hosts
|
|
128
|
+
older than v2.0.22 (whose session domain had no `remove`, so cleanup was a
|
|
129
|
+
silent no-op) and children whose idle event died with a daemon restart. The
|
|
130
|
+
sweep runs at setup and on an interval (`THATCH_STRAND_SWEEP_MS`), and the
|
|
131
|
+
rehydrate pass finalizes journaled extraction children through the host
|
|
132
|
+
before pruning their rows, so both the corpse population and the leak paths
|
|
133
|
+
are covered.
|
|
134
|
+
|
|
135
|
+
What it does, decided by `planStrandActions` (pure, unit-tested):
|
|
136
|
+
|
|
137
|
+
- Sessions in a live directory are never touched, whatever the row looks like
|
|
138
|
+
- Machinery (`thatch-extraction` title) in a dead directory: delete via the
|
|
139
|
+
host's session delete
|
|
140
|
+
- Real top-level sessions in a dead directory: MOVE to the project's
|
|
141
|
+
canonical checkout (`project.worktree` in opencode.db) -- the same outcome
|
|
142
|
+
as the manual recovery dialog, minus the picking; history is preserved and
|
|
143
|
+
user sessions are never deleted
|
|
144
|
+
- Host-created task children (parentId set): always skipped -- they are
|
|
145
|
+
opencode's own records of subagent work and deleting them would break the
|
|
146
|
+
parent transcript's subagent rendering
|
|
147
|
+
- Unresolvable canonical or no move endpoint: skipped and logged
|
|
148
|
+
|
|
149
|
+
The host supplies the enumeration (v1: `client.session.list`; v2: readonly
|
|
150
|
+
read of opencode.db, since the promise domain has no session list and the
|
|
151
|
+
sweep needs machine-wide rows) plus the move/delete endpoints; every leg
|
|
152
|
+
degrades silently. A summary toast fires only when something was healed. Set
|
|
153
|
+
`THATCH_STRAND_SWEEP=off` to disarm the sweep entirely (tests do, so it can
|
|
154
|
+
never act on a developer's real database through mocks).
|
|
155
|
+
|
|
121
156
|
### Internal state maps
|
|
122
157
|
|
|
123
158
|
- **childToParent**: `Map<childID, parentID>` -- maps child sessions to their parents
|
|
@@ -147,8 +147,9 @@ dependency for the TUI entry's runtime resolution.
|
|
|
147
147
|
## Testing
|
|
148
148
|
|
|
149
149
|
- Unit: `tests/session-tab-shared.test.ts` (validators, definition shape,
|
|
150
|
-
guard logic), `tests/opencode-v2.test.ts` (the execute flows'
|
|
151
|
-
the `makeContext` double with `session.move` + the `rpc`
|
|
150
|
+
guard logic), `tests/opencode-v2-adapter-tabs.test.ts` (the execute flows'
|
|
151
|
+
ordering via the `makeContext` double with `session.move` + the `rpc`
|
|
152
|
+
domain).
|
|
152
153
|
- QA: uc-059 (tool list + MCP leak check), uc-014/uc-060 (skill counts),
|
|
153
154
|
uc-119 (the live spawn flow through the docker sandbox).
|
|
154
155
|
- Live smoke (recorded 2026-10-08, ALL SCENARIOS PASSED): headless legs -
|
|
@@ -291,9 +291,11 @@ src/watchers.ts, test-enforced).
|
|
|
291
291
|
session.status tracking, session.deleted cleanup, dispose
|
|
292
292
|
- `src/prompts.ts` - `watcherNotificationNudge()`, system prompt
|
|
293
293
|
Watchers section
|
|
294
|
-
- `tests/watchers.test.ts` - registry, poll, and
|
|
294
|
+
- `tests/watchers-registry.test.ts` - registry, poll, and death-detection unit tests
|
|
295
|
+
- `tests/watchers-git-state.test.ts` - PR/branch diff and check-run description unit tests
|
|
296
|
+
- `tests/watchers-commands.test.ts` - command watcher and poll-resilience unit tests
|
|
295
297
|
(mocked gh and command runners, no network)
|
|
296
|
-
- `tests/tool-defs.test.ts` - watch tool execute-function tests
|
|
298
|
+
- `tests/tool-defs-watch-chat.test.ts` - watch tool execute-function tests
|
|
297
299
|
- `tests/qa/auto/uc-095-watchers.ts` - end-to-end lifecycle against a
|
|
298
300
|
mocked gh runner
|
|
299
301
|
- `tests/qa/auto/uc-101-command-watchers.ts` - command-watch lifecycle
|
package/docs/dev/skills.md
CHANGED
|
@@ -43,6 +43,7 @@ description: Extract durable project facts ... Use when ...
|
|
|
43
43
|
| `thatch-review-state-flow` | Data flow and contracts: module boundaries, implicit FSMs, error propagation. |
|
|
44
44
|
| `thatch-review-economy` | Design simplicity and maintainability: is the complexity earned? Evaluates both the overall change design (forest) and individual touch points (trees) for unnecessary complexity, redundancy, and simpler available alternatives. |
|
|
45
45
|
| `thatch-review-no-slop` | AI writing anti-patterns: change narration, fourth-wall breaks, em dashes, filler. |
|
|
46
|
+
| `thatch-review-clarity` | Prose clarity in comments, docstrings, error strings, and docs: made-up phrases, buzzwords, untranslated jargon, clause chains, buried leads, doc/code responsibility splits, checked against the project's clear-writing rules regardless of who wrote the text. |
|
|
46
47
|
| `thatch-review-breadcrumbs` | Comment narrative: do comments form a coherent outline of behavior? |
|
|
47
48
|
| `thatch-review-mark-and-sweep` | Mechanical change completeness: whole-repo sweep for stragglers after renames, flag removals, API substitutions. |
|
|
48
49
|
| `thatch-review-highlights` | Positive finding detection: notably clever solutions, cleanup done along the way, documentation that helps. Medium-high bar against generic praise. |
|
|
@@ -79,7 +80,7 @@ description: Extract durable project facts ... Use when ...
|
|
|
79
80
|
|
|
80
81
|
## REVIEW_COMMON
|
|
81
82
|
|
|
82
|
-
The
|
|
83
|
+
The 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
84
|
|
|
84
85
|
- **Static analysis only** — no running tests/linters/compilers.
|
|
85
86
|
- **Scope gathering** — resolve the git range, `git diff --stat`, read changed
|
package/docs/user/README.md
CHANGED
|
@@ -202,7 +202,7 @@ requirements.
|
|
|
202
202
|
| Tool | What it does |
|
|
203
203
|
|------|-------------|
|
|
204
204
|
| `thatch_chat_register` | Join the cross-session chat directory, so other sessions on this machine can message you. Display names are assigned by thatch, never chosen (a pool name plus a never-reused counter), so a name always refers to the same session. Call with no arguments to join, rejoin after unregistering, or check your name. On hosts without session context (Claude Code, Cursor), pass the name your thatch hook printed as `as`. |
|
|
205
|
-
| `thatch_chat_list` | List registered sessions grouped into Active and Stale sections (stale = two missed heartbeats, about a minute; its host process has probably stopped), with how long ago each last checked in, each session's project and checkout kind (project root or linked worktree), its topic, and your unread count. |
|
|
205
|
+
| `thatch_chat_list` | List registered sessions grouped into Active, Recent, and Stale sections (recent = alive but no open tab, so a wake may not be guaranteed; stale = two missed heartbeats, about a minute; its host process has probably stopped), with how long ago each last checked in, each session's project and checkout kind (project root or linked worktree), its topic, and your unread count. |
|
|
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. |
|
|
@@ -331,6 +331,7 @@ response, walkthrough, and workflow skills that support the review pipeline:
|
|
|
331
331
|
| `thatch-review-state-flow` | Data flow and contracts: module boundaries, implicit state machines, error propagation, separation of concerns. |
|
|
332
332
|
| `thatch-review-economy` | Design simplicity and maintainability: is the complexity earned? Evaluates both the overall change design (forest) and individual touch points (trees) for unnecessary complexity, redundancy, and simpler available alternatives. |
|
|
333
333
|
| `thatch-review-no-slop` | AI writing anti-patterns: change narration, fourth wall breaks, em dashes, hedging, filler. |
|
|
334
|
+
| `thatch-review-clarity` | Prose clarity in comments, docstrings, error strings, and docs: made-up phrases, buzzwords, untranslated jargon, clause chains, buried leads, doc/code responsibility splits, checked against the project's clear-writing rules regardless of who wrote the text. |
|
|
334
335
|
| `thatch-review-breadcrumbs` | Comment narrative: do comments form a coherent outline of the code's behavior? |
|
|
335
336
|
| `thatch-review-mark-and-sweep` | Mechanical change completeness: whole-repo sweep for stragglers after renames, flag removals, API substitutions. |
|
|
336
337
|
| `thatch-review-highlights` | Positive finding detection: notably clever solutions, cleanup done along the way, documentation that helps. Medium-high bar against generic praise. |
|
|
@@ -406,7 +407,7 @@ specialist in sequence, then synthesize:
|
|
|
406
407
|
```text
|
|
407
408
|
1. Load thatch-review-pedantic, review branch feature-x, report findings.
|
|
408
409
|
2. Load thatch-review-acceptance, review the same branch.
|
|
409
|
-
3. ... repeat for state-flow, no-slop, breadcrumbs, mark-and-sweep ...
|
|
410
|
+
3. ... repeat for state-flow, no-slop, clarity, breadcrumbs, mark-and-sweep ...
|
|
410
411
|
4. Load thatch-review-synthesizer, verify and aggregate all findings.
|
|
411
412
|
```
|
|
412
413
|
|
package/docs/user/cli.md
CHANGED
|
@@ -4,6 +4,10 @@ Thatch ships with a command-line tool for inspecting memories and
|
|
|
4
4
|
running maintenance tasks outside the agent. It requires `bun` on your
|
|
5
5
|
PATH.
|
|
6
6
|
|
|
7
|
+
`thatch --help` (or `-h`) prints the full command list and exits 0.
|
|
8
|
+
The flag works anywhere in the command line: `thatch chat list -h`
|
|
9
|
+
shows help instead of running the command.
|
|
10
|
+
|
|
7
11
|
## Memory inspection
|
|
8
12
|
|
|
9
13
|
```bash
|
|
@@ -45,7 +49,8 @@ Read-only access to the chat directory your opencode sessions share
|
|
|
45
49
|
(see [cross-session-chat.md](cross-session-chat.md)):
|
|
46
50
|
|
|
47
51
|
```bash
|
|
48
|
-
thatch chat list # who is
|
|
52
|
+
thatch chat list # who is open now: name, age, project, topic
|
|
53
|
+
thatch chat list --recent # also show alive sessions with no open tab
|
|
49
54
|
thatch chat list --stale all # also show stale sessions older than a day
|
|
50
55
|
thatch chat tail # follow sent and read events, live (JSONL)
|
|
51
56
|
thatch chat tail --once # print the last 20 messages and exit
|
|
@@ -55,21 +60,28 @@ thatch chat tail --from al --to bob # substring match on names
|
|
|
55
60
|
thatch chat tail --since 2026-09-12 --until "2026-09-13 09:00" # a window
|
|
56
61
|
```
|
|
57
62
|
|
|
58
|
-
`chat list`
|
|
59
|
-
Stale
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
63
|
+
`chat list` renders the chat roster in three sections - Active, Recent,
|
|
64
|
+
and Stale - each as aligned columns under a header row: how long since its last heartbeat, its status (`open` - a tab is open now - or
|
|
65
|
+
`recent` - host alive, no open tab - for opencode sessions; `active` or
|
|
66
|
+
`idle` for Claude Code and Cursor sessions, which only beat when a
|
|
67
|
+
prompt runs), its project, and its topic (the session's title) - the
|
|
68
|
+
fastest way to answer "which session should I talk to about X?" Rows
|
|
69
|
+
are grouped by project (alphabetical; sessions with no project last),
|
|
70
|
+
most recently seen first within each project.
|
|
71
|
+
|
|
72
|
+
The default view shows the sessions you can wake or reach now: open
|
|
73
|
+
sessions plus Claude Code and Cursor rows, which read mail at their
|
|
74
|
+
next prompt. A recent session - its harness is alive but its tab is
|
|
75
|
+
closed, so a wake may not reach it until the tab reopens - collapses
|
|
76
|
+
into a muted hidden-count note pointing at `--recent`, which reveals
|
|
77
|
+
the Recent section. A stale session has missed two heartbeats, about a
|
|
78
|
+
minute: the opencode process hosting it has probably stopped. Its
|
|
79
|
+
section is hidden too, behind `--stale`, but without a count note - the
|
|
80
|
+
default view simply drops stale rows. The stale section
|
|
81
|
+
shows rows up to one day old by default; older ones collapse into a
|
|
82
|
+
hidden-count note so long-dead sessions cannot bury the fresh signal.
|
|
83
|
+
Pass `--stale N` (days) to widen the window or `--stale all` to see
|
|
84
|
+
every stale row. Color is added only when the
|
|
73
85
|
output is a terminal; a pipe prints plain aligned text.
|
|
74
86
|
|
|
75
87
|
`chat tail` prints the conversation as a JSONL event log: one JSON
|
package/docs/user/code-review.md
CHANGED
|
@@ -25,7 +25,7 @@ the review:
|
|
|
25
25
|
3. **Research affected workflows**: load `thatch-code-archaeology`,
|
|
26
26
|
trace the code paths the change touches.
|
|
27
27
|
4. **Estimate complexity**: partition the diff into review units.
|
|
28
|
-
5. **Dispatch specialists**: fan out the
|
|
28
|
+
5. **Dispatch specialists**: fan out the specialists as
|
|
29
29
|
parallel sub-agents, each with the context brief.
|
|
30
30
|
6. **Synthesize**: the synthesizer reads all specialist output,
|
|
31
31
|
verifies each finding (cited text exists, claim is accurate,
|
|
@@ -38,7 +38,7 @@ the review:
|
|
|
38
38
|
sibling is not a defect, and inconsistencies between connected
|
|
39
39
|
PRs surface as cross-PR notes.
|
|
40
40
|
|
|
41
|
-
## The
|
|
41
|
+
## The specialists
|
|
42
42
|
|
|
43
43
|
| Skill | What it checks |
|
|
44
44
|
|-------|-------------|
|
|
@@ -47,6 +47,7 @@ the review:
|
|
|
47
47
|
| `thatch-review-state-flow` | Module boundaries, implicit state machines, error propagation, separation of concerns. |
|
|
48
48
|
| `thatch-review-economy` | Design simplicity and maintainability: is the complexity earned? Evaluates both the overall change design (forest) and individual touch points (trees) for unnecessary complexity, redundancy, and simpler available alternatives. |
|
|
49
49
|
| `thatch-review-no-slop` | AI writing anti-patterns: change narration, fourth wall breaks, em dashes, hedging, filler. |
|
|
50
|
+
| `thatch-review-clarity` | Prose clarity in comments, docstrings, error strings, and docs: made-up phrases, buzzwords, untranslated jargon, clause chains, buried leads, doc/code responsibility splits, checked against the project's clear-writing rules regardless of who wrote the text. |
|
|
50
51
|
| `thatch-review-breadcrumbs` | Comment narrative: do comments form a coherent outline of the code's behavior? |
|
|
51
52
|
| `thatch-review-mark-and-sweep` | Mechanical change completeness: whole-repo sweep for stragglers after renames, flag removals, API substitutions. |
|
|
52
53
|
| `thatch-review-highlights` | Positive finding detection: clever solutions, cleanup done along the way, documentation that helps. Medium-high bar. |
|
|
@@ -61,7 +62,7 @@ Two slash commands run the same pipeline with the findings weighted for
|
|
|
61
62
|
whose work is under review.
|
|
62
63
|
|
|
63
64
|
- `/thatch/self-review` reviews your own completed work before you
|
|
64
|
-
submit it. The slop, technique, and breadcrumbs lenses weigh
|
|
65
|
+
submit it. The slop, clarity, technique, and breadcrumbs lenses weigh
|
|
65
66
|
heaviest, simplicity is overweighted on shared projects, and
|
|
66
67
|
technique or highlights findings that would simplify the design are
|
|
67
68
|
surfaced for an adopt-or-dismiss decision rather than left as
|
|
@@ -25,11 +25,12 @@ explicitly with `thatch_chat_register` (no arguments) - useful after
|
|
|
25
25
|
`chat_unregister`, or just to check your assigned name. Once registered:
|
|
26
26
|
|
|
27
27
|
- `thatch_chat_list` shows every registered session on the machine, grouped
|
|
28
|
-
into Active and Stale sections: active sessions
|
|
28
|
+
into Active, Recent, and Stale sections: active sessions either have an
|
|
29
|
+
opencode tab open right now or are Claude Code and Cursor rows, which
|
|
30
|
+
read mail at their next prompt; recent ones are alive but have no open
|
|
31
|
+
tab, so a wake may not reach them until the tab reopens; stale
|
|
29
32
|
ones have missed two heartbeats, about a minute (the opencode process
|
|
30
|
-
hosting them has probably stopped). Each
|
|
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
|
+
hosting them has probably stopped). Each
|
|
33
34
|
row carries how long ago the session last
|
|
34
35
|
checked in, its project, whether it runs in the project root or a linked
|
|
35
36
|
git worktree, its topic, and your unread count
|
package/docs/user/skills.md
CHANGED
|
@@ -43,6 +43,7 @@ Skills are split into shared (all hosts) and opencode-only:
|
|
|
43
43
|
| `thatch-review-state-flow` | Data flow and contracts: module boundaries, implicit state machines, error propagation, separation of concerns. |
|
|
44
44
|
| `thatch-review-economy` | Design simplicity and maintainability: is the complexity earned? Evaluates both the overall change design (forest) and individual touch points (trees) for unnecessary complexity, redundancy, and simpler available alternatives. |
|
|
45
45
|
| `thatch-review-no-slop` | AI writing anti-patterns: change narration, fourth wall breaks, em dashes, hedging, filler. |
|
|
46
|
+
| `thatch-review-clarity` | Prose clarity in comments, docstrings, error strings, and docs: made-up phrases, buzzwords, untranslated jargon, clause chains, buried leads, doc/code responsibility splits, checked against the project's clear-writing rules regardless of who wrote the text. |
|
|
46
47
|
| `thatch-review-breadcrumbs` | Comment narrative: do comments form a coherent outline of the code's behavior? |
|
|
47
48
|
| `thatch-review-mark-and-sweep` | Mechanical change completeness: whole-repo sweep for stragglers after renames, flag removals, API substitutions. |
|
|
48
49
|
| `thatch-review-highlights` | Positive finding detection: notably clever solutions, cleanup done along the way, documentation that helps. Medium-high bar. |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jeffober/thatch",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8",
|
|
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",
|
package/src/capabilities.ts
CHANGED
|
@@ -77,6 +77,28 @@ export interface HostCapabilities {
|
|
|
77
77
|
sessionGet(id: string): Promise<{ title?: string } | null>;
|
|
78
78
|
/** List sessions in this directory. Null when unavailable (v2 degrade). */
|
|
79
79
|
sessionList(): Promise<{ id: string; parentID?: string; time?: { updated?: number } }[] | null>;
|
|
80
|
+
/**
|
|
81
|
+
* Enumerate every session the host knows (id, title, directory, parent
|
|
82
|
+
* link, project link) plus each project's canonical checkout directory,
|
|
83
|
+
* for the runtime's stranded-session sweep. v1 maps client.session.list();
|
|
84
|
+
* v2 reads opencode.db readonly (its promise domain has no session list
|
|
85
|
+
* and the sweep needs machine-wide rows, not per-directory ones). Null
|
|
86
|
+
* when the host has no enumeration surface or the read fails - the sweep
|
|
87
|
+
* treats null as "nothing known, do nothing".
|
|
88
|
+
*/
|
|
89
|
+
enumerateSessions(): Promise<{
|
|
90
|
+
sessions: { id: string; title: string; directory: string; parentId: string | null; projectId: string | null }[];
|
|
91
|
+
canonicalByProject: Record<string, string>;
|
|
92
|
+
} | null>;
|
|
93
|
+
/**
|
|
94
|
+
* Move a session to another directory. The strand sweep's self-heal for
|
|
95
|
+
* real sessions stranded in a deleted worktree (deletion is reserved for
|
|
96
|
+
* machinery sessions); moving is exactly what opencode's own
|
|
97
|
+
* "session location unavailable" recovery dialog does, minus the
|
|
98
|
+
* directory picking. Optional: the v1 SDK has no move endpoint, so the
|
|
99
|
+
* v1 adapter omits it and the sweep degrades to machinery-only deletion.
|
|
100
|
+
*/
|
|
101
|
+
sessionMove?(id: string, directory: string): Promise<void>;
|
|
80
102
|
/**
|
|
81
103
|
* Fetch a session's message list (wrap-up greenlight check, alert round
|
|
82
104
|
* classification). Null when unavailable. v2 reads them via
|
|
@@ -148,6 +170,20 @@ export function capabilitiesFromClient(client: PluginInput["client"]): HostCapab
|
|
|
148
170
|
const { data } = await client.session.list();
|
|
149
171
|
return data ?? null;
|
|
150
172
|
},
|
|
173
|
+
enumerateSessions: async () => {
|
|
174
|
+
const { data } = await client.session.list();
|
|
175
|
+
if (!data) return null;
|
|
176
|
+
return {
|
|
177
|
+
sessions: data.map((s) => ({
|
|
178
|
+
id: s.id,
|
|
179
|
+
title: s.title ?? "",
|
|
180
|
+
directory: (s as { directory?: string }).directory ?? "",
|
|
181
|
+
parentId: (s as { parentID?: string }).parentID ?? null,
|
|
182
|
+
projectId: null,
|
|
183
|
+
})),
|
|
184
|
+
canonicalByProject: {},
|
|
185
|
+
};
|
|
186
|
+
},
|
|
151
187
|
sessionMessages: async (id) => {
|
|
152
188
|
const { data } = await client.session.messages({ path: { id } });
|
|
153
189
|
// Map the message-level error (if any) to its name - the alert
|
package/src/chat.ts
CHANGED
|
@@ -389,31 +389,41 @@ export function sortChatRoster(rows: ChatSessionRow[]): ChatSessionRow[] {
|
|
|
389
389
|
});
|
|
390
390
|
}
|
|
391
391
|
|
|
392
|
-
/** Splits the roster for the
|
|
393
|
-
* reachable -
|
|
394
|
-
* their next prompt)
|
|
395
|
-
*
|
|
392
|
+
/** Splits the roster for the three-section display: Active (wakeable or
|
|
393
|
+
* reachable - opencode rows with an open tab, plus every MCP row, which
|
|
394
|
+
* read mail at their next prompt), Recent (fresh opencode rows with no
|
|
395
|
+
* open tab: the harness is alive and beating, but a wake may not reach
|
|
396
|
+
* the session until its tab reopens - headless subordinates may still
|
|
397
|
+
* read mail on their own turns), and Stale (opencode rows whose harness
|
|
398
|
+
* has stopped reporting; mail to them waits until they resume).
|
|
399
|
+
* openTabs refines fresh opencode rows into open versus recent; when
|
|
400
|
+
* omitted every fresh opencode row degrades to recent, never a false
|
|
401
|
+
* open (chatLiveness's rule).
|
|
396
402
|
* staleCapMs bounds how old a stale row the roster displays, so
|
|
397
403
|
* long-dead sessions cannot bury the fresh signal; hidden rows come
|
|
398
404
|
* back only as the staleHidden count, so the CLI can say what it left
|
|
399
405
|
* out. null (the default) shows every stale row - the tool surface has
|
|
400
406
|
* no cap, only the CLI applies one. */
|
|
401
|
-
export function splitChatRoster(rows: ChatSessionRow[], now = Date.now(), staleCapMs: number | null = null): { active: ChatSessionRow[]; stale: ChatSessionRow[]; staleHidden: number } {
|
|
407
|
+
export function splitChatRoster(rows: ChatSessionRow[], now = Date.now(), staleCapMs: number | null = null, openTabs?: ReadonlySet<string>): { active: ChatSessionRow[]; recent: ChatSessionRow[]; stale: ChatSessionRow[]; staleHidden: number } {
|
|
402
408
|
const active: ChatSessionRow[] = [];
|
|
409
|
+
const recent: ChatSessionRow[] = [];
|
|
403
410
|
const stale: ChatSessionRow[] = [];
|
|
404
411
|
let staleHidden = 0;
|
|
405
412
|
for (const row of rows) {
|
|
406
|
-
|
|
413
|
+
const verdict = chatLiveness(row, now, openTabs);
|
|
414
|
+
if (verdict === "stale") {
|
|
407
415
|
if (staleCapMs !== null && now - Date.parse(row.last_seen) > staleCapMs) {
|
|
408
416
|
staleHidden++;
|
|
409
417
|
} else {
|
|
410
418
|
stale.push(row);
|
|
411
419
|
}
|
|
420
|
+
} else if (verdict === "recent") {
|
|
421
|
+
recent.push(row);
|
|
412
422
|
} else {
|
|
413
423
|
active.push(row);
|
|
414
424
|
}
|
|
415
425
|
}
|
|
416
|
-
return { active, stale, staleHidden };
|
|
426
|
+
return { active, recent, stale, staleHidden };
|
|
417
427
|
}
|
|
418
428
|
|
|
419
429
|
/**
|
package/src/commands.ts
CHANGED
|
@@ -192,7 +192,7 @@ ${reviewPipelineCouching}
|
|
|
192
192
|
|
|
193
193
|
## Self review mode rules
|
|
194
194
|
|
|
195
|
-
- The reputation lenses weigh heaviest. NoSlop, Technique, and Breadcrumbs findings outrank same-severity findings from the other lenses. AI-writing tells,
|
|
195
|
+
- The reputation lenses weigh heaviest. NoSlop, Clarity, Technique, and Breadcrumbs findings outrank same-severity findings from the other lenses. AI-writing tells, unclear prose, and a missing narrative are exactly what this review exists to catch before anyone else sees them.
|
|
196
196
|
- Overweight simplicity on shared projects. Prefer findings that make the design easier to understand, easier to test, and more reliable: fewer assumptions, less surface area for bugs and side effects.
|
|
197
197
|
- Technique and highlights findings are decisions, not footnotes. Each one that would produce a simpler, clearer, or more reliable design is surfaced for an adopt-or-dismiss decision before submission. Work through them with the user one at a time; do not leave them as informational asides.
|
|
198
198
|
- Iterate rounds. After each round, work the findings with the user: fix each, or explicitly decline it with a reason. Then run another full round on the updated state. A round is meaningful when it produces a MEDIUM or higher finding, or a technique or slop finding worth adopting. A round producing only LOW mechanical findings gets them fixed but does not trigger another round. Stop at the first round with no meaningful findings, and never run more than three rounds.`,
|
package/src/opencode/v2.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type { ToolContext as V2ToolContext } from "@opencode/plugin/promise/tool
|
|
|
5
5
|
import type { ThatchDB } from "../db";
|
|
6
6
|
import type { HostCapabilities, PromptPart, ToastInput } from "../capabilities";
|
|
7
7
|
import { createRuntime } from "../runtime";
|
|
8
|
+
import { enumerateSweepSessions } from "../session-db";
|
|
8
9
|
import { wrapUpCommandContent, wrapUpCommandDescription } from "../commands";
|
|
9
10
|
import { deriveTitle } from "../extraction";
|
|
10
11
|
import { detectRepo, detectWorktreeKind } from "../git";
|
|
@@ -929,6 +930,20 @@ function buildCapabilities(
|
|
|
929
930
|
return (await session.get({ sessionID: id })) ?? null;
|
|
930
931
|
},
|
|
931
932
|
sessionList: async () => null,
|
|
933
|
+
enumerateSessions: async () => {
|
|
934
|
+
// The promise domain has no session list, and the strand sweep needs
|
|
935
|
+
// machine-wide rows (dead-worktree sessions belong to no live
|
|
936
|
+
// instance), so this reads opencode.db directly - the same readonly
|
|
937
|
+
// pattern the -c inheritance resolution already uses. Null on any
|
|
938
|
+
// schema drift; the sweep treats that as "nothing known".
|
|
939
|
+
return enumerateSweepSessions();
|
|
940
|
+
},
|
|
941
|
+
sessionMove: async (id, directory) => {
|
|
942
|
+
// The pinned dev types carry move on the SessionDomain pick (the
|
|
943
|
+
// session_tab spawn flow uses it), so this is a typed call, unlike
|
|
944
|
+
// remove/compact above.
|
|
945
|
+
await session.move({ sessionID: id, directory });
|
|
946
|
+
},
|
|
932
947
|
sessionMessages: async (id) => {
|
|
933
948
|
// The pinned promise domain has no message-list endpoint (no `message`
|
|
934
949
|
// accessor; compact/remove exist on newer SDK builds but not here).
|
package/src/prompts.ts
CHANGED
|
@@ -19,6 +19,7 @@ export const REVIEW_SPECIALISTS: readonly string[] = [
|
|
|
19
19
|
"thatch-review-state-flow",
|
|
20
20
|
"thatch-review-economy",
|
|
21
21
|
"thatch-review-no-slop",
|
|
22
|
+
"thatch-review-clarity",
|
|
22
23
|
"thatch-review-breadcrumbs",
|
|
23
24
|
"thatch-review-mark-and-sweep",
|
|
24
25
|
"thatch-review-highlights",
|