@jeffober/thatch 0.1.40 → 0.1.41
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 +10 -3
- package/artifacts/skills/thatch-code-review.md +4 -4
- package/artifacts/skills/thatch-review-synthesizer.md +8 -8
- package/artifacts/skills/{thatch-review-mentorship.md → thatch-review-technique.md} +9 -10
- package/bin/thatch +160 -16
- package/docs/dev/README.md +26 -14
- package/docs/dev/features/README.md +1 -0
- package/docs/dev/features/cli.md +22 -4
- package/docs/dev/features/cross-session-chat.md +256 -0
- package/docs/dev/features/database.md +52 -4
- package/docs/dev/features/multi-host.md +17 -9
- package/docs/dev/features/setup.md +12 -5
- package/docs/dev/gotchas.md +7 -4
- package/docs/dev/mcp-parity.md +2 -2
- package/docs/dev/setup-and-hooks.md +5 -4
- package/docs/dev/skills.md +5 -4
- package/docs/user/README.md +23 -5
- package/docs/user/cli.md +30 -0
- package/docs/user/code-review.md +1 -1
- package/docs/user/cross-session-chat.md +102 -0
- package/docs/user/setup.md +16 -2
- package/docs/user/skills.md +7 -6
- package/package.json +1 -1
- package/src/chat-names.ts +121 -0
- package/src/chat.ts +879 -0
- package/src/db.ts +179 -0
- package/src/index.ts +113 -5
- package/src/prompts.ts +133 -8
- package/src/setup.ts +83 -29
- package/src/skills.ts +47 -17
- package/src/tool-defs.ts +346 -0
package/docs/dev/features/cli.md
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
# CLI (bin/thatch)
|
|
2
2
|
|
|
3
|
-
The thatch CLI is a Bun script at `bin/thatch`. It provides memory inspection, MCP server startup, hook commands for MCP hosts, and the
|
|
3
|
+
The thatch CLI is a Bun script at `bin/thatch`. It provides memory inspection, MCP server startup, hook commands for MCP hosts, the setup installer, session archaeology over the opencode database, and a read-only window on the cross-session chat directory.
|
|
4
4
|
|
|
5
5
|
## Subcommands
|
|
6
6
|
|
|
7
|
-
15 subcommands:
|
|
8
|
-
|
|
9
7
|
| Command | Args | Flags | Stdin | Purpose |
|
|
10
8
|
|---------|------|-------|-------|---------|
|
|
11
9
|
| `stores` | none | none | none | List all store names |
|
|
@@ -22,6 +20,9 @@ The thatch CLI is a Bun script at `bin/thatch`. It provides memory inspection, M
|
|
|
22
20
|
| `flush-tools [--json]` | none | `--json` | JSON | Peek queue + extraction/recall/prediction/behavior/write nudge |
|
|
23
21
|
| `flush-predictions [--json]` | none | `--json` | JSON | Standalone prediction-only nudge |
|
|
24
22
|
| `setup --claude [--cursor] [--global]` | none | `--claude`, `--cursor`, `--global` | none | Install config + instructions + hooks + skills |
|
|
23
|
+
| `session list/get/transcript/search` | per subcommand | `-s/--session`, `--id`, `--after`, `--before`, `--regex`, `--limit` | none | Read-only archaeology on the opencode session database (JSONL output) |
|
|
24
|
+
| `chat list` | none | none | none | Registered chat sessions: name, human-readable age, project, topic |
|
|
25
|
+
| `chat tail [--once]` | none | `--once` | none | Follow cross-session chat: sent and read events (`--once` prints the backlog and exits) |
|
|
25
26
|
| (unknown) | none | none | none | Print usage, exit 1 |
|
|
26
27
|
|
|
27
28
|
## Global behavior
|
|
@@ -41,6 +42,21 @@ Runs the thatch-project-primer skill via an external CLI. Searches `PATH` in ord
|
|
|
41
42
|
|
|
42
43
|
Inherits stdio, exits with the child's exit code. Errors and exits 1 if none found.
|
|
43
44
|
|
|
45
|
+
## chat
|
|
46
|
+
|
|
47
|
+
Read-only window on the cross-session chat directory ([cross-session-chat.md](cross-session-chat.md)):
|
|
48
|
+
|
|
49
|
+
- `chat list` renders the roster: display name, human-readable age since the
|
|
50
|
+
last heartbeat, project, and topic when set.
|
|
51
|
+
- `chat tail [--once]` follows the message stream. Sent lines have the shape
|
|
52
|
+
`[timestamp] from -> to: body` (broadcast rows render `broadcast` via the
|
|
53
|
+
`via_broadcast` marker); in follow mode, an inbox drain emits a read line
|
|
54
|
+
(`[timestamp] ${reader} read a message from ${sender}: preview` — the
|
|
55
|
+
reader is the recipient draining its inbox). The diff logic is
|
|
56
|
+
`chatTailDiff()`/`formatChatTailEvent()` in `src/chat.ts`, unit-tested
|
|
57
|
+
there; `--once` prints one snapshot and exits, because a snapshot has no
|
|
58
|
+
previous state to diff read events against.
|
|
59
|
+
|
|
44
60
|
## Environment variables
|
|
45
61
|
|
|
46
62
|
| Variable | Purpose | Default |
|
|
@@ -59,7 +75,7 @@ Inherits stdio, exits with the child's exit code. Errors and exits 1 if none fou
|
|
|
59
75
|
|
|
60
76
|
## Source files
|
|
61
77
|
|
|
62
|
-
- `bin/thatch` — the CLI (Bun script
|
|
78
|
+
- `bin/thatch` — the CLI (Bun script)
|
|
63
79
|
- `bin/release` — release helper (bash, separate from the main CLI)
|
|
64
80
|
|
|
65
81
|
## Interactions with other features
|
|
@@ -70,6 +86,8 @@ Inherits stdio, exits with the child's exit code. Errors and exits 1 if none fou
|
|
|
70
86
|
- Extraction ([extraction.md](extraction.md)): `buffer-batch`, `buffer-tool`, `flush-tools` subcommands
|
|
71
87
|
- Hygiene ([hygiene.md](hygiene.md)): `hygiene`, `reminder` subcommands
|
|
72
88
|
- Multi-host ([multi-host.md](multi-host.md)): `mcp` subcommand starts the MCP server for Claude Code and Cursor
|
|
89
|
+
- Session archaeology: the `session` subcommand group (see the table above) reads the opencode session database via `src/session-db.ts`
|
|
90
|
+
- Cross-session chat ([cross-session-chat.md](cross-session-chat.md)): the `chat` subcommand group reads the shared chat directory and inbox
|
|
73
91
|
|
|
74
92
|
## Key invariants
|
|
75
93
|
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
# Cross-Session Chat
|
|
2
|
+
|
|
3
|
+
Cross-session chat lets independent opencode sessions on one machine message
|
|
4
|
+
each other through thatch: a session registers in a shared directory, other
|
|
5
|
+
sessions see it in the list, and messages land in its inbox. An idle
|
|
6
|
+
recipient is woken with a prompt when mail arrives, so coordination does not
|
|
7
|
+
route through the human ("ask the other session whether the release is
|
|
8
|
+
green" instead of "Jeff, go ask").
|
|
9
|
+
|
|
10
|
+
User-facing behavior is documented in
|
|
11
|
+
[docs/user/cross-session-chat.md](../../user/cross-session-chat.md).
|
|
12
|
+
|
|
13
|
+
## What it does
|
|
14
|
+
|
|
15
|
+
- `thatch_chat_register` / `thatch_chat_list` / `thatch_chat_send` /
|
|
16
|
+
`thatch_chat_read` / `thatch_chat_unregister` / `thatch_chat_broadcast`
|
|
17
|
+
tools (shared across hosts; opencode injects identity, MCP hosts
|
|
18
|
+
self-declare with the as argument)
|
|
19
|
+
- A shared SQLite directory and inbox in thatch.db, writable by any opencode
|
|
20
|
+
process on the machine
|
|
21
|
+
- A per-process poller that heartbeats hosted sessions and delivers wake
|
|
22
|
+
prompts via `client.session.promptAsync` with a synthetic part - the same
|
|
23
|
+
mechanism watchers use
|
|
24
|
+
- Anti-loop protection at two levels: prompt guidance (messages are
|
|
25
|
+
informational, no reflexive auto-reply) and a hard per-recipient nudge
|
|
26
|
+
rate cap
|
|
27
|
+
|
|
28
|
+
## The shared-state, local-delivery decision
|
|
29
|
+
|
|
30
|
+
Watchers keep their registry in plugin memory, never SQLite, because the
|
|
31
|
+
process that registered a watcher is the only one that can deliver its
|
|
32
|
+
events (see [watchers.md](watchers.md)). Chat cannot work that way: the
|
|
33
|
+
sender's process and the recipient's process are usually different, so the
|
|
34
|
+
state they coordinate through must be shared.
|
|
35
|
+
|
|
36
|
+
The split:
|
|
37
|
+
|
|
38
|
+
- **State is shared.** `chat_sessions` and `chat_messages` live in thatch.db.
|
|
39
|
+
Every opencode process on the machine reads and writes the same tables.
|
|
40
|
+
Concurrent access is the already-solved case - multiple sessions share the
|
|
41
|
+
DB for memories today, under WAL plus a busy timeout.
|
|
42
|
+
- **Delivery is local.** Each process polls the inbox for messages addressed
|
|
43
|
+
to sessions it hosts (those it has seen `session.status` events for) and
|
|
44
|
+
prompts them through its own SDK client. A sender never prompts a session
|
|
45
|
+
in another process.
|
|
46
|
+
|
|
47
|
+
This preserves the watcher rationale's core property - no process prompts a
|
|
48
|
+
session it does not host - so each message has exactly one possible
|
|
49
|
+
deliverer. There is no claiming logic, no double-delivery race, and no
|
|
50
|
+
orphaned delivery state. The cost is the mirror of watchers': chat state
|
|
51
|
+
outlives a process, so liveness needs a signal beyond process lifetime.
|
|
52
|
+
|
|
53
|
+
## How it works
|
|
54
|
+
|
|
55
|
+
### Registration and identity
|
|
56
|
+
|
|
57
|
+
`chat_register` joins the directory. Without a name it draws one at random
|
|
58
|
+
from the built-in pool (`src/chat-names.ts`): whimsical geek-culture names
|
|
59
|
+
in the style of fnord's Nomenclater, statically baked in so assignment never
|
|
60
|
+
costs a model call. Pool draws redraw on the rare collision a concurrent
|
|
61
|
+
registration can cause, and skip any name a session already claimed by
|
|
62
|
+
custom registration - so the pool is the recommended path. With a name,
|
|
63
|
+
the session claims it as a custom name -
|
|
64
|
+
uniqueness is case-insensitive ("Landru" and "landru" are one name), so two
|
|
65
|
+
visually identical identities cannot coexist, and message addressing follows
|
|
66
|
+
the same rule. Databases created before the case-insensitive constraint are
|
|
67
|
+
rebuilt at schema init (first row wins per case group; message history
|
|
68
|
+
survives because the endpoints are not foreign keys).
|
|
69
|
+
|
|
70
|
+
Identity is the host session ID, which the model cannot know or forge.
|
|
71
|
+
Re-registering renames. Unregistered sessions are invisible and
|
|
72
|
+
unmessageable in both directions. Only top-level sessions should register -
|
|
73
|
+
sub-agent children are ephemeral - which the tool descriptions and system
|
|
74
|
+
prompt state; there is no mechanical barrier, a documented trust posture.
|
|
75
|
+
|
|
76
|
+
Names are claimable by any session (no impersonation defense). The trust
|
|
77
|
+
model is the same as the shared memory stores: every agent on this machine
|
|
78
|
+
is the operator's agent.
|
|
79
|
+
|
|
80
|
+
### Topics
|
|
81
|
+
|
|
82
|
+
The optional topic is the roster's "who is working on what" signal: with a
|
|
83
|
+
dozen registered sessions, a name like "Kurn the Typechecker" says nothing,
|
|
84
|
+
but `topic:QAing the release` in `chat_list` tells a coordinator which
|
|
85
|
+
session to address. Topics are free text, sanitized to one roster line
|
|
86
|
+
(whitespace runs collapse, 80-character cap, advisory - oversizing degrades
|
|
87
|
+
rather than errors). The null/empty distinction is deliberate: omitting the
|
|
88
|
+
topic on re-register keeps the existing one; an empty topic clears it.
|
|
89
|
+
Broadcast senders benefit most: `chat_broadcast` answers "which of you is
|
|
90
|
+
working on X?" with the roster already in hand.
|
|
91
|
+
|
|
92
|
+
### Messages
|
|
93
|
+
|
|
94
|
+
`chat_send` addresses a recipient by display name or session ID. Both
|
|
95
|
+
endpoints must be registered and distinct. `chat_read` drains the calling
|
|
96
|
+
session's inbox oldest-first and stamps rows read. Message history survives
|
|
97
|
+
unregistration; a departed sender degrades to an unknown name in the
|
|
98
|
+
reader's view (the endpoints are deliberately not foreign keys - leaving the
|
|
99
|
+
directory must not be blocked by history).
|
|
100
|
+
|
|
101
|
+
`chat_broadcast` posts one message to every other registered session at
|
|
102
|
+
once, one inbox row per recipient - the existing wake machinery (grouping,
|
|
103
|
+
gating, rate cap) treats each as ordinary mail. Stale sessions are skipped
|
|
104
|
+
and reported rather than messaged: a host that has stopped heartbeat-ing
|
|
105
|
+
will never read the mail, and dead mail to a dead process is just clutter.
|
|
106
|
+
The sender is excluded. It is a separate tool rather than a magic
|
|
107
|
+
`chat_send` recipient because "send to all" changes the behavior (fan-out,
|
|
108
|
+
stale skipping, no address resolution), and a function that changes
|
|
109
|
+
behavior drastically on a parameter value is two functions.
|
|
110
|
+
|
|
111
|
+
### Polling, heartbeat, staleness
|
|
112
|
+
|
|
113
|
+
A `setInterval` loop (default 30s) runs one cycle per process: heartbeat the
|
|
114
|
+
hosted sessions' `last_seen`, then deliver. A crashed process fires no
|
|
115
|
+
`session.deleted`, so `chat_list` marks sessions whose `last_seen` is older
|
|
116
|
+
than the staleness threshold (default 10 minutes) as stale rather than
|
|
117
|
+
hiding them - the operator decides what to do with ghosts. `session.deleted`
|
|
118
|
+
is the graceful-exit fast path that unregisters immediately.
|
|
119
|
+
|
|
120
|
+
### Delivery, re-nudge, and the rate cap
|
|
121
|
+
|
|
122
|
+
A message needs a wake prompt when it is unread and either never delivered
|
|
123
|
+
or delivered more than the re-nudge window ago (default 15 minutes).
|
|
124
|
+
Delivery is gated by the same `canDeliver` predicate watchers use - the
|
|
125
|
+
recipient must be idle and not compacting - and the idle event handler
|
|
126
|
+
flushes pending mail directly so it lands promptly instead of waiting for
|
|
127
|
+
the next cycle. Failed deliveries stay pending and retry. Only registered
|
|
128
|
+
recipients are ever selected: unregistering stops wake prompts for
|
|
129
|
+
kept-but-unread mail, which is the `chat_unregister` tool's promise.
|
|
130
|
+
Delivery is at-least-once - a crash after the wake prompt but before the
|
|
131
|
+
delivered_at stamp re-nudges the same batch on the next cycle.
|
|
132
|
+
|
|
133
|
+
One wake prompt covers all of a recipient's pending messages: sender names
|
|
134
|
+
and a count, pointer-only like watcher notifications, with the model calling
|
|
135
|
+
`chat_read` for content. On success the poller stamps `delivered_at`
|
|
136
|
+
(re-stamping on re-nudges resets the re-nudge timer).
|
|
137
|
+
|
|
138
|
+
The hard brake: at most a fixed number of wake prompts per recipient per
|
|
139
|
+
hour (default 6, in-memory on purpose - it is a loop guard, not accounting).
|
|
140
|
+
Without it, two agents politely acknowledging each other would ping-pong
|
|
141
|
+
forever, each wake triggering a reply that wakes the other.
|
|
142
|
+
|
|
143
|
+
### Transcript echo
|
|
144
|
+
|
|
145
|
+
Plugin tools render in the opencode TUI as muted one-line generic entries,
|
|
146
|
+
with the output block behind a default-off toggle - so without help, a chat
|
|
147
|
+
exchange is invisible to the human watching the session. The plugin's
|
|
148
|
+
`tool.execute.after` hook builds a short echo for the conversational events
|
|
149
|
+
(`chatEchoText()` in `src/prompts.ts`: register, send, read, broadcast) and
|
|
150
|
+
delivers it as a non-synthetic, `noReply` promptAsync part: rendered as a
|
|
151
|
+
visible bubble in the transcript, no model turn started (the server's
|
|
152
|
+
prompt path returns before the completion loop). Failed calls never echo;
|
|
153
|
+
`chat_list` and `chat_unregister` stay on the muted tool line. Broadcasts
|
|
154
|
+
echo their fan-out count with the clipped body.
|
|
155
|
+
|
|
156
|
+
The trade-off: non-synthetic is what makes the TUI render the part, and it
|
|
157
|
+
also means later turns see the echo in context - a small duplication of the
|
|
158
|
+
tool call it mirrors, accepted for visibility. Echo bodies are clipped
|
|
159
|
+
(send: the body; read: the formatted inbox; broadcast: the body) so a
|
|
160
|
+
bubble stays cheap. Echo delivery is fire-and-forget: a failure must never
|
|
161
|
+
fail the tool call it follows. Because the opencode server fires the
|
|
162
|
+
`chat.message` hook for every prompt part before the `noReply`
|
|
163
|
+
early-return, the plugin's `chat.message` handler skips messages whose
|
|
164
|
+
visible text is entirely `[chat]`-prefixed bubbles (`isChatEchoParts`) -
|
|
165
|
+
otherwise every echo would run the nudge machinery for a message no model
|
|
166
|
+
turn reads. The accepted edge: a real user message whose every visible
|
|
167
|
+
part starts with the prefix is skipped the same way, losing that one turn's
|
|
168
|
+
advisory nudges.
|
|
169
|
+
|
|
170
|
+
### CLI access
|
|
171
|
+
|
|
172
|
+
The user watches the conversation without an agent: `thatch chat list`
|
|
173
|
+
renders the roster with human-readable heartbeat ages, and
|
|
174
|
+
`thatch chat tail [--once]` follows the message stream. The tail diffs
|
|
175
|
+
`ChatStore.messageFeed()` snapshots via `chatTailDiff()` (unit-tested in
|
|
176
|
+
`tests/chat.test.ts`): sent lines on first view, then new sends and newly-
|
|
177
|
+
read messages per poll. `chat_messages.via_broadcast` marks fan-out rows so
|
|
178
|
+
the tail renders one `-> broadcast` line per recipient instead of what
|
|
179
|
+
would otherwise look like identical direct sends. The CLI takes no chat
|
|
180
|
+
write actions (the wake machinery owns those paths); the only writes it
|
|
181
|
+
can trigger are the schema migrations that run whenever any thatch
|
|
182
|
+
process opens the database.
|
|
183
|
+
|
|
184
|
+
## Interactions with other features
|
|
185
|
+
|
|
186
|
+
- Watchers ([watchers.md](watchers.md)): chat reuses the delivery gate
|
|
187
|
+
(`canDeliver` over `sessionStatus` + `compacting`), the idle-flush
|
|
188
|
+
pattern, and the synthetic-part + toast notification shape. The state
|
|
189
|
+
model is the deliberate opposite - shared SQLite vs process memory - and
|
|
190
|
+
each feature's doc explains why its side of the split is right for its
|
|
191
|
+
problem.
|
|
192
|
+
- Session lifecycle ([session-lifecycle.md](session-lifecycle.md)):
|
|
193
|
+
`session.status` feeds the delivery gate and the hosted-session set;
|
|
194
|
+
`session.deleted` unregisters.
|
|
195
|
+
- Extraction pipeline ([extraction.md](extraction.md)): chat tool calls
|
|
196
|
+
are `thatch_*` tools and excluded from buffering like all thatch tools;
|
|
197
|
+
non-thatch tool calls made during a chat notification turn are buffered
|
|
198
|
+
and extracted like any other turn's.
|
|
199
|
+
- Multi-host ([multi-host.md](multi-host.md)): the chat tools are shared -
|
|
200
|
+
MCP hosts register with a self-declared identity (the `as` argument;
|
|
201
|
+
the stored session ID is synthetic, `mcp_<hash of name>`), and pending
|
|
202
|
+
mail surfaces at prompt time through the flush-tools hook line and
|
|
203
|
+
`chat_status`. Wake-up delivery remains opencode-only: no MCP server
|
|
204
|
+
can start a turn in the client's conversation.
|
|
205
|
+
|
|
206
|
+
## Multi-host delivery tiers
|
|
207
|
+
|
|
208
|
+
Chat works on every host. Earlier designs for the MCP path - a JSONL
|
|
209
|
+
temp-file inbox, a socket or daemon - were rejected: the DB already is the
|
|
210
|
+
inbox, and MCP hosts cannot hold connections across turns, so both
|
|
211
|
+
degenerate to the turn-granularity polling the tools already provide. The
|
|
212
|
+
wake analog is the existing hook channel.
|
|
213
|
+
|
|
214
|
+
| Host | Send/receive | New-mail signal | Echo bubbles |
|
|
215
|
+
|-------------|--------------|------------------------------|--------------|
|
|
216
|
+
| opencode | yes | promptAsync wake (idle-gated)| yes |
|
|
217
|
+
| claude code | yes | flush-tools hook line | no |
|
|
218
|
+
| cursor | yes | flush-tools hook line | no |
|
|
219
|
+
|
|
220
|
+
MCP identity is the registered name: `chat_register` without a host
|
|
221
|
+
session stores a synthetic `mcp_<sha256(name)[0:12]>` session ID, so the
|
|
222
|
+
identity is stable across the ephemeral sessions those harnesses run.
|
|
223
|
+
`chat_status` is the quiet check (registered flag + pending/total); the
|
|
224
|
+
flush-tools hook line names the addressee sessions and prints nothing
|
|
225
|
+
when the project has no pending chat - absence is silent by construction.
|
|
226
|
+
Staleness semantics differ by kind: an opencode row's stale age means the
|
|
227
|
+
process is gone (broadcast skips it), while an mcp row's age only means
|
|
228
|
+
"between turns" (broadcast always delivers).
|
|
229
|
+
|
|
230
|
+
## Defaults
|
|
231
|
+
|
|
232
|
+
Timing constants live in `src/chat.ts`: poll interval 30s, staleness
|
|
233
|
+
threshold 10 minutes, re-nudge window 15 minutes, nudge cap 6 per recipient
|
|
234
|
+
per hour. There are no environment overrides yet; add them the way
|
|
235
|
+
`THATCH_WATCH_POLL_SECONDS` works if a user needs them.
|
|
236
|
+
|
|
237
|
+
## Source files
|
|
238
|
+
|
|
239
|
+
- `src/chat.ts` - ChatStore (directory + inbox SQL, pool assignment),
|
|
240
|
+
ChatPoller (heartbeat, gated delivery, re-nudge, rate cap), staleness
|
|
241
|
+
helper
|
|
242
|
+
- `src/chat-names.ts` - the static display-name pool (nomenclater style)
|
|
243
|
+
- `src/db.ts` - the chat tables in schema init, the NOCASE collation
|
|
244
|
+
migration and topic column migration, delegated methods
|
|
245
|
+
- `src/tool-defs.ts` - the chat tool definitions
|
|
246
|
+
- `src/index.ts` - poller construction, delivery closure, idle flush,
|
|
247
|
+
session.deleted unregister, dispose, transcript echo in
|
|
248
|
+
tool.execute.after
|
|
249
|
+
- `src/prompts.ts` - `chatNotificationNudge()`, `chatEchoText()` /
|
|
250
|
+
`isChatEchoParts()`, system prompt Cross-Session Chat section, MCP
|
|
251
|
+
absent-tools note
|
|
252
|
+
- `tests/chat.test.ts` - store, pool, migration, echo-text, and poller unit
|
|
253
|
+
tests (temp-dir SQLite, injected delivery)
|
|
254
|
+
- `tests/qa/auto/uc-097-chat.ts` - full lifecycle against a mocked poller
|
|
255
|
+
- `tests/qa/live/uc-098-chat-cross-session.ts` - two real sessions exchange
|
|
256
|
+
a message through the shared DB
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Database
|
|
2
2
|
|
|
3
|
-
Thatch stores all data in a single SQLite file. The schema is organized around
|
|
3
|
+
Thatch stores all data in a single SQLite file. The schema is organized around feature concerns: memory entries, the prediction engine, the behavior engine, and cross-session chat.
|
|
4
4
|
|
|
5
5
|
## Configuration
|
|
6
6
|
|
|
@@ -10,13 +10,14 @@ Thatch stores all data in a single SQLite file. The schema is organized around t
|
|
|
10
10
|
|
|
11
11
|
## Schema overview
|
|
12
12
|
|
|
13
|
-
The tables fall into
|
|
13
|
+
The tables fall into these groups:
|
|
14
14
|
|
|
15
15
|
| Group | Tables | Purpose |
|
|
16
16
|
|-------|--------|---------|
|
|
17
17
|
| Memory | `stores`, `entries`, `dedup_pairs` | Core memory CRUD + dedup verdicts |
|
|
18
18
|
| Prediction engine | `prediction_matchers`, `predictions`, `prediction_edges`, `prediction_provenance` | User decision model |
|
|
19
19
|
| Behavior engine | `behavior_matchers`, `behaviors`, `behavior_edges`, `behavior_provenance` | LLM self-discipline rules |
|
|
20
|
+
| Cross-session chat | `chat_sessions`, `chat_messages` | Session directory + message inbox (shared across opencode processes) |
|
|
20
21
|
|
|
21
22
|
The prediction and behavior engines share the same four-table shape (matchers, items, edges, provenance) with different table names. They are separate because the semantics differ: predictions model what the user wants; behaviors model what the LLM should do.
|
|
22
23
|
|
|
@@ -213,6 +214,52 @@ behavior_provenance(
|
|
|
213
214
|
|
|
214
215
|
- `signal`: "confirm" (ham), "disconfirm" (spam), or "codify".
|
|
215
216
|
|
|
217
|
+
## Cross-session chat tables
|
|
218
|
+
|
|
219
|
+
### chat_sessions
|
|
220
|
+
|
|
221
|
+
```sql
|
|
222
|
+
chat_sessions(
|
|
223
|
+
session_id TEXT PRIMARY KEY,
|
|
224
|
+
name TEXT NOT NULL UNIQUE COLLATE NOCASE,
|
|
225
|
+
topic TEXT,
|
|
226
|
+
project TEXT,
|
|
227
|
+
registered_at TEXT NOT NULL,
|
|
228
|
+
last_seen TEXT NOT NULL
|
|
229
|
+
)
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
- The machine-wide session directory. `last_seen` is the heartbeat: each
|
|
233
|
+
host process refreshes it for its own sessions every poll cycle, and a
|
|
234
|
+
stale value marks a crashed or closed process. Name uniqueness is
|
|
235
|
+
case-insensitive; databases whose chat_sessions table predates the NOCASE
|
|
236
|
+
constraint are rebuilt at schema init (first row wins per case group).
|
|
237
|
+
|
|
238
|
+
### chat_messages
|
|
239
|
+
|
|
240
|
+
```sql
|
|
241
|
+
chat_messages(
|
|
242
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
243
|
+
from_session TEXT NOT NULL,
|
|
244
|
+
to_session TEXT NOT NULL,
|
|
245
|
+
body TEXT NOT NULL,
|
|
246
|
+
created_at TEXT NOT NULL,
|
|
247
|
+
delivered_at TEXT,
|
|
248
|
+
read_at TEXT,
|
|
249
|
+
via_broadcast INTEGER NOT NULL DEFAULT 0
|
|
250
|
+
)
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
- `delivered_at` is the last wake-prompt stamp (restamped on re-nudges);
|
|
254
|
+
`read_at` is set when the recipient drains its inbox. `via_broadcast`
|
|
255
|
+
marks rows a chat_broadcast fan-out created (the CLI tail renders them
|
|
256
|
+
as `-> broadcast`). The endpoints are
|
|
257
|
+
plain columns, not foreign keys: unregistering a session must not be
|
|
258
|
+
blocked by message history, and a departed sender degrades to an unknown
|
|
259
|
+
name in the reader's view.
|
|
260
|
+
|
|
261
|
+
See [cross-session-chat.md](cross-session-chat.md) for the delivery model.
|
|
262
|
+
|
|
216
263
|
## Schema migration
|
|
217
264
|
|
|
218
265
|
`recall_count`, `last_recalled_at`, and `archived` are added to pre-existing databases by an idempotent column migration at init. The migration uses `PRAGMA table_info` to check for the column's existence, then `ALTER TABLE ADD COLUMN` if missing. This handles databases created before these columns existed.
|
|
@@ -235,8 +282,9 @@ Embeddings are raw Float32Array bytes stored as BLOBs. Serialization honors `byt
|
|
|
235
282
|
|
|
236
283
|
- Memory store ([memory-store.md](memory-store.md)): `entries` table is the core; `stores` table is the registry
|
|
237
284
|
- Deduplication ([deduplication.md](deduplication.md)): `dedup_pairs` table tracks reviewed pairs
|
|
238
|
-
- Prediction engine ([prediction-engine.md](prediction-engine.md)):
|
|
239
|
-
- Behavior engine ([behavior-engine.md](behavior-engine.md)):
|
|
285
|
+
- Prediction engine ([prediction-engine.md](prediction-engine.md)): the prediction tables
|
|
286
|
+
- Behavior engine ([behavior-engine.md](behavior-engine.md)): the behavior tables
|
|
287
|
+
- Cross-session chat ([cross-session-chat.md](cross-session-chat.md)): the chat tables, shared across opencode processes
|
|
240
288
|
- Hygiene ([hygiene.md](hygiene.md)): `staleEntryCount` uses `recall_count` and `last_recalled_at`; `branchesInStore` uses `branch` column
|
|
241
289
|
|
|
242
290
|
## Source files
|
|
@@ -16,11 +16,16 @@ Thatch supports three host agents from a single codebase:
|
|
|
16
16
|
- **Cursor MCP**---same stdio MCP server, `--json` hook output format,
|
|
17
17
|
`mcp__thatch__` tool prefix, system prompt in AGENTS.md
|
|
18
18
|
|
|
19
|
-
All three paths share the tool definitions from `src/tool-defs.ts`:
|
|
20
|
-
host-agnostic tools plus opencode-only tools
|
|
21
|
-
|
|
22
|
-
`
|
|
23
|
-
|
|
19
|
+
All three paths share the tool definitions from `src/tool-defs.ts`: the
|
|
20
|
+
host-agnostic tools plus the opencode-only tools, which the MCP server
|
|
21
|
+
filters out. The opencode-only set covers host-session capabilities MCP
|
|
22
|
+
hosts lack: `get_session_info`, `session_search`/`session_get` (no session
|
|
23
|
+
concept or session database), the `watch_create`/`watch_branch_create`/
|
|
24
|
+
`watch_list`/`watch_cancel` watcher tools (no poller or proactive-prompt
|
|
25
|
+
channel), and the `chat_register`/`chat_list`/`chat_send`/`chat_read`/
|
|
26
|
+
`chat_unregister`/`chat_broadcast` chat tools (no session identity or
|
|
27
|
+
wake-up channel). The
|
|
28
|
+
shared tools take args and a `CoreContext`
|
|
24
29
|
(`{ db, model, defaultStore }`) and return a string. The host-specific
|
|
25
30
|
wrapping---prefixing, transport, session hooks---lives outside the core.
|
|
26
31
|
|
|
@@ -74,9 +79,10 @@ at session start.
|
|
|
74
79
|
so the agent sees `mcp__thatch__memory_remember`. Thatch itself does not apply
|
|
75
80
|
this prefix.
|
|
76
81
|
|
|
77
|
-
**Skills**: installed to
|
|
78
|
-
|
|
79
|
-
|
|
82
|
+
**Skills**: installed to the scope's skills dir (repo `.claude/skills/` for
|
|
83
|
+
project-local setup, `$CLAUDE_CONFIG_DIR/skills/` for `--global`)---shared
|
|
84
|
+
skills only. The code-review coordinator skill is opencode-only because it
|
|
85
|
+
requires sub-agent dispatch, which Claude Code does not support.
|
|
80
86
|
|
|
81
87
|
**Session behavior**: driven by external CLI hook processes (`bin/thatch`).
|
|
82
88
|
The hooks are registered in `.claude/settings.json` during setup. Each hook
|
|
@@ -182,6 +188,8 @@ behavior, with some features having no MCP counterpart.
|
|
|
182
188
|
| Tool batching | N/A (in-process) | PostToolBatch (batch) | postToolUse (per-tool) |
|
|
183
189
|
| `get_session_info` (session identity) | Yes | No | No |
|
|
184
190
|
| `session_search` / `session_get` (conversation archaeology) | Yes (tools) | CLI only (`thatch session ...`) | CLI only (`thatch session ...`) |
|
|
191
|
+
| `watch_*` (PR and branch watchers) | Yes (tools) | No | No |
|
|
192
|
+
| `chat_*` (cross-session chat) | Yes | No | No |
|
|
185
193
|
|
|
186
194
|
For the full parity matrix, see [../mcp-parity.md](../mcp-parity.md).
|
|
187
195
|
|
|
@@ -231,4 +239,4 @@ the opencode-only skills require sub-agent dispatch or in-process hooks.
|
|
|
231
239
|
- The MCP server always exposes bare names. The `mcp__thatch__` prefix is applied by the MCP client, not thatch.
|
|
232
240
|
- The three system prompt variants are independent string constants in `src/prompts.ts` with no shared template. Editing shared prose in one requires mirroring in the other two, or they drift.
|
|
233
241
|
- The tool list line in all three prompts must match `TOOL_DEFS` in `src/tool-defs.ts` (opencode's list includes opencode-only tools; the MCP lists exclude them). The historical failure mode: a tool is added to `TOOL_DEFS` but only two of three prompts are updated.
|
|
234
|
-
- opencode-only features (direct extraction, compaction recovery, code-review coordinator, background sub-agents, TUI toasts, `get_session_info`, `session_search`/`session_get`, PR watchers) have no MCP counterpart. The `thatch session` CLI subcommands work anywhere - they only need an opencode.db file to read.
|
|
242
|
+
- opencode-only features (direct extraction, compaction recovery, code-review coordinator, background sub-agents, TUI toasts, `get_session_info`, `session_search`/`session_get`, PR watchers, cross-session chat) have no MCP counterpart. The `thatch session` CLI subcommands work anywhere - they only need an opencode.db file to read.
|
|
@@ -52,8 +52,9 @@ Four artifacts, all idempotent:
|
|
|
52
52
|
→ `thatch reminder`, `PostToolBatch` → `thatch buffer-batch`,
|
|
53
53
|
`UserPromptSubmit` → `thatch flush-tools`. Nested format:
|
|
54
54
|
`{hooks:{Event:{hooks:[{type,command}]}}}`.
|
|
55
|
-
4. **Skills** --- installs to
|
|
56
|
-
|
|
55
|
+
4. **Skills** --- installs to the scope's skills dir: the repo's
|
|
56
|
+
`.claude/skills/` for project-local, `$CLAUDE_CONFIG_DIR/skills/` for
|
|
57
|
+
`--global`. `SHARED_SKILLS` only --- the
|
|
57
58
|
code-review coordinator requires sub-agent dispatch, which Claude Code
|
|
58
59
|
does not support.
|
|
59
60
|
|
|
@@ -73,7 +74,9 @@ Four artifacts, same idempotent pattern:
|
|
|
73
74
|
`{version:1, hooks:{event:[{command}]}}`. Three hooks: `sessionStart`
|
|
74
75
|
→ `thatch reminder --json`, `postToolUse` → `thatch buffer-tool`,
|
|
75
76
|
`beforeSubmitPrompt` → `thatch flush-tools --json`.
|
|
76
|
-
4. **Skills** --- installs to
|
|
77
|
+
4. **Skills** --- installs to the scope's skills dir: the repo's
|
|
78
|
+
`.cursor/skills/` for project-local, `$CURSOR_CONFIG_DIR/skills/` for
|
|
79
|
+
`--global` (shared only).
|
|
77
80
|
|
|
78
81
|
### Marker system (`appendBlock`)
|
|
79
82
|
|
|
@@ -184,8 +187,12 @@ example, an older `thatch echo` hook is replaced with the current
|
|
|
184
187
|
1. **All operations are idempotent.** Re-running setup updates drifted content
|
|
185
188
|
without clobbering. Instructions use markers, hooks filter by `thatch`
|
|
186
189
|
string, MCP config preserves existing servers, skills diff before writing.
|
|
187
|
-
2. **Skills
|
|
188
|
-
|
|
190
|
+
2. **Skills follow the setup scope.** Project-local installs write skills to
|
|
191
|
+
the repo's `.claude/skills/` / `.cursor/skills/` (Claude Code and Cursor
|
|
192
|
+
both discover project skills there); `--global` writes to the user config
|
|
193
|
+
dirs. Setup reports the directory plus added/updated/removed/unchanged
|
|
194
|
+
counts, and notes (without touching) any thatch skills found in the
|
|
195
|
+
opposite scope.
|
|
189
196
|
3. **`appendBlock` leaves content alone if markers do not parse.** A start
|
|
190
197
|
marker without an end marker means the file was edited externally. Writing
|
|
191
198
|
would risk clobbering content after the start marker.
|
package/docs/dev/gotchas.md
CHANGED
|
@@ -124,10 +124,13 @@ here first. These are the things that have already cost time.
|
|
|
124
124
|
|
|
125
125
|
## Setup
|
|
126
126
|
|
|
127
|
-
- **Skills
|
|
128
|
-
`.claude/
|
|
129
|
-
|
|
130
|
-
|
|
127
|
+
- **Skills follow the setup scope.** Project-local installs write skills to
|
|
128
|
+
the repo's `.claude/skills/` / `.cursor/skills/` (they version with the
|
|
129
|
+
project); `--global` writes to `$CLAUDE_CONFIG_DIR/skills/` /
|
|
130
|
+
`~/.cursor/skills/`. A run never touches the opposite scope: if thatch
|
|
131
|
+
skills already exist there (e.g. user-scope copies from an older local
|
|
132
|
+
setup), setup reports them in a note but leaves them alone, so they can
|
|
133
|
+
drift stale until refreshed or deleted.
|
|
131
134
|
- **`appendBlock` leaves content alone if the markers don't parse.** If the
|
|
132
135
|
start marker is found but the end marker isn't, the whole block is skipped
|
|
133
136
|
rather than half-replaced. Fix the markers or delete the block manually.
|
package/docs/dev/mcp-parity.md
CHANGED
|
@@ -18,7 +18,7 @@ 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** (single source of truth) | Plugin tool registration, `thatch_` prefix | MCP `tools/list` + `tools/call`, `mcp__thatch__` prefix (opencode-only tools filtered out) | MCP `tools/list` + `tools/call`, `mcp__thatch__` prefix (opencode-only tools filtered out) | **Full** for the shared tools; `get_session_info` (session identity) and `session_search`/`session_get` (conversation archaeology) are opencode-only — MCP hosts have no session concept. The `thatch session` CLI subcommands cover the same archaeology on any host with an opencode.db file. The `watch_*` tools (PR and branch watchers) are opencode-only — MCP hosts have no poller or proactive-prompt channel |
|
|
21
|
+
| **Tools** (single source of truth) | Plugin tool registration, `thatch_` prefix | MCP `tools/list` + `tools/call`, `mcp__thatch__` prefix (opencode-only tools filtered out) | MCP `tools/list` + `tools/call`, `mcp__thatch__` prefix (opencode-only tools filtered out) | **Full** for the shared tools; `get_session_info` (session identity) and `session_search`/`session_get` (conversation archaeology) are opencode-only — MCP hosts have no session concept. The `thatch session` CLI subcommands cover the same archaeology on any host with an opencode.db file. The `watch_*` tools (PR and branch watchers) are opencode-only — MCP hosts have no poller or proactive-prompt channel. The `chat_*` tools (cross-session chat) are opencode-only — no host session identity and no wake-up channel |
|
|
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 |
|
|
@@ -27,7 +27,7 @@ This document maps the feature parity and documents the remaining gaps.
|
|
|
27
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 |
|
|
28
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 |
|
|
29
29
|
| **Behavior auto-fire** (self-discipline rules) | `chat.message` hook scores the prompt embedding against behavior matchers; injects a `[thatch] Situational behaviors` nudge (separate synthetic part, same embedding call) | `UserPromptSubmit` hook → `thatch flush-tools` fires behavior query via the sideband socket's `behaviors` method in parallel with recall and predictions | `beforeSubmitPrompt` hook → `thatch flush-tools --json` (same sideband path) | **Full** — same `scoreBehaviorNudge` entry point in both paths |
|
|
30
|
-
| **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)
|
|
30
|
+
| **Skills** | Installed to `$XDG_CONFIG_HOME/opencode/skills` at plugin init (shared **+** opencode-only) | Installed by `thatch setup` (shared only) to the repo's `.claude/skills/` (local) or `$CLAUDE_CONFIG_DIR/skills/` (global) | Installed by `thatch setup` (shared only) to the repo's `.cursor/skills/` (local) or `~/.cursor/skills/` (global) | **Full** — same SKILL.md format; the code-review coordinator is opencode-only (needs sub-agents) |
|
|
31
31
|
| **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** |
|
|
32
32
|
| **Setup detection at startup** | n/a — plugin auto-installs at init | `checkSetup` in MCP server: detects missing or broken instructions in CLAUDE.md | Same as Claude Code (checks AGENTS.md) | **Full** — warns the agent to tell the user to run `thatch setup` |
|
|
33
33
|
| **Hook config format** | n/a — plugin registers hooks in code | Nested: `.claude/settings.json` `{hooks:{Event:{hooks:[{type,command}]}}}` | Flat: `.cursor/hooks.json` `{version:1,hooks:{event:[{command}]}}` | n/a |
|
|
@@ -49,11 +49,12 @@ the TUI is not connected (headless mode).
|
|
|
49
49
|
| MCP config | `.mcp.json` (`mcpServers.thatch`, stdio, `["mcp"]`) | none — prints `claude mcp add --scope user thatch -- <bin> mcp` |
|
|
50
50
|
| Instructions | `CLAUDE.md` (idempotent `appendBlock`) | `$CLAUDE_CONFIG_DIR/CLAUDE.md` |
|
|
51
51
|
| Hooks | `.claude/settings.json` | `$CLAUDE_CONFIG_DIR/settings.json` |
|
|
52
|
-
| Skills |
|
|
52
|
+
| Skills | `.claude/skills/` (in the repo) | `$CLAUDE_CONFIG_DIR/skills/` |
|
|
53
53
|
|
|
54
54
|
`CLAUDE_CONFIG_DIR` overrides the default `~/.claude` for all user-scoped
|
|
55
|
-
paths. Project-local keeps `.mcp.json`, `CLAUDE.md`,
|
|
56
|
-
|
|
55
|
+
paths. Project-local keeps everything in the repo: `.mcp.json`, `CLAUDE.md`,
|
|
56
|
+
`.claude/settings.json`, and `.claude/skills/` (so skills version with the
|
|
57
|
+
project and every contributor gets them).
|
|
57
58
|
|
|
58
59
|
### Hook events (nested `settings.json`)
|
|
59
60
|
|
|
@@ -88,7 +89,7 @@ Cursor uses the same stdio MCP server as Claude Code; only the hooks differ.
|
|
|
88
89
|
| MCP config | `.cursor/mcp.json` | `$CURSOR_CONFIG_DIR/mcp.json` (or `~/.cursor/mcp.json`) |
|
|
89
90
|
| Instructions | `AGENTS.md` (idempotent `appendBlock`, Cursor-marked) | `$CURSOR_CONFIG_DIR/AGENTS.md` |
|
|
90
91
|
| Hooks | `.cursor/hooks.json` (flat) | `$CURSOR_CONFIG_DIR/hooks.json` |
|
|
91
|
-
| Skills |
|
|
92
|
+
| Skills | `.cursor/skills/` (in the repo) | `$CURSOR_CONFIG_DIR/skills/` |
|
|
92
93
|
|
|
93
94
|
Cursor has no documented env override like `CLAUDE_CONFIG_DIR`; `CURSOR_CONFIG_DIR`
|
|
94
95
|
is honored for symmetry and forward-compatibility. Cursor has no equivalent of
|
package/docs/dev/skills.md
CHANGED
|
@@ -46,7 +46,7 @@ description: Extract durable project facts ... Use when ...
|
|
|
46
46
|
| `thatch-review-breadcrumbs` | Comment narrative: do comments form a coherent outline of behavior? |
|
|
47
47
|
| `thatch-review-mark-and-sweep` | Mechanical change completeness: whole-repo sweep for stragglers after renames, flag removals, API substitutions. |
|
|
48
48
|
| `thatch-review-highlights` | Positive finding detection: notably clever solutions, cleanup done along the way, documentation that helps. Medium-high bar against generic praise. |
|
|
49
|
-
| `thatch-review-
|
|
49
|
+
| `thatch-review-technique` | Technique lens: teaching-grade notes on unseen helpers/internal packages, companion techniques, house patterns, test-craft, API-shape principles, next-reader discoverability. Informational only, verified citations, never blocking. |
|
|
50
50
|
| `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. |
|
|
51
51
|
| `thatch-review-context` | Gather project/feature context (PR descriptions, git archaeology, ticket references, linked docs/tickets followed from the change, memory) before fan-out. Prevents false positives about intentionally deferred work, and checks whether main moved past the merge-base touching the same paths (staleness signal for the synthesizer). 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. |
|
|
52
52
|
| `thatch-code-archaeology` | Investigate an existing feature, debug an unfamiliar area, or begin a new ticket. Explores the code base from multiple angles (data model, state flow, git history, sibling features, skeletons) before proposing changes. The research skill; pair with `thatch-coding-workflow`. |
|
|
@@ -112,9 +112,10 @@ and `--cursor` pass only the shared set.
|
|
|
112
112
|
|
|
113
113
|
## Install mechanics
|
|
114
114
|
|
|
115
|
-
- **Location**: `$XDG_CONFIG_HOME/opencode/skills` (opencode)
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
- **Location**: `$XDG_CONFIG_HOME/opencode/skills` (opencode);
|
|
116
|
+
scope-dependent for MCP hosts — the repo's `.claude/skills/` or
|
|
117
|
+
`.cursor/skills/` for project-local `thatch setup`, the host config
|
|
118
|
+
dirs (`$CLAUDE_CONFIG_DIR/skills`, `~/.cursor/skills`) for `--global`.
|
|
118
119
|
- **Trigger**: opencode installs at plugin init; Claude Code/Cursor install via
|
|
119
120
|
`thatch setup`.
|
|
120
121
|
- **Drift detection**: `installSkills` only writes when the on-disk content
|