@jeffober/thatch 0.1.16 → 0.1.18
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 +38 -16
- package/artifacts/skills/pr-description.md +360 -0
- package/artifacts/skills/split-overlarge-pr.md +112 -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 +6 -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 +1 -1
- package/src/extract-queue.ts +0 -7
- package/src/extraction.ts +18 -5
- package/src/index.ts +42 -20
- package/src/mcp.ts +2 -1
- package/src/prompts.ts +35 -1
- package/src/setup.ts +1 -1
- package/src/skills.ts +3 -0
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
|
|-------|------|
|
|
@@ -42,6 +42,9 @@ description: Extract durable project facts ... Use when ...
|
|
|
42
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
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. |
|
|
44
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. |
|
|
45
48
|
|
|
46
49
|
**opencode-only (1)** — the coordinator needs sub-agent support:
|
|
47
50
|
|
|
@@ -81,7 +84,7 @@ The synthesizer reuses the same verification rigor but has its own structure
|
|
|
81
84
|
## The two arrays
|
|
82
85
|
|
|
83
86
|
```ts
|
|
84
|
-
const SHARED_SKILLS: SkillDef[] = [ /*
|
|
87
|
+
const SHARED_SKILLS: SkillDef[] = [ /* 18 skills above */ ];
|
|
85
88
|
const OPENCODE_ONLY_SKILLS: SkillDef[] = [ /* code-review coordinator */ ];
|
|
86
89
|
```
|
|
87
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.18",
|
|
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/extract-queue.ts
CHANGED
|
@@ -99,13 +99,6 @@ export function consumeQueue(sessionID: string): void {
|
|
|
99
99
|
try { unlinkSync(queuePath(sessionID)); } catch { /* not present is fine */ }
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
/** Read and delete a session's queue. Returns the buffered interactions in order. */
|
|
103
|
-
export function flushQueue(sessionID: string): ToolInteraction[] {
|
|
104
|
-
const interactions = peekQueue(sessionID);
|
|
105
|
-
consumeQueue(sessionID);
|
|
106
|
-
return interactions;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
102
|
function readQueueFile(path: string): ToolInteraction[] {
|
|
110
103
|
if (!existsSync(path)) return [];
|
|
111
104
|
const text = readFileSync(path, "utf8").trim();
|
package/src/extraction.ts
CHANGED
|
@@ -110,11 +110,24 @@ export class ExtractionPipeline {
|
|
|
110
110
|
this.#buffers.delete(sessionID);
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
/**
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
113
|
+
/**
|
|
114
|
+
* Remove entries that were in the snapshot (by reference identity), keeping
|
|
115
|
+
* any that arrived after the snapshot was taken. Used by the child-parent
|
|
116
|
+
* drain: when a sub-agent writes a memory, only the entries present at
|
|
117
|
+
* dispatch time should be drained — not the entire buffer, which may now
|
|
118
|
+
* contain interleaved-turn entries the parent accumulated while the
|
|
119
|
+
* sub-agent was running.
|
|
120
|
+
*/
|
|
121
|
+
consumeSnapshot(sessionID: string, snapshot: ToolInteraction[]): void {
|
|
122
|
+
const buf = this.#buffers.get(sessionID);
|
|
123
|
+
if (!buf) return;
|
|
124
|
+
const old = new Set(snapshot);
|
|
125
|
+
const remaining = buf.filter((ix) => !old.has(ix));
|
|
126
|
+
if (remaining.length === 0) {
|
|
127
|
+
this.#buffers.delete(sessionID);
|
|
128
|
+
} else {
|
|
129
|
+
this.#buffers.set(sessionID, remaining);
|
|
130
|
+
}
|
|
118
131
|
}
|
|
119
132
|
|
|
120
133
|
pending(sessionID: string): boolean {
|
package/src/index.ts
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
extractionNudge,
|
|
13
13
|
type NudgeMatch,
|
|
14
14
|
} from "./prompts";
|
|
15
|
-
import { ExtractionPipeline } from "./extraction";
|
|
15
|
+
import { ExtractionPipeline, type ToolInteraction } from "./extraction";
|
|
16
16
|
import { installSkills, SHARED_SKILLS, OPENCODE_ONLY_SKILLS } from "./skills";
|
|
17
17
|
import { hygieneReport } from "./hygiene";
|
|
18
18
|
|
|
@@ -64,6 +64,14 @@ export const server: Plugin = async ({ client, worktree }) => {
|
|
|
64
64
|
// Populated from session.created events that carry a parentID.
|
|
65
65
|
const childToParent = new Map<string, string>();
|
|
66
66
|
|
|
67
|
+
// Snapshot of the parent's buffer at the moment each child was dispatched.
|
|
68
|
+
// When the child writes a memory, consumeSnapshot drains only these entries
|
|
69
|
+
// (by reference identity), preserving interleaved-turn entries that arrived
|
|
70
|
+
// while the sub-agent was running. Without this, the child's memory_remember
|
|
71
|
+
// would drain the parent's ENTIRE buffer — silently dropping facts from
|
|
72
|
+
// any tool calls the parent made concurrenly with the sub-agent.
|
|
73
|
+
const parentSnapshots = new Map<string, ToolInteraction[]>();
|
|
74
|
+
|
|
67
75
|
// Skills always install to the global opencode config — installing into the
|
|
68
76
|
// worktree would mutate the user's repo (untracked files in git status).
|
|
69
77
|
// A failed install degrades the nudge workflow but must not kill the plugin.
|
|
@@ -112,32 +120,41 @@ export const server: Plugin = async ({ client, worktree }) => {
|
|
|
112
120
|
// agent writes a memory, so ignored nudges accumulate and the payload
|
|
113
121
|
// grows with each missed cycle.
|
|
114
122
|
//
|
|
115
|
-
//
|
|
116
|
-
//
|
|
117
|
-
//
|
|
118
|
-
//
|
|
123
|
+
// A memory_remember call in a child session (sub-agent) also drains
|
|
124
|
+
// the parent's buffer — but only the entries that existed at dispatch
|
|
125
|
+
// time (the snapshot). Entries from interleaved turns survive so their
|
|
126
|
+
// facts aren't silently dropped. Without this, dispatching the
|
|
127
|
+
// fact-extractor as a background task writes memories in the child but
|
|
128
|
+
// never clears the parent's queue — the nudge replays every turn.
|
|
119
129
|
//
|
|
120
|
-
//
|
|
121
|
-
//
|
|
122
|
-
//
|
|
123
|
-
//
|
|
130
|
+
// thatch_extraction_done is an explicit acknowledgment tool the model
|
|
131
|
+
// calls in the parent session after dispatching. Covers the case where
|
|
132
|
+
// the sub-agent errors out or the host doesn't expose parent-child
|
|
133
|
+
// session relationships.
|
|
124
134
|
"tool.execute.after": async (input, output) => {
|
|
125
135
|
if (input.tool === "thatch_memory_remember") {
|
|
126
136
|
extraction.consume(input.sessionID);
|
|
127
137
|
missedNudges.delete(input.sessionID);
|
|
128
|
-
//
|
|
129
|
-
//
|
|
130
|
-
//
|
|
138
|
+
// If this is a child session, drain only the parent's snapshot entries
|
|
139
|
+
// (those present at dispatch time). Interleaved-turn entries survive.
|
|
140
|
+
// If no snapshot was recorded (e.g. session.created event never
|
|
141
|
+
// arrived), fall back to draining the entire parent buffer.
|
|
131
142
|
const parentID = childToParent.get(input.sessionID);
|
|
132
143
|
if (parentID) {
|
|
133
|
-
|
|
144
|
+
const snapshot = parentSnapshots.get(input.sessionID);
|
|
145
|
+
if (snapshot) {
|
|
146
|
+
extraction.consumeSnapshot(parentID, snapshot);
|
|
147
|
+
parentSnapshots.delete(input.sessionID);
|
|
148
|
+
} else {
|
|
149
|
+
extraction.consume(parentID);
|
|
150
|
+
}
|
|
134
151
|
missedNudges.delete(parentID);
|
|
135
152
|
}
|
|
136
153
|
return;
|
|
137
154
|
}
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
//
|
|
155
|
+
// Explicit acknowledgment tool drains the buffer without requiring a
|
|
156
|
+
// memory write. The model calls this after dispatching the
|
|
157
|
+
// fact-extractor to a sub-agent.
|
|
141
158
|
if (input.tool === "thatch_extraction_done") {
|
|
142
159
|
extraction.consume(input.sessionID);
|
|
143
160
|
missedNudges.delete(input.sessionID);
|
|
@@ -218,18 +235,23 @@ export const server: Plugin = async ({ client, worktree }) => {
|
|
|
218
235
|
// 5. Session-start reminder, carrying the hygiene heartbeat. Hygiene is
|
|
219
236
|
// best-effort: a failure there must not cost the reminder itself.
|
|
220
237
|
//
|
|
221
|
-
// Also tracks parent-child session relationships for
|
|
222
|
-
//
|
|
223
|
-
//
|
|
238
|
+
// Also tracks parent-child session relationships for cross-session buffer
|
|
239
|
+
// drain. session.created with a parentID records the mapping AND
|
|
240
|
+
// snapshots the parent's current buffer — so the child's later
|
|
241
|
+
// memory_remember drains only those snapshot entries, not the parent's
|
|
242
|
+
// entire buffer (which may have grown from interleaved turns).
|
|
243
|
+
// session.deleted cleans both maps to avoid unbounded growth.
|
|
224
244
|
event: async ({ event }) => {
|
|
225
245
|
if (event.type === "session.created") {
|
|
226
246
|
const info = event.properties.info;
|
|
227
247
|
if (info.parentID) {
|
|
228
248
|
childToParent.set(info.id, info.parentID);
|
|
249
|
+
parentSnapshots.set(info.id, [...extraction.peek(info.parentID)]);
|
|
229
250
|
}
|
|
230
251
|
}
|
|
231
252
|
if (event.type === "session.deleted") {
|
|
232
253
|
childToParent.delete(event.properties.info.id);
|
|
254
|
+
parentSnapshots.delete(event.properties.info.id);
|
|
233
255
|
return;
|
|
234
256
|
}
|
|
235
257
|
|
|
@@ -248,7 +270,7 @@ export const server: Plugin = async ({ client, worktree }) => {
|
|
|
248
270
|
path: { id },
|
|
249
271
|
body: {
|
|
250
272
|
noReply: true,
|
|
251
|
-
parts: [{ type: "text", text: sessionStartReminder(repo, hygiene) }],
|
|
273
|
+
parts: [{ type: "text", text: sessionStartReminder(repo, hygiene), synthetic: true }],
|
|
252
274
|
},
|
|
253
275
|
});
|
|
254
276
|
} catch (err) {
|
package/src/mcp.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { detectRepo } from "./git";
|
|
|
5
5
|
import { checkSetup } from "./setup";
|
|
6
6
|
import { TOOL_DEFS, type CoreContext, type ToolDef } from "./tool-defs";
|
|
7
7
|
import { SidebandServer, sidebandSocketPath } from "./sideband";
|
|
8
|
+
import pkg from "../package.json";
|
|
8
9
|
|
|
9
10
|
// ---------------------------------------------------------------------------
|
|
10
11
|
// JSON-RPC 2.0 types
|
|
@@ -34,7 +35,7 @@ const INTERNAL_ERROR = -32603;
|
|
|
34
35
|
// ---------------------------------------------------------------------------
|
|
35
36
|
|
|
36
37
|
const SERVER_NAME = "thatch";
|
|
37
|
-
const SERVER_VERSION =
|
|
38
|
+
const SERVER_VERSION = pkg.version;
|
|
38
39
|
|
|
39
40
|
/**
|
|
40
41
|
* MCP capabilities declared in the initialize response. Thatch is a tools-only
|