@jeffober/thatch 0.1.21 → 0.1.24

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.
@@ -54,7 +54,7 @@ bin/thatch → CLI: stores|list|show|forget|search|mcp|reminder|hygi
54
54
  | `tool-defs.ts` | **Single source of truth** for all tools. Each tool has a name, description, zod schema (args), and execute function. Framework-agnostic — neither opencode nor MCP specific. |
55
55
  | `tools.ts` | Thin opencode wrappers. Imports tool-defs, wraps each in opencode's `tool()` with a `thatch_` prefix. |
56
56
  | `mcp.ts` | Stdio JSON-RPC 2.0 server. Compiles zod schemas to JSON Schema via `z.toJSONSchema()` for `tools/list`. Validates args via `z.object().parse()` in `tools/call`. All logging to stderr (stdout is the transport). |
57
- | `index.ts` | OpenCode plugin entry. Wires DB, model, extraction; registers tools and hooks; installs skills. |
57
+ | `index.ts` | OpenCode plugin entry. Wires DB, model, extraction; registers tools and hooks; installs skills. Internal state beyond the extraction pipeline: `extracting` set (parent IDs with an active direct-extraction child), `childMetrics` map (new/updated/deleted counts per child session), and `triggerExtraction(parentID)` (creates a child session via the SDK client and prompts it with the extraction payload). |
58
58
  | `setup.ts` | `thatch setup --claude` installer. Writes .mcp.json, appends to CLAUDE.md (idempotent), installs hooks in settings.json, installs skills. |
59
59
  | `hygiene.ts` | Hygiene report: pending dedup pairs, stale count, orphaned branch memories. Shared by the plugin's session-start hook and the CLI's `thatch reminder` command. |
60
60
  | `git.ts` | Parse `owner/repo` from git remote. Worktree-safe fallback chain. |
@@ -64,7 +64,7 @@ bin/thatch → CLI: stores|list|show|forget|search|mcp|reminder|hygi
64
64
  | `extract-queue.ts` | File-backed per-session JSONL queue (caps 20, oldest dropped). Shared by the Claude Code and Cursor hook paths, which fire one-shot per event with no cross-call state. This is the MCP-side equivalent of `extraction.ts`. |
65
65
  | `sideband.ts` | Unix domain socket server + client. The MCP server (long-lived, warm model) runs `SidebandServer` so one-shot hook processes can ask it to embed a prompt and search for matches without loading the model themselves. Handles two methods: `match` (recall nudge) and `predictions` (prediction auto-fire). Socket path is a hash of the DB path — both processes compute it independently. |
66
66
  | `prompts.ts` | Text constants: opencode system prompt, compaction context, session-start reminder, prompt-aware recall nudge (`recallNudge` / `claudeRecallNudge`), Claude Code CLAUDE.md instructions, Cursor AGENTS.md instructions, Claude Code hook text. |
67
- | `skills.ts` | `SKILL.md` content for all thatch skills, plus the installer. Skills are split into `SHARED_SKILLS` (18 skills: fact-extractor, dedup-classifier, project-primer, 6 review specialists, review synthesizer, review context, workflow research, change walkthrough, code walkthrough, session reflection, pr-description, ticket-description, split-overlarge-pr — work on both opencode and Claude Code) and `OPENCODE_ONLY_SKILLS` (1 skill: code-review coordinator — requires sub-agent support, not installed for Claude Code). `installSkills(dir, skills)` defaults to `SHARED_SKILLS`; the opencode plugin passes `[...SHARED_SKILLS, ...OPENCODE_ONLY_SKILLS]`. |
67
+ | `skills.ts` | `SKILL.md` content for all thatch skills, plus the installer. Skills are split into `SHARED_SKILLS` (19 skills: fact-extractor, dedup-classifier, project-primer, 7 review specialists, review synthesizer, review context, workflow research, change walkthrough, code walkthrough, session reflection, pr-description, ticket-description, split-overlarge-pr — work on both opencode and Claude Code) and `OPENCODE_ONLY_SKILLS` (1 skill: code-review coordinator — requires sub-agent support, not installed for Claude Code). `installSkills(dir, skills)` defaults to `SHARED_SKILLS`; the opencode plugin passes `[...SHARED_SKILLS, ...OPENCODE_ONLY_SKILLS]`. |
68
68
 
69
69
  ## Plugin hooks
70
70
 
@@ -75,9 +75,9 @@ bin/thatch → CLI: stores|list|show|forget|search|mcp|reminder|hygi
75
75
  | `experimental.chat.system.transform` | Appends the thatch system prompt (store names, usage rules). |
76
76
  | `experimental.session.compacting` | Marks the session as compacting and appends re-familiarization context so a compacted session still knows thatch exists. |
77
77
  | `experimental.compaction.autocontinue` | Clears the compacting flag so `chat.message` nudges resume. Without this, nudges that instruct tool calls would fire during summary generation where tools are blocked. The `chat.message` hook also clears the flag if it fires for a session still in the compacting set but the incoming message has no compaction-type part — this handles compaction failure, where the session would otherwise be stuck with nudges off forever. |
78
- | `tool.execute.after` | Buffers every non-`thatch_*`, non-`skill`, non-`task` tool call into the session's extraction buffer. (Skill/task are excluded — buffering them creates a feedback loop where the nudge triggers a skill load, which gets buffered, which triggers another nudge.) Memory writes (`thatch_memory_remember`) and `thatch_extraction_done` drain the buffer and reset the missed-nudge counter. This is a plugin hook, NOT a bus event — do not move it into the `event` handler; the event bus has no such event and it will silently never fire. |
79
- | `chat.message` | Two priority tiers: (a) if extraction buffer has interactions, **peeks** the buffer (does NOT drain it) and injects a synthetic text part carrying the extraction nudge + JSON payload — the buffer persists until the agent writes a memory or calls `thatch_extraction_done`, so ignored nudges repeat and escalate (polite → insistent → ALL-CAPS) via the `missedNudges` counter; (b) otherwise, embeds the user's prompt text with the in-process warm model, searches `db.search()` across repo + global, and pushes a recall nudge if matches exceed the threshold (default 0.55). The same embedding also feeds the prediction auto-fire: `db.scorePredictionNudge` scores the prompt against prediction matchers and injects a separate `[thatch] User decision model` nudge if any matchers clear the 0.45 threshold. Recall and prediction nudges fire independently (separate try/catch blocks, separate synthetic parts). Skipped entirely while the session is compacting (tool calls are blocked during summary generation). |
80
- | `event` (`session.created`) | Sends the session-start reminder via `client.session.prompt`, carrying the hygiene heartbeat (pending dedup pairs, stale count, orphaned branch memories) when any signal is non-zero. The session id is at `event.properties.info.id`. |
78
+ | `tool.execute.after` | Buffers every non-`thatch_*`, non-`skill`, non-`task` tool call into the session's extraction buffer. (Skill/task are excluded — buffering them creates a feedback loop where the nudge triggers a skill load, which gets buffered, which triggers another nudge.) Memory writes (`thatch_memory_remember`) and `thatch_extraction_done` drain the buffer and reset the missed-nudge counter. For child sessions (`childToParent.has(sessionID)`), also tracks metrics: `remember` with `overwrite:false` → new++, `overwrite:true` → updated++, `forget` → deleted++. This is a plugin hook, NOT a bus event — do not move it into the `event` handler; the event bus has no such event and it will silently never fire. |
79
+ | `chat.message` | Two priority tiers: (a) if extraction buffer has interactions and the session is NOT in the `extracting` set (direct extraction in progress), **peeks** the buffer (does NOT drain it) and injects a synthetic text part carrying the extraction nudge + JSON payload — the buffer persists until the agent writes a memory or calls `thatch_extraction_done`, so ignored nudges repeat and escalate (polite → insistent → ALL-CAPS) via the `missedNudges` counter; (b) otherwise, embeds the user's prompt text with the in-process warm model, searches `db.search()` across repo + global, and pushes a recall nudge if matches exceed the threshold (default 0.55). The same embedding also feeds the prediction auto-fire: `db.scorePredictionNudge` scores the prompt against prediction matchers and injects a separate `[thatch] User decision model` nudge if any matchers clear the 0.45 threshold. Recall and prediction nudges fire independently (separate try/catch blocks, separate synthetic parts). Skipped entirely while the session is compacting (tool calls are blocked during summary generation). |
80
+ | `event` | Subscribes to all session bus events. `session.created`: records `childToParent` + `parentSnapshots` (shallow copy of the parent's buffer for snapshot-aware drain), then sends the session-start reminder via `client.session.prompt` carrying the hygiene heartbeat (pending dedup pairs, stale count, orphaned branch memories) when any signal is non-zero. `session.status` (idle): if the session is a child, drains its snapshot, fires a toast with `childMetrics`, and deletes the child session; if the session is a parent with pending tool interactions and not already extracting, calls `triggerExtraction` to create a direct-extraction child. `session.error`: requeues the parent's buffer (child died without draining). `session.deleted`: cleans up `childToParent`, `parentSnapshots`, `childMetrics`, and `extracting`. `session.compacted`: clears the compacting flag so `chat.message` nudges resume. |
81
81
  | `dispose` | Closes the DB. |
82
82
 
83
83
  Hook failures are logged with a `[thatch]` prefix — never swallowed silently.
@@ -168,16 +168,45 @@ dedup cycle (agent-driven)
168
168
  → thatch_dedup_mark_checked records verdicts for surviving pairs
169
169
  (overwriting or forgetting an entry clears its verdicts → can re-flag)
170
170
 
171
- extraction cycle (agent-driven)
171
+ extraction cycle
172
172
  → tool.execute.after buffers non-thatch, non-skill, non-task tool calls
173
- per session (max 20)
174
- → next chat.message peeks the buffer (does NOT drain it) and injects a
175
- nudge part with JSON payload; missed nudges escalate (polite → insistent
176
- → ALL-CAPS) via the missedNudges counter
177
- → drain: thatch_memory_remember in the session (or a child sub-agent via
178
- the childToParent Map) OR thatch_extraction_done — both clear the buffer
179
- and reset the missed-nudge counter
180
- → agent loads thatch-fact-extractor skill, saves facts via thatch_memory_remember
173
+ per session (max 20); for child sessions, also tracks new/updated/deleted
174
+ metrics
175
+ → direct extraction (primary path, opencode-only):
176
+ parent goes idle (session.status idle) with pending tool interactions
177
+ → triggerExtraction adds parentID to the `extracting` set (suppresses
178
+ nudge in chat.message), creates a child session via
179
+ client.session.create, and prompts it with the extraction payload
180
+ → child runs the fact-extractor skill, writes memories via
181
+ thatch_memory_remember → consumeSnapshot drains the parent's buffer
182
+ (snapshot-aware: removes only entries captured at dispatch time by
183
+ reference identity, preserving interleaved-turn entries)
184
+ → child goes idle → event handler drains remaining snapshot entries,
185
+ fires a toast with childMetrics, deletes the child session
186
+ → nudge path (fallback): if triggerExtraction throws (create or prompt
187
+ fails), the `extracting` set clears and the next chat.message sees
188
+ pending entries with no extracting flag → peeks the buffer and injects
189
+ a nudge part with JSON payload; missed nudges escalate (polite →
190
+ insistent → ALL-CAPS) via the missedNudges counter
191
+ → drain: thatch_memory_remember (or thatch_extraction_done) clears the
192
+ buffer and resets the missed-nudge counter
193
+ → agent loads thatch-fact-extractor skill, saves facts via
194
+ thatch_memory_remember
195
+ → MCP path (Claude Code/Cursor): unchanged — no SDK client, no child
196
+ sessions; extract-queue.ts + flush-tools drives the nudge via hooks
197
+
198
+ toast notifications (opencode-only, TUI-rendered)
199
+ → client.tui.showToast — best-effort, silently ignored if TUI not
200
+ connected (headless mode); model-invisible (goes to the user only,
201
+ not the conversation history — the inverse of synthetic nudge parts,
202
+ which are model-visible but TUI-hidden)
203
+ → extraction metrics: `[thatch] new: N, updated: M, deleted: K` (success
204
+ variant, 4s) — fires when an extraction child goes idle; no-save runs
205
+ show `[thatch] extraction complete — nothing to save` (info variant)
206
+ → recall matches: `[thatch] recalled N memories` (info variant, 3s) —
207
+ fires when chat.message matches stored memories
208
+ → prediction matches: `[thatch] N predictions surfaced` (info variant,
209
+ 3s) — fires when chat.message matches decision-model patterns
181
210
 
182
211
  prompt-aware recall nudge (both paths)
183
212
  → opencode: chat.message hook embeds prompt text with warm in-process model,
@@ -65,15 +65,37 @@ here first. These are the things that have already cost time.
65
65
  agent writes a memory or calls `thatch_extraction_done`. Ignored nudges
66
66
  accumulate; the `missedNudges` counter escalates the tone (polite at 0-1
67
67
  misses, insistent at 2, ALL-CAPS at 3+). The counter resets when the
68
- buffer drains.
68
+ buffer drains. The primary opencode path doesn't use the nudge at all:
69
+ `triggerExtraction` creates a child session directly, and the `extracting`
70
+ set suppresses the nudge in `chat.message` while a direct-extraction child
71
+ is active. The peek-not-drain semantics still hold for the fallback nudge
72
+ path (MCP hosts and any `triggerExtraction` failure).
69
73
  - **A child sub-agent's `thatch_memory_remember` drains the parent's buffer**
70
74
  via the `childToParent` Map (`index.ts:65` declaration, `index.ts:131`
71
- lookup). Without this, dispatching the fact-extractor as a background task
72
- would write memories in the child but never clear the parent's queue — the
73
- nudge would replay every turn. `thatch_extraction_done` is the
74
- belt-and-suspenders explicit acknowledgment: it drains the buffer without
75
- requiring a memory write (covers cases where the sub-agent errors out or the
76
- host doesn't expose parent-child session relationships).
75
+ lookup). Two paths reach this machinery: (a) the **plugin-initiated child
76
+ session** — `triggerExtraction` calls `client.session.create` with a
77
+ `parentID`, the primary opencode path; (b) the **agent-initiated background
78
+ task** — the model dispatches the fact-extractor via the `task` tool after
79
+ receiving the nudge, the fallback path still used by MCP hosts. Both use
80
+ the same `childToParent`/`parentSnapshots`/`consumeSnapshot` plumbing.
81
+ Without this, either path would write memories in the child but never
82
+ clear the parent's queue — the nudge would replay every turn.
83
+ `thatch_extraction_done` is the belt-and-suspenders explicit
84
+ acknowledgment: it drains the buffer without requiring a memory write
85
+ (covers cases where the sub-agent errors out or the host doesn't expose
86
+ parent-child session relationships).
87
+ - **The no-save drain runs in the child-idle handler regardless of writes.**
88
+ When the extraction child goes idle after a no-save run (nothing worth
89
+ extracting), the parent's snapshot entries must still be drained from the
90
+ buffer. Without this, entries linger in pending and the nudge fires as a
91
+ synthetic (TUI-hidden) part on the next `chat.message`. The child-idle
92
+ handler drains the snapshot only if it still exists (the child wrote no
93
+ memories). If the child did write memories, `tool.execute.after` already
94
+ consumed the snapshot — the idle handler skips the drain so interleaved-turn
95
+ entries survive.
96
+ - **`client.tui.showToast` is best-effort.** The toast call is wrapped in a
97
+ catch-and-ignore — if the TUI is not connected (headless mode), it silently
98
+ does nothing. The toast is TUI-rendered (in-app), not an OS notification.
77
99
 
78
100
  ## Hooks (Claude Code / Cursor)
79
101
 
@@ -22,7 +22,8 @@ This document maps the feature parity and documents the remaining gaps.
22
22
  | **System prompt** (store names, usage rules) | `experimental.chat.system.transform` — dynamic, repo baked in at runtime | CLAUDE.md static text appended by `thatch setup`; repo auto-detected by the MCP server at startup | AGENTS.md static text appended by `thatch setup`; repo auto-detected at startup | **Approximate** — static text persists through compaction, so the agent retains the usage instructions, but the dynamic per-turn refresh is lost |
23
23
  | **Session-start reminder** (recall nudge + hygiene heartbeat) | `session.created` event → `client.session.prompt` injects a synthetic message | `SessionStart` hook → `thatch reminder`; stdout becomes context | `sessionStart` hook → `thatch reminder --json`; output is `additional_context` JSON | **Full** |
24
24
  | **Compaction context** (re-familiarize after compaction) | `experimental.session.compacting` hook appends context to the compaction output | `PostCompact` hook — side-effects only, **cannot inject context** | No equivalent hook | **Gapped** — see below |
25
- | **Extraction nudge** (buffer tool calls, inject JSON payload) | `tool.execute.after` buffers non-thatch, non-skill, non-task tool calls in-process; `chat.message` peeks the buffer (does not drain it) and injects a synthetic text part | `PostToolBatch` → `thatch buffer-batch` (file-backed JSONL queue); `UserPromptSubmit` → `thatch flush-tools` peeks the queue and prints the nudge (does not drain — the queue persists until a memory write or `extraction_done`) | `postToolUse` → `thatch buffer-tool` (single-tool, file-backed queue); `beforeSubmitPrompt` → `thatch flush-tools --json` | **Full** (file-backed) — timing differs: the nudge arrives at the **start** of the next turn in Claude Code/Cursor, not at the end of the current one like opencode's `chat.message` |
25
+ | **Extraction** (buffer tool calls, extract durable facts) | `tool.execute.after` buffers non-thatch, non-skill, non-task tool calls in-process; **direct extraction** is the primary path — when the parent session goes idle with pending buffer entries, the plugin creates a child session via the SDK (`client.session.create` with `parentID`) and prompts it with the extraction payload (nudge suppressed via the `extracting` set); the `chat.message` nudge path is a **fallback** used when direct extraction throws | `PostToolBatch` → `thatch buffer-batch` (file-backed JSONL queue); `UserPromptSubmit` → `thatch flush-tools` peeks the queue and prints the nudge (does not drain — the queue persists until a memory write or `extraction_done`) | `postToolUse` → `thatch buffer-tool` (single-tool, file-backed queue); `beforeSubmitPrompt` → `thatch flush-tools --json` | **Partial** — opencode uses direct extraction (parent idle → child session) as the primary path, with the nudge as fallback; MCP hosts are nudge-only. Both paths produce the same JSON shape via `buildExtractionPayload()`. Nudge timing differs: the nudge arrives at the **start** of the next turn in Claude Code/Cursor; opencode's direct extraction fires at **end of turn** (session idle), and the nudge fallback fires at the start of the next turn like MCP |
26
+ | **Extraction feedback** (toast notifications) | `client.tui.showToast` fires when the extraction child goes idle — shows `[thatch] new: N, updated: M, deleted: K` (success) or `extraction complete — nothing to save` (info); best-effort, silently ignored in headless mode | No TUI connection — MCP hosts have no equivalent | No TUI connection | **Gapped** — MCP hosts cannot show toast notifications |
26
27
  | **Prompt-aware recall nudge** | `chat.message` hook embeds the prompt with the in-process warm model, searches `db.search()`, pushes a nudge part if matches ≥ threshold | `UserPromptSubmit` hook → `thatch flush-tools` connects to the MCP server's sideband socket; the warm server embeds + searches; hook prints the nudge or falls back to the write nudge | `beforeSubmitPrompt` hook → `thatch flush-tools --json` (same sideband path) | **Full** — sideband socket gives cold hook processes access to the warm MCP server's model |
27
28
  | **Prediction auto-fire** (user decision model) | `chat.message` hook scores the prompt embedding against prediction matchers; injects a `[thatch] User decision model` nudge alongside the recall nudge (separate synthetic part, same embedding call) | `UserPromptSubmit` hook → `thatch flush-tools` fires prediction query via the sideband socket's `predictions` method in parallel with the recall nudge | `beforeSubmitPrompt` hook → `thatch flush-tools --json` (same sideband path) | **Full** — same `scorePredictionNudge` entry point in both paths |
28
29
  | **Skills** | Installed to `$XDG_CONFIG_HOME/opencode/skills` at plugin init (shared **+** opencode-only) | Installed to `$CLAUDE_CONFIG_DIR/skills/` by `thatch setup` (shared only) | Installed to `~/.cursor/skills/` by `thatch setup` (shared only) | **Full** — same SKILL.md format; the code-review coordinator is opencode-only (needs sub-agents) |
@@ -61,18 +62,37 @@ through compaction, so the agent retains the usage instructions. The
61
62
  compaction in Claude Code, so the recall reminder runs again. There is no
62
63
  explicit "re-familiarization" nudge for hosts without a compaction hook.
63
64
 
64
- ### Extraction nudge timing
65
+ ### Extraction timing
65
66
 
66
- OpenCode's extraction pipeline works by:
67
+ OpenCode's extraction pipeline has two paths — direct extraction (primary)
68
+ and the nudge (fallback):
69
+
70
+ **Direct extraction (primary path):**
67
71
 
68
72
  1. `tool.execute.after` buffers every non-thatch, non-skill, non-task tool
69
73
  call into a per-session in-memory array (max 20 interactions).
70
- 2. On the next `chat.message`, the buffer is **peeked** (not drained) and a
71
- synthetic text part carrying the JSON payload is injected into the
72
- conversation. The buffer persists until the agent writes a memory or calls
73
- `thatch_extraction_done`, so ignored nudges repeat and escalate (polite →
74
- insistent → ALL-CAPS) via the `missedNudges` counter.
75
- 3. The agent sees the nudge, loads the `thatch-fact-extractor` skill, and saves
74
+ 2. When the parent session goes idle (`session.status` idle event) with pending
75
+ buffer entries, the plugin calls `triggerExtraction`: creates a child session
76
+ via `client.session.create` (with `parentID`), then prompts it via
77
+ `client.session.promptAsync` (or `prompt`) with the extraction payload. The
78
+ `extracting` set suppresses the nudge in `chat.message` while the child runs.
79
+ 3. The child session runs the `thatch-fact-extractor` skill, writes memories via
80
+ `thatch_memory_remember`. A memory write in the child drains the parent's
81
+ buffer snapshot (via the `childToParent` Map and `consumeSnapshot`) and
82
+ resets the missed-nudge counter.
83
+ 4. When the child goes idle, the handler drains any remaining snapshot entries
84
+ (covers no-save runs), deletes the child session, and fires a toast via
85
+ `client.tui.showToast`.
86
+
87
+ **Nudge (fallback path):**
88
+
89
+ 1. If `triggerExtraction` throws (session creation or prompt fails), the catch
90
+ block clears the `extracting` flag. On the next `chat.message`, the buffer is
91
+ **peeked** (not drained) and a synthetic text part carrying the JSON payload
92
+ is injected into the conversation. The buffer persists until the agent writes
93
+ a memory or calls `thatch_extraction_done`, so ignored nudges repeat and
94
+ escalate (polite → insistent → ALL-CAPS) via the `missedNudges` counter.
95
+ 2. The agent sees the nudge, loads the `thatch-fact-extractor` skill, and saves
76
96
  durable facts via `thatch_memory_remember`. A memory write in the session
77
97
  (or a child sub-agent via the `childToParent` Map) or `thatch_extraction_done`
78
98
  drains the buffer and resets the missed-nudge counter.
@@ -19,14 +19,25 @@ The plugin entry (`src/index.ts`) registers these hooks in code:
19
19
  | `experimental.chat.system.transform` | `{}` | `{ system: string[] }` | Pushes the system prompt (store names, usage rules). |
20
20
  | `experimental.session.compacting` | `{ sessionID }` | `{ context: string[] }` | Marks the session as compacting, pushes re-familiarization context. |
21
21
  | `experimental.compaction.autocontinue` | `{ sessionID }` | `{ enabled: boolean }` | Clears the compacting flag so `chat.message` nudges resume post-compaction. |
22
- | `tool.execute.after` | `{ tool, sessionID, callID, args }` | `{ title, output, metadata }` | Buffers non-`thatch_`, non-`skill`, non-`task` tool calls into the in-memory extraction ring buffer (max 20). |
23
- | `chat.message` | `{ sessionID, messageID }` | `{ message, parts }` | Two tiers: extraction nudge if pending, else prompt-aware recall nudge. Skipped while the session is compacting. |
24
- | `event` (`session.created`) | `{ event: { type, properties: { info: { id } } } }` | — | Calls `client.session.prompt` with the reminder + hygiene heartbeat. |
22
+ | `tool.execute.after` | `{ tool, sessionID, callID, args }` | `{ title, output, metadata }` | Buffers non-`thatch_`, non-`skill`, non-`task` tool calls into the in-memory extraction ring buffer (max 20). In child sessions, also tracks new/updated/deleted counts via `childMetrics` for the extraction toast. |
23
+ | `chat.message` | `{ sessionID, messageID }` | `{ message, parts }` | Two tiers: extraction nudge if pending and not already extracting (fallback — direct extraction via the `event` hook is the primary path), else prompt-aware recall nudge. Suppressed while compacting or while `extracting` is set. |
24
+ | `event` | `{ event: { type, properties } }` | — | Dispatches on `event.type`: `session.created` (top-level) → `client.session.prompt` with reminder + hygiene heartbeat; `session.created` (child with `parentID`) → records `childToParent` + snapshots parent's pending buffer. `session.status` idle (parent, pending interactions) → `triggerExtraction` (direct child-session extraction); idle (child) → complete/drain, `showToast` with metrics, delete child. `session.error` (child) → requeue parent's accepted entries. `session.deleted` → requeue if child, drop accepted if parent, clean maps. `session.compacted` → clear compacting flag. |
25
25
  | `dispose` | — | — | Closes the DB. |
26
26
 
27
27
  Skills install to `$XDG_CONFIG_HOME/opencode/skills` at plugin init — shared
28
28
  **and** opencode-only (the coordinator needs sub-agents).
29
29
 
30
+ Direct extraction is the primary path for opencode: when a parent session goes
31
+ idle with pending tool interactions, the `event` hook creates a child session
32
+ and prompts it directly via `triggerExtraction`. The `chat.message` extraction
33
+ nudge is a fallback — it fires only if direct extraction was never triggered or
34
+ threw an error (the `extracting` set is cleared on failure).
35
+
36
+ `client.tui.showToast` is called from the `event` hook (on child idle, with
37
+ extraction metrics: new/updated/deleted counts) and from `chat.message` (on
38
+ recall and prediction matches). Best-effort — silently ignored if the TUI is
39
+ not connected (headless mode).
40
+
30
41
  ## Claude Code (MCP server + hooks)
31
42
 
32
43
  `thatch setup --claude` (project-local) or `thatch setup --claude --global`.
@@ -21,9 +21,9 @@ description: Extract durable project facts ... Use when ...
21
21
  - `description` drives when the agent loads the skill (both opencode and
22
22
  Cursor auto-discover skills and use the description for relevance).
23
23
 
24
- ## The 19 skills
24
+ ## The 20 skills
25
25
 
26
- **Shared (18)** — installed everywhere; no sub-agents required:
26
+ **Shared (19)** — installed everywhere; no sub-agents required:
27
27
 
28
28
  | Skill | Role |
29
29
  |-------|------|
@@ -36,6 +36,7 @@ description: Extract durable project facts ... Use when ...
36
36
  | `thatch-review-no-slop` | AI writing anti-patterns: change narration, fourth-wall breaks, em dashes, filler. |
37
37
  | `thatch-review-breadcrumbs` | Comment narrative: do comments form a coherent outline of behavior? |
38
38
  | `thatch-review-mark-and-sweep` | Mechanical change completeness: whole-repo sweep for stragglers after renames, flag removals, API substitutions. |
39
+ | `thatch-review-highlights` | Positive finding detection: notably clever solutions, cleanup done along the way, documentation that helps. Medium-high bar against generic praise. |
39
40
  | `thatch-review-synthesizer` | Verify specialist findings against code, dedupe, classify, calibrate severity. Starts the final report with a workflow-change preface, then cross-references findings against prior review comments when a follow-up round register is provided. |
40
41
  | `thatch-review-context` | Gather project/feature context (PR descriptions, git archaeology, ticket references, memory) before fan-out. Prevents false positives about intentionally deferred work. Also fetches prior review comments on a connected PR/MR for follow-up-round detection and builds a register with preliminary addressed-check status per comment. |
41
42
  | `thatch-workflow-research` | Research code workflows/features affected by a change or planned change. Reads code flows, comments, git history, memories, docs. Produces a guide to the code for reviewers or planners. |
@@ -50,11 +51,11 @@ description: Extract durable project facts ... Use when ...
50
51
 
51
52
  | Skill | Role |
52
53
  |-------|------|
53
- | `thatch-code-review` | Resolve review target (incl. VCS detection and connected PR/MR lookup for follow-up round detection), gather project context, research affected workflows, estimate complexity, partition, dispatch the 6 specialists in parallel, synthesize with a workflow-change preface and prior-comment cross-reference. |
54
+ | `thatch-code-review` | Resolve review target (incl. VCS detection and connected PR/MR lookup for follow-up round detection), gather project context, research affected workflows, estimate complexity, partition, dispatch the 7 specialists in parallel, synthesize with a workflow-change preface and prior-comment cross-reference. |
54
55
 
55
56
  ## REVIEW_COMMON
56
57
 
57
- The six review specialists share a framework interpolated via `${REVIEW_COMMON}`:
58
+ The six problem-finding review specialists share a framework interpolated via `${REVIEW_COMMON}` (the seventh specialist, highlights, finds positive things and uses its own structure):
58
59
 
59
60
  - **Static analysis only** — no running tests/linters/compilers.
60
61
  - **Scope gathering** — resolve the git range, `git diff --stat`, read changed
@@ -84,7 +85,7 @@ The synthesizer reuses the same verification rigor but has its own structure
84
85
  ## The two arrays
85
86
 
86
87
  ```ts
87
- const SHARED_SKILLS: SkillDef[] = [ /* 18 skills above */ ];
88
+ const SHARED_SKILLS: SkillDef[] = [ /* 19 skills above */ ];
88
89
  const OPENCODE_ONLY_SKILLS: SkillDef[] = [ /* code-review coordinator */ ];
89
90
  ```
90
91
 
@@ -107,7 +108,7 @@ and `--cursor` pass only the shared set.
107
108
 
108
109
  ## Adding a skill
109
110
 
110
- 1. Write `artifacts/skills/thatch-<name>.md` with YAML frontmatter. If it's a review specialist, include `${REVIEW_COMMON}` (interpolated from `artifacts/skills/common.md`).
111
+ 1. Write `artifacts/skills/thatch-<name>.md` with YAML frontmatter. If it's a problem-finding review specialist, include `${REVIEW_COMMON}` (interpolated from `artifacts/skills/common.md`). The highlights specialist does not use REVIEW_COMMON — it finds positive things, not problems, so the problem-finding framework does not apply.
111
112
  2. Add the skill name to the `names` array in `loadSharedSkills()` (or `loadOpencodeOnlySkills()` if sub-agents are needed). The loader in `src/skills.ts` reads `.md` files at init.
112
113
  3. If the skill name appears in a tool's workflow (e.g. `thatch-fact-extractor`), update `src/prompts.ts`.
113
114
  4. Run `mise run check` — tests verify counts; `installSkills` picks up new files on next init.
@@ -115,7 +116,7 @@ and `--cursor` pass only the shared set.
115
116
  ## Memory review skills in practice
116
117
 
117
118
  - **Quick single lens**: load any specialist directly and point it at a branch.
118
- - **Full review on opencode**: load `thatch-code-review` — it gathers project context, researches affected workflows, dispatches all 6 specialists in parallel (with both the context brief and workflow guide injected into each briefing), then synthesizes a report that starts with the workflow changes before findings.
119
+ - **Full review on opencode**: load `thatch-code-review` — it gathers project context, researches affected workflows, dispatches all 7 specialists in parallel (with both the context brief and workflow guide injected into each briefing), then synthesizes a report that starts with the workflow changes before findings.
119
120
  - **Full review on Claude Code/Cursor**: run each specialist in sequence, then
120
121
  run `thatch-review-synthesizer` to verify and aggregate.
121
122
 
@@ -22,13 +22,13 @@ file/IPC/CLI contracts ready to graduate into `bun:test` integration tests.
22
22
  | [UC-011](UC-011-write-time-similarity-warning.md) | Write-time warning never blocks; merge or mark-distinct reconciliation |
23
23
  | [UC-012](UC-012-model-migration.md) | `THATCH_MODEL` change: old memories skipped (not corrupted) until re-saved |
24
24
  | [UC-013](UC-013-compaction-context.md) | Compaction context re-familiarization (opencode-only) |
25
- | [UC-014](UC-014-skill-install-drift.md) | Skill install: 18 shared vs 19 opencode; drift overwritten on re-setup |
25
+ | [UC-014](UC-014-skill-install-drift.md) | Skill install: 19 shared vs 20 opencode; drift overwritten on re-setup |
26
26
  | [UC-015](UC-015-env-override-matrix.md) | `THATCH_DB_PATH`, `XDG_CONFIG_HOME`, `CLAUDE_CONFIG_DIR`, `THATCH_QUEUE_DIR`, `THATCH_RECALL_THRESHOLD`, `THATCH_MODEL` |
27
27
  | [UC-016](UC-016-concurrent-session-isolation.md) | Per-session extraction isolation (in-memory + file queue); shared stateless sideband |
28
28
  | [UC-017](UC-017-buffer-tool-vs-buffer-batch.md) | `buffer-batch` (Claude, `tool_calls[]`+`session_id`) vs `buffer-tool` (Cursor, single+`conversation_id`) |
29
29
  | [UC-018](UC-018-mcp-startup-setup-detection.md) | MCP startup detects missing/broken setup instructions and warns the agent |
30
30
  | [UC-019](UC-019-archived-memory-lifecycle.md) | Archive, search exclusion, update guard, unarchive |
31
- | [UC-020](UC-020-extraction-escalation.md) | Nudge escalation (polite → insistent → ALL-CAPS), buffer persistence, `extraction_done` drain |
31
+ | [UC-020](UC-020-extraction-escalation.md) | Nudge escalation (polite → insistent → ALL-CAPS), buffer persistence, `extraction_done` accept/complete |
32
32
  | [UC-021](UC-021-prediction-autofire.md) | Prediction auto-fire: matcher scores prompt, prediction nudge injected alongside recall |
33
33
  | [UC-022](UC-022-prediction-dedup.md) | Prediction create-on-existing: dedup finds match, links edge without disconfirming |
34
34
  | [UC-023](UC-023-prediction-confidence-model.md) | Bayesian confidence math: create, confirm, soft (weak disconfirm), disconfirm signals |
@@ -9,24 +9,46 @@ client and a synthetic tool call sequence._
9
9
  **Steps**
10
10
  1. Do some real work: have the agent read files, run commands, edit code —
11
11
  any non-`thatch_*`, non-`skill`, non-`task` tool activity.
12
- 2. Send your next message (anything).
12
+ 2. The agent's turn ends and the session goes idle.
13
13
 
14
- **Expected**
15
- - The agent's context for that message includes a `[thatch]` nudge carrying a
16
- JSON payload summarizing the buffered tool interactions (tool name, title,
17
- truncated args/output).
18
- - The agent loads `thatch-fact-extractor` and saves durable facts via
14
+ **Expected (opencode — direct extraction, primary path)**
15
+ - The plugin's `event` hook catches `session.status` idle with pending tool
16
+ interactions and calls `triggerExtraction`: creates a child session via
17
+ `client.session.create({ parentID })` and prompts it via
18
+ `client.session.promptAsync` (or `client.session.prompt` fire-and-forget
19
+ if the env var is unset).
20
+ - The `extracting` set suppresses the nudge in `chat.message` while the
21
+ child runs — no nudge text appears in the user's conversation.
22
+ - The child loads `thatch-fact-extractor` and saves durable facts via
19
23
  `thatch_memory_remember` — or saves nothing if the activity was routine.
20
- - The buffer is **not** drained on nudge delivery — it persists until the agent
21
- writes a memory or calls `thatch_extraction_done`. If the nudge is ignored,
22
- the next message carries a repeat nudge, escalating in urgency:
24
+ - A `thatch_memory_remember` call in the child drains the parent's snapshot
25
+ entries from the buffer (via `consumeSnapshot` with `parentSnapshots`).
26
+ - When the child goes idle, the parent's snapshot entries are drained
27
+ (covering no-save runs where no memory was written), the child session is
28
+ deleted, and a toast notification fires with the extraction metrics
29
+ (`[thatch] new: N, updated: M, deleted: K`, or
30
+ `[thatch] extraction complete — nothing to save`).
31
+ - If `triggerExtraction` throws, the `extracting` set is cleared and the
32
+ nudge fires as a fallback on the next `chat.message` (see below).
33
+
34
+ **Expected (fallback nudge path — opencode when direct extraction fails,
35
+ and all MCP hosts)**
36
+ - The agent's context for the next message includes a `[thatch]` nudge
37
+ carrying a JSON payload summarizing the buffered tool interactions (tool
38
+ name, title, truncated args/output).
39
+ - The buffer is **not** drained on nudge delivery — it persists until the
40
+ agent writes a memory or accepts it by calling `thatch_extraction_done`.
41
+ Accepting quiets the nudge while holding the entries until the extractor
42
+ completes; a child extractor that errors or is deleted requeues them. If
43
+ the nudge is ignored, the next message carries a repeat nudge, escalating
44
+ in urgency:
23
45
  - 1st-2nd miss: polite tone
24
46
  - 3rd consecutive miss (missedCount=2): insistent (directive) tone
25
47
  - 4th+ consecutive miss (missedCount>=3): ALL-CAPS tone
26
- The counter resets when the buffer drains (memory write or
27
- `thatch_extraction_done`).
28
- - A `thatch_memory_remember` call in a child sub-agent session also drains the
29
- parent's buffer (via the `childToParent` Map), so dispatching the
48
+ The counter resets when the buffer drains (memory write) or is accepted
49
+ (`thatch_extraction_done`).
50
+ - A `thatch_memory_remember` call in a child sub-agent session also drains
51
+ the parent's buffer (via the `childToParent` Map), so dispatching the
30
52
  fact-extractor as a background task clears the parent's queue.
31
53
  - Two concurrent sessions never see each other's interactions in a nudge.
32
54
  - The agent's own `thatch_*` tool calls never appear in a payload (no
@@ -17,12 +17,12 @@
17
17
  appends a thatch block to `CLAUDE.md` bracketed by start/end markers (replaced, not
18
18
  duplicated, on re-run), writes `.claude/settings.json` hooks (`SessionStart` -> `thatch reminder`,
19
19
  `PostToolBatch` -> `thatch buffer-batch`, `UserPromptSubmit` -> `thatch flush-tools`), and installs
20
- **18 shared skills** (no code-review coordinator) to `$CLAUDE_CONFIG_DIR/skills/` — user-scoped
20
+ **19 shared skills** (no code-review coordinator) to `$CLAUDE_CONFIG_DIR/skills/` — user-scoped
21
21
  even in project-local mode.
22
22
  - `--cursor` project-local writes `.cursor/mcp.json`, appends to `AGENTS.md`, writes
23
23
  `.cursor/hooks.json` in the **flat format** (`{version:1, hooks:{...}}`): `sessionStart` ->
24
24
  `thatch reminder --json`, `postToolUse` -> `thatch buffer-tool`, `beforeSubmitPrompt` ->
25
- `thatch flush-tools --json`; and installs 18 shared skills to `~/.cursor/skills/`.
25
+ `thatch flush-tools --json`; and installs 19 shared skills to `~/.cursor/skills/`.
26
26
  - Re-run is idempotent: instructions are not duplicated, thatch hooks are replaced (not appended),
27
27
  and non-thatch hooks are preserved. A legacy `thatch echo` hook is replaced with `flush-tools`.
28
28
  - `--global` (Claude) writes `~/.claude/CLAUDE.md` + `~/.claude/settings.json` but **no project
@@ -13,16 +13,16 @@
13
13
  5. Check whether the `thatch-code-review` coordinator skill is present.
14
14
 
15
15
  **Expected**
16
- - Claude Code and Cursor install exactly **18 shared skills** to the skills
16
+ - Claude Code and Cursor install exactly **19 shared skills** to the skills
17
17
  dir — the coordinator (`thatch-code-review`) is **absent** (it needs
18
18
  sub-agents, which those hosts lack).
19
- - opencode installs **19** — the 18 shared plus the coordinator.
19
+ - opencode installs **20** — the 19 shared plus the coordinator.
20
20
  - The locally edited `SKILL.md` is **overwritten** with the canonical content
21
21
  on the next `setup`/init (drift detection: a file is only rewritten when its
22
22
  content differs from the definition). Unrelated skill files are untouched.
23
23
  - Skills never land in the worktree — always under the user-scoped config dir
24
24
  (`~/.claude/skills`, `~/.cursor/skills`, `$XDG_CONFIG_HOME/opencode/skills`).
25
25
 
26
- _Automatable: yes — file presence, count (18 vs 19), content-diff-overwrite, and
26
+ _Automatable: yes — file presence, count (19 vs 20), content-diff-overwrite, and
27
27
  coordinator host-gating are all file assertions (`setup.test.ts` already covers
28
28
  the unit contract)._
@@ -1,6 +1,6 @@
1
1
  # UC-020: Extraction nudge escalation and acknowledgment
2
2
 
3
- _Automatable: missedNudges counter + escalation tiers + extraction_done drain
3
+ _Automatable: missedNudges counter + escalation tiers + extraction_done accept
4
4
  are testable with the mock client and a synthetic tool call sequence._
5
5
 
6
6
  **Preconditions**
@@ -27,12 +27,26 @@ are testable with the mock client and a synthetic tool call sequence._
27
27
  persists until a memory is written or `extraction_done` is called. The nudge
28
28
  repeats each turn with escalated tone (and the payload grows if new tool
29
29
  activity adds entries between nudges).
30
- - Step 6: `thatch_extraction_done` drains the buffer and resets the
31
- `missedNudges` counter. The tool returns `"[acknowledged] extraction buffer
32
- drained"`.
33
- - Step 7: no extraction nudge appears (buffer is empty). If the user prompt
30
+ - Step 6: `thatch_extraction_done` in the parent accepts the buffer (moves it
31
+ to a holding area) and resets the `missedNudges` counter. The tool returns
32
+ `"[acknowledged]"`. Held entries drop when the extractor completes (child
33
+ memory write, child `extraction_done`, or child idle); if the child errors
34
+ or is deleted first, the entries requeue and the nudge replays.
35
+ - Step 7: no extraction nudge appears (buffer is accepted). If the user prompt
34
36
  semantically matches existing memories, a recall nudge may appear instead.
35
- - Alternative drain path: if a child sub-agent (dispatched via the `task`
36
- tool) writes a memory via `thatch_memory_remember`, the parent's buffer is
37
- also drained via the `childToParent` Map — the `missedNudges` counter resets
38
- for the parent session.
37
+ - Alternative drain path (opencode direct extraction): when the parent session
38
+ goes idle with pending tool interactions, the plugin calls
39
+ `triggerExtraction` — creates a child session via the SDK client and prompts
40
+ it directly. The `extracting` set suppresses the nudge in `chat.message`
41
+ while the child runs. The child writes memories via
42
+ `thatch_memory_remember`, which drains the parent's snapshot entries via
43
+ `consumeSnapshot`. When the child goes idle, the snapshot is drained
44
+ (covering no-save runs), the child session is deleted, and a toast fires
45
+ with the metrics. This path bypasses the nudge-and-acknowledge cycle
46
+ entirely — no `missedNudges` counter, no escalation. The nudge path is
47
+ fallback only (when `triggerExtraction` throws).
48
+ - Alternative drain path (agent-initiated): if a child sub-agent (dispatched
49
+ via the `task` tool in the fallback nudge path) writes a memory via
50
+ `thatch_memory_remember`, the parent's buffer is also drained via the
51
+ `childToParent` Map — the `missedNudges` counter resets for the parent
52
+ session.
@@ -20,16 +20,15 @@ Publish to npm and add thatch to your opencode config:
20
20
 
21
21
  OpenCode installs the plugin and its dependencies automatically on next start.
22
22
 
23
- Set this environment variable so the extraction nudge can dispatch the
24
- fact-extractor as a **background** sub-agent instead of blocking your turn:
23
+ For async extraction (child sessions run in the background):
25
24
 
26
25
  ```bash
27
26
  export OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true
28
27
  ```
29
28
 
30
- Without it, opencode hides the task tool's `background` parameter and the
31
- extraction nudge runs synchronously. Put it in your shell rc, `mise.toml`
32
- `[env]`, or `.envrc`.
29
+ Without this env var, extraction still works — the child session runs
30
+ fire-and-forget instead of asynchronously. Put it in your shell rc,
31
+ `mise.toml` `[env]`, or `.envrc`.
33
32
 
34
33
  For local development before publishing, use a file path:
35
34
 
@@ -106,7 +105,7 @@ Stores are created automatically — no setup required.
106
105
 
107
106
  | Tool | What it does |
108
107
  |------|-------------|
109
- | `thatch_extraction_done` | Acknowledge that the extraction nudge was dispatched to a sub-agent. Drains the buffer so the nudge does not replay. Called after dispatching a background task to run the fact-extractor skill. |
108
+ | `thatch_extraction_done` | Acknowledge extraction buffer work. In the nudge fallback path, quiets the nudge while holding entries until the extractor completes. In a child extractor session, marks entries complete. Called by the model after dispatching the fact-extractor skill. |
110
109
 
111
110
  ### Prediction tools
112
111
 
@@ -133,13 +132,21 @@ Beyond the tools, thatch hooks into opencode itself:
133
132
  - **Write-time similarity warning.** Saving a memory that closely resembles an
134
133
  existing one succeeds, but the response warns the agent and lists the
135
134
  similar entries so it can merge them or record that they're distinct.
136
- - **Fact-extraction nudges.** Thatch buffers the session's recent tool calls
137
- (up to 20, per session). On your next message, the agent receives a summary
138
- payload and is prompted to save any durable facts it reveals. The nudge
139
- repeats and escalates (polite to insistent to ALL-CAPS) each turn it's
140
- ignored — the buffer drains only when the agent writes a memory or
141
- acknowledges via `thatch_extraction_done`. The agent does the writing —
142
- thatch never saves memories on its own.
135
+ - **Fact extraction.** Thatch buffers the session's recent tool calls (up
136
+ to 20, per session). When the session goes idle, the plugin creates a
137
+ child session and prompts it to run the fact-extractor skill directly —
138
+ no nudge text in your conversation. The child writes memories via
139
+ `thatch_memory_remember` and is cleaned up when it finishes. A toast
140
+ notification shows the results (`[thatch] new: 2, updated: 1`). If the
141
+ direct path fails, a nudge is injected into the next message as a
142
+ fallback. On Claude Code and Cursor, the nudge-and-acknowledge path is
143
+ the only mechanism (no SDK client to create child sessions). The agent
144
+ does the writing — thatch never saves memories on its own.
145
+ - **Recall and prediction toasts.** When your prompt matches stored
146
+ memories or learned decision patterns, thatch injects a synthetic nudge
147
+ (invisible to you, visible to the agent) and fires a toast notification
148
+ (`[thatch] recalled 3 memories` or `[thatch] 2 predictions surfaced`).
149
+ The toast is ephemeral — it fades after a few seconds.
143
150
  - **Compaction context.** When opencode compacts a long session, thatch injects
144
151
  a reminder so the summarized session still knows memory tools exist.
145
152
  - **Prediction auto-fire.** When a prompt matches learned contexts, thatch
@@ -179,7 +186,7 @@ With `thatch setup --claude`, skills install to `~/.claude/skills/` (or
179
186
 
180
187
  ### Code review skills
181
188
 
182
- Six specialist review lenses, each a self-contained static-analysis pass:
189
+ Seven specialist review lenses, each a self-contained static-analysis pass:
183
190
 
184
191
  | Skill | Focus |
185
192
  |-------|-------|
@@ -189,7 +196,8 @@ Six specialist review lenses, each a self-contained static-analysis pass:
189
196
  | `thatch-review-no-slop` | AI writing anti-patterns: change narration, fourth wall breaks, em dashes, hedging, filler. |
190
197
  | `thatch-review-breadcrumbs` | Comment narrative: do comments form a coherent outline of the code's behavior? |
191
198
  | `thatch-review-mark-and-sweep` | Mechanical change completeness: whole-repo sweep for stragglers after renames, flag removals, API substitutions. |
192
- | `thatch-review-synthesizer` | Verifies and synthesizes findings from multiple specialists into a report that starts with workflow changes, then deduplicated severity-grouped findings. |
199
+ | `thatch-review-highlights` | Positive finding detection: notably clever solutions, cleanup done along the way, documentation that helps. Medium-high bar against generic praise. |
200
+ | `thatch-review-synthesizer` | Verifies and synthesizes findings from multiple specialists into a report that starts with workflow changes, then highlights, then deduplicated severity-grouped findings. |
193
201
  | `thatch-review-context` | Gathers project context (PR descriptions, git archaeology, ticket references, memory) before a review. Prevents false positives about intentionally deferred work. |
194
202
  | `thatch-workflow-research` | Researches code workflows and features affected by a change or planned change. Reads code flows, comments, git history, and produces a guide for reviewers or planners. |
195
203
  | `thatch-change-walkthrough` | Explains a change to the user as a teaching walkthrough: researches each affected workflow at the merge-base, teaches current behavior, then overlays the modifications with file:line citations. |
@@ -214,7 +222,7 @@ Six specialist review lenses, each a self-contained static-analysis pass:
214
222
  | Skill | opencode | Claude Code | Cursor |
215
223
  |-------|----------|-------------|--------|
216
224
  | Memory skills (4) | Yes | Yes | Yes |
217
- | Review specialists (6) | Yes | Yes | Yes |
225
+ | Review specialists (7) | Yes | Yes | Yes |
218
226
  | Review synthesizer | Yes | Yes | Yes |
219
227
  | Review context + workflow research | Yes | Yes | Yes |
220
228
  | Walkthrough skills (2) | Yes | Yes | Yes |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jeffober/thatch",
3
- "version": "0.1.21",
3
+ "version": "0.1.24",
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",