@jeffober/thatch 0.1.15 → 0.1.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -15
- package/artifacts/skills/pr-description.md +360 -0
- package/artifacts/skills/split-overlarge-pr.md +112 -0
- package/artifacts/skills/thatch-change-walkthrough.md +211 -0
- package/artifacts/skills/thatch-code-walkthrough.md +194 -0
- package/artifacts/skills/ticket-description.md +399 -0
- package/bin/thatch +1 -1
- package/docs/dev/README.md +42 -18
- package/docs/dev/gotchas.md +27 -0
- package/docs/dev/mcp-parity.md +17 -10
- package/docs/dev/setup-and-hooks.md +4 -4
- package/docs/dev/skills.md +8 -3
- package/docs/qa/README.md +1 -1
- package/docs/qa/use-cases/README.md +4 -2
- package/docs/qa/use-cases/UC-003-extraction-nudge.md +17 -4
- package/docs/qa/use-cases/UC-005-setup-install.md +2 -2
- package/docs/qa/use-cases/UC-006-cursor-hook-lifecycle.md +4 -3
- package/docs/qa/use-cases/UC-009-flush-tools-tiers.md +3 -1
- package/docs/qa/use-cases/UC-014-skill-install-drift.md +3 -3
- package/docs/qa/use-cases/UC-016-concurrent-session-isolation.md +4 -3
- package/docs/qa/use-cases/UC-019-archived-memory-lifecycle.md +36 -0
- package/docs/qa/use-cases/UC-020-extraction-escalation.md +38 -0
- package/docs/user/README.md +27 -5
- package/package.json +3 -2
- package/src/index.ts +15 -15
- package/src/mcp.ts +1 -1
- package/src/prompts.ts +41 -1
- package/src/setup.ts +1 -1
- package/src/skills.ts +11 -3
package/docs/dev/mcp-parity.md
CHANGED
|
@@ -18,11 +18,11 @@ This document maps the feature parity and documents the remaining gaps.
|
|
|
18
18
|
|
|
19
19
|
| Feature | OpenCode plugin | Claude Code MCP + hooks | Cursor MCP + hooks | Parity |
|
|
20
20
|
|---------|----------------|------------------------|---------------------|--------|
|
|
21
|
-
| **Tools** (
|
|
21
|
+
| **Tools** (9, single source of truth) | Plugin tool registration, `thatch_` prefix | MCP `tools/list` + `tools/call`, `mcp__thatch__` prefix | MCP `tools/list` + `tools/call`, `mcp__thatch__` prefix | **Full** |
|
|
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 tool calls in-process; `chat.message`
|
|
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` |
|
|
26
26
|
| **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
27
|
| **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) |
|
|
28
28
|
| **Store detection** (repo identity from git remote) | `worktree` parameter from the opencode plugin | `CLAUDE_PROJECT_DIR` env (set by Claude Code for stdio MCP servers) | `CURSOR_PROJECT_DIR` then `CLAUDE_PROJECT_DIR` then cwd | **Full** |
|
|
@@ -64,13 +64,17 @@ explicit "re-familiarization" nudge for hosts without a compaction hook.
|
|
|
64
64
|
|
|
65
65
|
OpenCode's extraction pipeline works by:
|
|
66
66
|
|
|
67
|
-
1. `tool.execute.after` buffers every non-thatch
|
|
68
|
-
in-memory array (max 20 interactions).
|
|
69
|
-
2. On the next `chat.message`, the buffer is
|
|
70
|
-
carrying the JSON payload is injected into the
|
|
71
|
-
|
|
67
|
+
1. `tool.execute.after` buffers every non-thatch, non-skill, non-task tool
|
|
68
|
+
call into a per-session in-memory array (max 20 interactions).
|
|
69
|
+
2. On the next `chat.message`, the buffer is **peeked** (not drained) and a
|
|
70
|
+
synthetic text part carrying the JSON payload is injected into the
|
|
71
|
+
conversation. The buffer persists until the agent writes a memory or calls
|
|
72
|
+
`thatch_extraction_done`, so ignored nudges repeat and escalate (polite →
|
|
73
|
+
insistent → ALL-CAPS) via the `missedNudges` counter.
|
|
72
74
|
3. The agent sees the nudge, loads the `thatch-fact-extractor` skill, and saves
|
|
73
|
-
durable facts via `thatch_memory_remember`.
|
|
75
|
+
durable facts via `thatch_memory_remember`. A memory write in the session
|
|
76
|
+
(or a child sub-agent via the `childToParent` Map) or `thatch_extraction_done`
|
|
77
|
+
drains the buffer and resets the missed-nudge counter.
|
|
74
78
|
|
|
75
79
|
Claude Code and Cursor use a **file-backed** queue (`src/extract-queue.ts`)
|
|
76
80
|
because hooks fire one-shot with no cross-call state:
|
|
@@ -80,7 +84,10 @@ because hooks fire one-shot with no cross-call state:
|
|
|
80
84
|
(max 20, oldest dropped). These commands are silent — no stdout — so the
|
|
81
85
|
agent loop is not delayed.
|
|
82
86
|
2. `UserPromptSubmit` / `beforeSubmitPrompt` runs `thatch flush-tools`, which
|
|
83
|
-
|
|
87
|
+
peeks the queue (does not drain it) and prints the JSON payload. The queue
|
|
88
|
+
persists until the agent writes a memory or calls `thatch_extraction_done`,
|
|
89
|
+
so ignored nudges accumulate and escalate (polite → insistent → ALL-CAPS)
|
|
90
|
+
via the file-backed missed-nudge counter. This arrives at the **start**
|
|
84
91
|
of the next turn (before the model processes the prompt), not at the end of
|
|
85
92
|
the current one.
|
|
86
93
|
|
|
@@ -106,7 +113,7 @@ Claude Code / Cursor session
|
|
|
106
113
|
│ ├── stdio JSON-RPC (tool calls)
|
|
107
114
|
│ └── Unix socket sideband (embed + search for hooks)
|
|
108
115
|
├── UserPromptSubmit / beforeSubmitPrompt hook → thatch flush-tools [--json]
|
|
109
|
-
│ ├──
|
|
116
|
+
│ ├── peeks the file-backed extraction queue (does not drain)
|
|
110
117
|
│ ├── connects to sideband socket
|
|
111
118
|
│ ├── sends prompt text
|
|
112
119
|
│ ├── receives match labels + scores
|
|
@@ -19,7 +19,7 @@ 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_` tool calls into the in-memory extraction ring buffer (max 20). |
|
|
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
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
24
|
| `event` (`session.created`) | `{ event: { type, properties: { info: { id } } } }` | — | Calls `client.session.prompt` with the reminder + hygiene heartbeat. |
|
|
25
25
|
| `dispose` | — | — | Closes the DB. |
|
|
@@ -60,10 +60,10 @@ in the repo; only skills are user-scoped.
|
|
|
60
60
|
|-------|---------|--------|------|
|
|
61
61
|
| `SessionStart` | `thatch reminder` | plain text to stdout (becomes context) | Recall instructions + hygiene heartbeat |
|
|
62
62
|
| `PostToolBatch` | `thatch buffer-batch` | **silent** (no stdout) | Appends a batch of tool calls to the file-backed JSONL queue |
|
|
63
|
-
| `UserPromptSubmit` | `thatch flush-tools` | nudge text to stdout |
|
|
63
|
+
| `UserPromptSubmit` | `thatch flush-tools` | nudge text to stdout | Peeks queue (extraction nudge), else recall nudge via sideband, else write nudge |
|
|
64
64
|
|
|
65
65
|
`PostToolBatch` is silent so the agent loop is not delayed; the buffered
|
|
66
|
-
content is invisible until `UserPromptSubmit`
|
|
66
|
+
content is invisible until `UserPromptSubmit` peeks it.
|
|
67
67
|
|
|
68
68
|
## Cursor (MCP server + hooks)
|
|
69
69
|
|
|
@@ -100,7 +100,7 @@ is honored for symmetry and forward-compatibility. Cursor has no equivalent of
|
|
|
100
100
|
|-------|---------|--------|------|
|
|
101
101
|
| `sessionStart` | `thatch reminder --json` | `{ additional_context: "..." }` | Recall + heartbeat, JSON-wrapped for Cursor |
|
|
102
102
|
| `postToolUse` | `thatch buffer-tool` | **silent** | Appends a **single** tool call to the file-backed queue |
|
|
103
|
-
| `beforeSubmitPrompt` | `thatch flush-tools --json` | JSON `additional_context` |
|
|
103
|
+
| `beforeSubmitPrompt` | `thatch flush-tools --json` | JSON `additional_context` | Peeks queue, else recall via sideband, else write nudge |
|
|
104
104
|
|
|
105
105
|
Differences from Claude Code:
|
|
106
106
|
|
package/docs/dev/skills.md
CHANGED
|
@@ -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
|
|
24
|
+
## The 19 skills
|
|
25
25
|
|
|
26
|
-
**Shared (
|
|
26
|
+
**Shared (18)** — installed everywhere; no sub-agents required:
|
|
27
27
|
|
|
28
28
|
| Skill | Role |
|
|
29
29
|
|-------|------|
|
|
@@ -39,7 +39,12 @@ description: Extract durable project facts ... Use when ...
|
|
|
39
39
|
| `thatch-review-synthesizer` | Verify specialist findings against code, dedupe, classify, calibrate severity. Cross-references findings against prior review comments when a follow-up round register is provided; produces a Previously identified appendix. |
|
|
40
40
|
| `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
41
|
| `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. |
|
|
42
|
+
| `thatch-change-walkthrough` | Explain a change to the user as a teaching walkthrough: resolve the delta, research each affected workflow at the merge-base, teach current behavior, then overlay the modifications with file:line citations and analogies. |
|
|
43
|
+
| `thatch-code-walkthrough` | Explain a feature, module, or workflow to the user as a teaching walkthrough: identify the code area (optionally from a branch or PR), research how it works, teach it with file:line citations and analogies, list the key files. |
|
|
42
44
|
| `thatch-session-reflection` | End-of-session memory recording (project, user, tools, self). |
|
|
45
|
+
| `pr-description` | Draft PR descriptions with instructional-design scaffolding (SYNOPSIS / PURPOSE / DESCRIPTION / WALK-THROUGH / NOTES) and bold+italic emphasis for scanning. |
|
|
46
|
+
| `ticket-description` | Draft ticket/issue descriptions with instructional-design scaffolding (SYNOPSIS / PROBLEM / BACKGROUND / PROPOSED APPROACH / ACCEPTANCE CRITERIA / RELEASE PLAN / VALIDATION / DEPENDENCIES / NOTES). |
|
|
47
|
+
| `split-overlarge-pr` | Split already-completed work from an overlarge PR into human-reviewable, release-safe PRs targeting main. |
|
|
43
48
|
|
|
44
49
|
**opencode-only (1)** — the coordinator needs sub-agent support:
|
|
45
50
|
|
|
@@ -79,7 +84,7 @@ The synthesizer reuses the same verification rigor but has its own structure
|
|
|
79
84
|
## The two arrays
|
|
80
85
|
|
|
81
86
|
```ts
|
|
82
|
-
const SHARED_SKILLS: SkillDef[] = [ /*
|
|
87
|
+
const SHARED_SKILLS: SkillDef[] = [ /* 18 skills above */ ];
|
|
83
88
|
const OPENCODE_ONLY_SKILLS: SkillDef[] = [ /* code-review coordinator */ ];
|
|
84
89
|
```
|
|
85
90
|
|
package/docs/qa/README.md
CHANGED
|
@@ -91,7 +91,7 @@ correctness on `README.md` and `docs/` excluding `docs/plans/`; see
|
|
|
91
91
|
|
|
92
92
|
### `tests/plugin.test.ts`
|
|
93
93
|
|
|
94
|
-
- `server` export shape: all
|
|
94
|
+
- `server` export shape: all nine tools, every hook present, tools carry
|
|
95
95
|
description/args/execute
|
|
96
96
|
- System transform and compaction hooks append their text
|
|
97
97
|
- `tool.execute.after` → `chat.message` extraction round-trip: nudge carries
|
|
@@ -14,7 +14,7 @@ file/IPC/CLI contracts ready to graduate into `bun:test` integration tests.
|
|
|
14
14
|
| [UC-003](UC-003-extraction-nudge.md) | Tool buffering → extraction nudge → agent-driven fact saving |
|
|
15
15
|
| [UC-004](UC-004-cli-inspection.md) | CLI commands, arg validation, env overrides |
|
|
16
16
|
| [UC-005](UC-005-setup-install.md) | `thatch setup --claude`/`--cursor`, project-local + `--global`, idempotent re-run, legacy hook migration |
|
|
17
|
-
| [UC-006](UC-006-cursor-hook-lifecycle.md) | Cursor hook lifecycle: `buffer-tool` (`conversation_id`), `flush-tools --json`, queue
|
|
17
|
+
| [UC-006](UC-006-cursor-hook-lifecycle.md) | Cursor hook lifecycle: `buffer-tool` (`conversation_id`), `flush-tools --json`, queue peek |
|
|
18
18
|
| [UC-007](UC-007-recall-nudge.md) | Prompt-aware recall nudge — opencode in-process + Claude/Cursor sideband paths; no telemetry |
|
|
19
19
|
| [UC-008](UC-008-hygiene-heartbeat.md) | Hygiene heartbeat: stale 90d, orphaned branches, pending dedup; advisory, agent-driven |
|
|
20
20
|
| [UC-009](UC-009-flush-tools-tiers.md) | `flush-tools` three-tier priority (extraction > recall > write) + sideband degradation |
|
|
@@ -22,11 +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:
|
|
25
|
+
| [UC-014](UC-014-skill-install-drift.md) | Skill install: 18 shared vs 19 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
|
+
| [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 |
|
|
30
32
|
|
|
31
33
|
## Template
|
|
32
34
|
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
# UC-003: Fact extraction from tool activity
|
|
2
2
|
|
|
3
|
+
_Automatable: buffer/peek/escalation contract could be tested with the mock
|
|
4
|
+
client and a synthetic tool call sequence._
|
|
5
|
+
|
|
3
6
|
**Preconditions**
|
|
4
7
|
- thatch plugin active in an opencode session
|
|
5
8
|
|
|
6
9
|
**Steps**
|
|
7
10
|
1. Do some real work: have the agent read files, run commands, edit code —
|
|
8
|
-
any non
|
|
11
|
+
any non-`thatch_*`, non-`skill`, non-`task` tool activity.
|
|
9
12
|
2. Send your next message (anything).
|
|
10
13
|
|
|
11
14
|
**Expected**
|
|
@@ -14,8 +17,18 @@
|
|
|
14
17
|
truncated args/output).
|
|
15
18
|
- The agent loads `thatch-fact-extractor` and saves durable facts via
|
|
16
19
|
`thatch_memory_remember` — or saves nothing if the activity was routine.
|
|
17
|
-
- The buffer is
|
|
18
|
-
|
|
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:
|
|
23
|
+
- 1st-2nd miss: polite tone
|
|
24
|
+
- 3rd consecutive miss (missedCount=2): insistent (directive) tone
|
|
25
|
+
- 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
|
|
30
|
+
fact-extractor as a background task clears the parent's queue.
|
|
19
31
|
- Two concurrent sessions never see each other's interactions in a nudge.
|
|
20
32
|
- The agent's own `thatch_*` tool calls never appear in a payload (no
|
|
21
|
-
feedback loop).
|
|
33
|
+
feedback loop). `skill` and `task` tool calls are also excluded (buffering
|
|
34
|
+
them would create a nudge → skill load → buffer → nudge loop).
|
|
@@ -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
|
-
**
|
|
20
|
+
**18 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
|
|
25
|
+
`thatch flush-tools --json`; and installs 18 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
|
|
@@ -17,9 +17,10 @@
|
|
|
17
17
|
**Expected**
|
|
18
18
|
- `buffer-tool` normalizes `conversation_id` to a safe session file name and appends exactly one
|
|
19
19
|
interaction per call; thatch's own `mcp__thatch__*` tools never enter the queue (no self-echo).
|
|
20
|
-
- `flush-tools`
|
|
21
|
-
Cursor as `additional_context
|
|
22
|
-
|
|
20
|
+
- `flush-tools` peeks the queue first (extraction tier): prints the JSON payload wrapped for
|
|
21
|
+
Cursor as `additional_context`. The queue is **not** deleted — it persists until the agent
|
|
22
|
+
writes a memory or calls `thatch_extraction_done`, so ignored nudges repeat and escalate
|
|
23
|
+
(polite to insistent to ALL-CAPS) on subsequent calls.
|
|
23
24
|
- With the queue empty and the sideband socket live, a semantically matching prompt yields a
|
|
24
25
|
recall nudge using the bare `memory_recall` tool name; below `THATCH_RECALL_THRESHOLD`
|
|
25
26
|
(default 0.55), no nudge.
|
|
@@ -16,7 +16,9 @@
|
|
|
16
16
|
|
|
17
17
|
**Expected**
|
|
18
18
|
- Tiers fire in strict priority — **extraction > recall > write** — at most one nudge per call:
|
|
19
|
-
- extraction: queue non-empty -> JSON payload
|
|
19
|
+
- extraction: queue non-empty -> JSON payload. The queue is peeked, not
|
|
20
|
+
deleted; it persists until a memory write or `extraction_done` (drains via
|
|
21
|
+
`consumeQueue` in `appendBatch`).
|
|
20
22
|
- recall: queue empty, socket live, match at or above `THATCH_RECALL_THRESHOLD` (default 0.55)
|
|
21
23
|
-> nudge with match labels; below threshold -> no nudge.
|
|
22
24
|
- write: socket unavailable, no matches, or socket error -> static
|
|
@@ -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 **
|
|
16
|
+
- Claude Code and Cursor install exactly **18 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
|
+
- opencode installs **19** — the 18 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 (
|
|
26
|
+
_Automatable: yes — file presence, count (18 vs 19), content-diff-overwrite, and
|
|
27
27
|
coordinator host-gating are all file assertions (`setup.test.ts` already covers
|
|
28
28
|
the unit contract)._
|
|
@@ -24,9 +24,10 @@
|
|
|
24
24
|
no nudge from session A's buffered interaction; session B's message only sees
|
|
25
25
|
its own buffer. The in-memory `ExtractionPipeline` and the file-backed queue are
|
|
26
26
|
both keyed by session ID.
|
|
27
|
-
- `flush-tools` for session A
|
|
28
|
-
flush for A
|
|
29
|
-
|
|
27
|
+
- `flush-tools` for session A peeks only A's queue file (does not delete it); a
|
|
28
|
+
second flush for A returns the same content with an escalated missed-nudge
|
|
29
|
+
counter (the queue persists until a memory write or `extraction_done`). Session
|
|
30
|
+
B's queue file is untouched and is peeked on its own flush.
|
|
30
31
|
- The sideband socket is **shared** (one warm MCP server) but **stateless per
|
|
31
32
|
query**: each request embeds a fresh prompt and returns its own matches. Two
|
|
32
33
|
concurrent match requests do not interfere.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# UC-019: Archived memory lifecycle
|
|
2
|
+
|
|
3
|
+
_Automatable: archive guard + search/dedup/staleness exclusion are pure DB
|
|
4
|
+
contract testable with a temp DB._
|
|
5
|
+
|
|
6
|
+
**Preconditions**
|
|
7
|
+
- thatch plugin active in an opencode session (or CLI with a test store)
|
|
8
|
+
|
|
9
|
+
**Steps**
|
|
10
|
+
1. Save a memory scoped to a feature branch:
|
|
11
|
+
`thatch_memory_remember` with `branch: "feature-x"`, `label: "Feature X design decisions"`, `content: "..."`.
|
|
12
|
+
2. Merge or delete the branch. Save a consolidated archived memory:
|
|
13
|
+
`thatch_memory_remember` with `overwrite: true`, `archived: true`,
|
|
14
|
+
`branch: "feature-x"`, same label, consolidated content.
|
|
15
|
+
3. Search for content related to the archived memory:
|
|
16
|
+
`thatch_memory_recall` with a relevant query (no `includeArchived`).
|
|
17
|
+
4. Search again with `includeArchived: true`.
|
|
18
|
+
5. Attempt to update the archived memory's content without passing the
|
|
19
|
+
`archived` param.
|
|
20
|
+
6. Unarchive the memory: `thatch_memory_remember` with `overwrite: true`,
|
|
21
|
+
`archived: false`.
|
|
22
|
+
|
|
23
|
+
**Expected**
|
|
24
|
+
- Step 2: the memory is saved with `archived: true`. The `archived` column is
|
|
25
|
+
set to 1 in the database.
|
|
26
|
+
- Step 3: the archived memory does NOT appear in search results — `search`
|
|
27
|
+
filters `WHERE archived = 0`.
|
|
28
|
+
- Step 4: the archived memory DOES appear when `includeArchived: true` is
|
|
29
|
+
passed.
|
|
30
|
+
- Step 5: the tool returns an error: `"Feature X design decisions" is
|
|
31
|
+
archived. Pass archived: true to keep it archived, or archived: false to
|
|
32
|
+
unarchive it.` The content is NOT updated.
|
|
33
|
+
- Step 6: the memory is unarchived (`archived` column set to 0). It reappears
|
|
34
|
+
in normal search results.
|
|
35
|
+
- `find_duplicates` does not surface archived memories as candidates.
|
|
36
|
+
- `staleEntryCount` (hygiene) does not count archived memories as stale.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# UC-020: Extraction nudge escalation and acknowledgment
|
|
2
|
+
|
|
3
|
+
_Automatable: missedNudges counter + escalation tiers + extraction_done drain
|
|
4
|
+
are testable with the mock client and a synthetic tool call sequence._
|
|
5
|
+
|
|
6
|
+
**Preconditions**
|
|
7
|
+
- thatch active in an opencode, Claude Code, or Cursor session
|
|
8
|
+
- For the child-sub-agent path: opencode with
|
|
9
|
+
`OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true`
|
|
10
|
+
|
|
11
|
+
**Steps**
|
|
12
|
+
1. Do some non-thatch tool work (read files, run commands). Do NOT write any
|
|
13
|
+
memories.
|
|
14
|
+
2. Send a message. Observe the extraction nudge — polite tone (missedCount=0).
|
|
15
|
+
3. Ignore the nudge. Send another message without writing a memory (missedCount=1).
|
|
16
|
+
4. Repeat step 3 (missedCount=2).
|
|
17
|
+
5. Repeat step 3 again (missedCount=3+).
|
|
18
|
+
6. Acknowledge by calling `thatch_extraction_done` in the session.
|
|
19
|
+
7. Send another message.
|
|
20
|
+
|
|
21
|
+
**Expected**
|
|
22
|
+
- Step 2: the nudge is polite, referencing the queued tool interactions.
|
|
23
|
+
- Step 4: after 2 consecutive misses (missedCount=2), the nudge is insistent
|
|
24
|
+
(directive tone).
|
|
25
|
+
- Step 5: after 3+ consecutive misses (missedCount>=3), the nudge is ALL-CAPS.
|
|
26
|
+
- The buffer was NOT drained at any point during steps 2-5 — the buffer
|
|
27
|
+
persists until a memory is written or `extraction_done` is called. The nudge
|
|
28
|
+
repeats each turn with escalated tone (and the payload grows if new tool
|
|
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
|
|
34
|
+
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.
|
package/docs/user/README.md
CHANGED
|
@@ -102,6 +102,12 @@ Stores are created automatically — no setup required.
|
|
|
102
102
|
| `thatch_find_duplicates` | Surface pairs of memories with suspiciously similar content. |
|
|
103
103
|
| `thatch_dedup_mark_checked` | Record the verdict for a reviewed pair so it stops being re-reported. |
|
|
104
104
|
|
|
105
|
+
### Extraction tools
|
|
106
|
+
|
|
107
|
+
| Tool | What it does |
|
|
108
|
+
|------|-------------|
|
|
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. |
|
|
110
|
+
|
|
105
111
|
## Automatic behaviors
|
|
106
112
|
|
|
107
113
|
Beyond the tools, thatch hooks into opencode itself:
|
|
@@ -120,8 +126,11 @@ Beyond the tools, thatch hooks into opencode itself:
|
|
|
120
126
|
similar entries so it can merge them or record that they're distinct.
|
|
121
127
|
- **Fact-extraction nudges.** Thatch buffers the session's recent tool calls
|
|
122
128
|
(up to 20, per session). On your next message, the agent receives a summary
|
|
123
|
-
payload and is prompted to save any durable facts it reveals. The
|
|
124
|
-
|
|
129
|
+
payload and is prompted to save any durable facts it reveals. The nudge
|
|
130
|
+
repeats and escalates (polite to insistent to ALL-CAPS) each turn it's
|
|
131
|
+
ignored — the buffer drains only when the agent writes a memory or
|
|
132
|
+
acknowledges via `thatch_extraction_done`. The agent does the writing —
|
|
133
|
+
thatch never saves memories on its own.
|
|
125
134
|
- **Compaction context.** When opencode compacts a long session, thatch injects
|
|
126
135
|
a reminder so the summarized session still knows memory tools exist.
|
|
127
136
|
|
|
@@ -155,7 +164,7 @@ With `thatch setup --claude`, skills install to `~/.claude/skills/` (or
|
|
|
155
164
|
|
|
156
165
|
### Code review skills
|
|
157
166
|
|
|
158
|
-
|
|
167
|
+
Six specialist review lenses, each a self-contained static-analysis pass:
|
|
159
168
|
|
|
160
169
|
| Skill | Focus |
|
|
161
170
|
|-------|-------|
|
|
@@ -164,9 +173,20 @@ Five specialist review lenses, each a self-contained static-analysis pass:
|
|
|
164
173
|
| `thatch-review-state-flow` | Data flow and contracts: module boundaries, implicit state machines, error propagation, separation of concerns. |
|
|
165
174
|
| `thatch-review-no-slop` | AI writing anti-patterns: change narration, fourth wall breaks, em dashes, hedging, filler. |
|
|
166
175
|
| `thatch-review-breadcrumbs` | Comment narrative: do comments form a coherent outline of the code's behavior? |
|
|
176
|
+
| `thatch-review-mark-and-sweep` | Mechanical change completeness: whole-repo sweep for stragglers after renames, flag removals, API substitutions. |
|
|
167
177
|
| `thatch-review-synthesizer` | Verifies and synthesizes findings from multiple specialists into a single deduplicated, severity-grouped report. |
|
|
168
178
|
| `thatch-review-context` | Gathers project context (PR descriptions, git archaeology, ticket references, memory) before a review. Prevents false positives about intentionally deferred work. |
|
|
169
179
|
| `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. |
|
|
180
|
+
| `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. |
|
|
181
|
+
| `thatch-code-walkthrough` | Explains a feature, module, or workflow to the user as a teaching walkthrough with file:line citations. Also used to draft high-level docs for new or undocumented features. |
|
|
182
|
+
|
|
183
|
+
### Writing skills
|
|
184
|
+
|
|
185
|
+
| Skill | Purpose |
|
|
186
|
+
|-------|---------|
|
|
187
|
+
| `pr-description` | Drafts PR descriptions using instructional-design scaffolding (SYNOPSIS / PURPOSE / DESCRIPTION / WALK-THROUGH / NOTES) with bold and italic emphasis for scanning. |
|
|
188
|
+
| `ticket-description` | Drafts ticket or issue descriptions (Linear or Jira) using instructional-design scaffolding with bold and italic emphasis for scanning. |
|
|
189
|
+
| `split-overlarge-pr` | Splits already-completed work from an overlarge PR into human-reviewable, release-safe PRs targeting main. |
|
|
170
190
|
|
|
171
191
|
### opencode-only skills
|
|
172
192
|
|
|
@@ -179,9 +199,11 @@ Five specialist review lenses, each a self-contained static-analysis pass:
|
|
|
179
199
|
| Skill | opencode | Claude Code | Cursor |
|
|
180
200
|
|-------|----------|-------------|--------|
|
|
181
201
|
| Memory skills (4) | Yes | Yes | Yes |
|
|
182
|
-
| Review specialists (
|
|
202
|
+
| Review specialists (6) | Yes | Yes | Yes |
|
|
183
203
|
| Review synthesizer | Yes | Yes | Yes |
|
|
184
204
|
| Review context + workflow research | Yes | Yes | Yes |
|
|
205
|
+
| Walkthrough skills (2) | Yes | Yes | Yes |
|
|
206
|
+
| Writing skills (3) | Yes | Yes | Yes |
|
|
185
207
|
| Code review coordinator | Yes | No (requires sub-agents) | No (requires sub-agents) |
|
|
186
208
|
|
|
187
209
|
### Using review skills
|
|
@@ -208,7 +230,7 @@ specialist in sequence, then synthesize:
|
|
|
208
230
|
```text
|
|
209
231
|
1. Load thatch-review-pedantic, review branch feature-x, report findings.
|
|
210
232
|
2. Load thatch-review-acceptance, review the same branch.
|
|
211
|
-
3. ... repeat for state-flow, no-slop, breadcrumbs ...
|
|
233
|
+
3. ... repeat for state-flow, no-slop, breadcrumbs, mark-and-sweep ...
|
|
212
234
|
4. Load thatch-review-synthesizer, verify and aggregate all findings.
|
|
213
235
|
```
|
|
214
236
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jeffober/thatch",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.17",
|
|
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",
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
"@opencode-ai/plugin": "^1.17.13",
|
|
31
31
|
"@types/bun": "latest",
|
|
32
32
|
"markdownlint-cli2": "^0.23.0",
|
|
33
|
-
"typescript": "^5.7.0"
|
|
33
|
+
"typescript": "^5.7.0",
|
|
34
|
+
"yaml": "^2.7.0"
|
|
34
35
|
},
|
|
35
36
|
"peerDependencies": {
|
|
36
37
|
"@opencode-ai/plugin": "^1.17.0"
|
package/src/index.ts
CHANGED
|
@@ -112,22 +112,22 @@ export const server: Plugin = async ({ client, worktree }) => {
|
|
|
112
112
|
// agent writes a memory, so ignored nudges accumulate and the payload
|
|
113
113
|
// grows with each missed cycle.
|
|
114
114
|
//
|
|
115
|
-
//
|
|
116
|
-
//
|
|
117
|
-
//
|
|
118
|
-
//
|
|
115
|
+
// A memory_remember call in a child session (sub-agent) also drains
|
|
116
|
+
// the parent's buffer. Without this, dispatching the fact-extractor as
|
|
117
|
+
// a background task writes memories in the child but never clears the
|
|
118
|
+
// parent's queue — the nudge replays every turn.
|
|
119
119
|
//
|
|
120
|
-
//
|
|
121
|
-
//
|
|
122
|
-
//
|
|
123
|
-
//
|
|
120
|
+
// thatch_extraction_done is an explicit acknowledgment tool the model
|
|
121
|
+
// calls in the parent session after dispatching. Covers the case where
|
|
122
|
+
// the sub-agent errors out or the host doesn't expose parent-child
|
|
123
|
+
// session relationships.
|
|
124
124
|
"tool.execute.after": async (input, output) => {
|
|
125
125
|
if (input.tool === "thatch_memory_remember") {
|
|
126
126
|
extraction.consume(input.sessionID);
|
|
127
127
|
missedNudges.delete(input.sessionID);
|
|
128
|
-
//
|
|
129
|
-
//
|
|
130
|
-
//
|
|
128
|
+
// If this is a child session, also drain the parent so the parent's
|
|
129
|
+
// extraction nudge stops replaying after a sub-agent writes memories
|
|
130
|
+
// on its behalf.
|
|
131
131
|
const parentID = childToParent.get(input.sessionID);
|
|
132
132
|
if (parentID) {
|
|
133
133
|
extraction.consume(parentID);
|
|
@@ -135,9 +135,9 @@ export const server: Plugin = async ({ client, worktree }) => {
|
|
|
135
135
|
}
|
|
136
136
|
return;
|
|
137
137
|
}
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
//
|
|
138
|
+
// Explicit acknowledgment tool drains the buffer without requiring a
|
|
139
|
+
// memory write. The model calls this after dispatching the
|
|
140
|
+
// fact-extractor to a sub-agent.
|
|
141
141
|
if (input.tool === "thatch_extraction_done") {
|
|
142
142
|
extraction.consume(input.sessionID);
|
|
143
143
|
missedNudges.delete(input.sessionID);
|
|
@@ -248,7 +248,7 @@ export const server: Plugin = async ({ client, worktree }) => {
|
|
|
248
248
|
path: { id },
|
|
249
249
|
body: {
|
|
250
250
|
noReply: true,
|
|
251
|
-
parts: [{ type: "text", text: sessionStartReminder(repo, hygiene) }],
|
|
251
|
+
parts: [{ type: "text", text: sessionStartReminder(repo, hygiene), synthetic: true }],
|
|
252
252
|
},
|
|
253
253
|
});
|
|
254
254
|
} catch (err) {
|
package/src/mcp.ts
CHANGED
|
@@ -34,7 +34,7 @@ const INTERNAL_ERROR = -32603;
|
|
|
34
34
|
// ---------------------------------------------------------------------------
|
|
35
35
|
|
|
36
36
|
const SERVER_NAME = "thatch";
|
|
37
|
-
const SERVER_VERSION = "0.1.
|
|
37
|
+
const SERVER_VERSION = "0.1.16";
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
40
|
* MCP capabilities declared in the initialize response. Thatch is a tools-only
|