@jeffober/thatch 0.1.28 → 0.1.29
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 +24 -10
- package/artifacts/skills/thatch-code-review.md +9 -7
- package/artifacts/skills/thatch-review-economy.md +97 -0
- package/artifacts/skills/thatch-review-followup.md +1 -1
- package/artifacts/skills/thatch-review-synthesizer.md +15 -5
- package/bin/qa-run +38 -0
- package/bin/thatch +10 -5
- package/docs/dev/README.md +71 -18
- package/docs/dev/features/README.md +33 -0
- package/docs/dev/features/behavior-engine.md +271 -0
- package/docs/dev/features/cicd.md +89 -0
- package/docs/dev/features/cli.md +81 -0
- package/docs/dev/features/compaction-recovery.md +48 -0
- package/docs/dev/features/database.md +244 -0
- package/docs/dev/features/deduplication.md +102 -0
- package/docs/dev/features/extraction.md +253 -0
- package/docs/dev/features/hygiene.md +81 -0
- package/docs/dev/features/memory-store.md +154 -0
- package/docs/dev/features/multi-host.md +228 -0
- package/docs/dev/features/nudge-pipeline.md +282 -0
- package/docs/dev/features/prediction-engine.md +253 -0
- package/docs/dev/features/qa-system.md +205 -0
- package/docs/dev/features/session-lifecycle.md +110 -0
- package/docs/dev/features/setup.md +199 -0
- package/docs/dev/features/sideband.md +104 -0
- package/docs/dev/gotchas.md +3 -3
- package/docs/dev/mcp-parity.md +6 -4
- package/docs/dev/setup-and-hooks.md +4 -4
- package/docs/dev/skills.md +10 -8
- package/docs/plans/README.md +2 -2
- package/docs/user/README.md +58 -20
- package/docs/user/behavior-engine.md +85 -0
- package/docs/user/cli.md +98 -0
- package/docs/user/code-review.md +73 -0
- package/docs/user/deduplication.md +70 -0
- package/docs/user/default-behaviors.md +75 -0
- package/docs/user/extraction.md +74 -0
- package/docs/user/hygiene.md +76 -0
- package/docs/user/memory.md +81 -0
- package/docs/user/prediction-engine.md +87 -0
- package/docs/user/setup.md +110 -0
- package/docs/user/skills.md +97 -0
- package/package.json +2 -2
- package/src/behavior.ts +120 -0
- package/src/db.ts +160 -417
- package/src/embeddings.ts +0 -41
- package/src/extraction.ts +2 -2
- package/src/git.ts +3 -1
- package/src/index.ts +55 -9
- package/src/mcp.ts +30 -1
- package/src/prediction.ts +127 -0
- package/src/prompts.ts +135 -188
- package/src/scoring-engine.ts +374 -0
- package/src/seed-behaviors.ts +339 -0
- package/src/setup.ts +2 -6
- package/src/sideband.ts +36 -8
- package/src/skills.ts +1 -0
- package/src/tool-defs.ts +172 -25
- package/src/vector-math.ts +30 -0
- package/docs/qa/README.md +0 -175
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Hygiene
|
|
2
|
+
|
|
3
|
+
Thatch checks your memory store for maintenance issues at the start of
|
|
4
|
+
every session. The agent sees the report and can tend the store when
|
|
5
|
+
convenient. Thatch never deletes memories on its own.
|
|
6
|
+
|
|
7
|
+
## How it works
|
|
8
|
+
|
|
9
|
+
At session start, thatch runs three checks against the current project
|
|
10
|
+
store. Only non-zero signals are reported. A healthy store stays
|
|
11
|
+
silent.
|
|
12
|
+
|
|
13
|
+
### Duplicate candidates
|
|
14
|
+
|
|
15
|
+
Memories whose content is very similar (cosine similarity >= 0.85).
|
|
16
|
+
These are the same pairs that `thatch_find_duplicates` surfaces. The
|
|
17
|
+
hygiene report shows the count, not the full list. To see the actual
|
|
18
|
+
pairs, the agent calls `thatch_find_duplicates`.
|
|
19
|
+
|
|
20
|
+
See [deduplication.md](deduplication.md) for the full dedup workflow.
|
|
21
|
+
|
|
22
|
+
### Stale entries
|
|
23
|
+
|
|
24
|
+
Memories that have not been updated or recalled in 90+ days. The check
|
|
25
|
+
uses `recall_count` and `last_recalled_at` columns, which are stamped
|
|
26
|
+
by `thatch_memory_recall` and CLI `thatch search`. Archived memories
|
|
27
|
+
are excluded.
|
|
28
|
+
|
|
29
|
+
If a memory is stale but still accurate, the agent can recall it
|
|
30
|
+
(which resets the timer). If it is stale and wrong, the agent can
|
|
31
|
+
update it with `overwrite: true` or delete it with
|
|
32
|
+
`thatch_memory_forget`.
|
|
33
|
+
|
|
34
|
+
### Orphaned branch memories
|
|
35
|
+
|
|
36
|
+
Memories scoped to a git branch that no longer exists. The check
|
|
37
|
+
cross-references the `branch` column against `git branch --list`. This
|
|
38
|
+
check is skipped when the working directory is not a git repo, so it
|
|
39
|
+
never produces false positives outside a repo.
|
|
40
|
+
|
|
41
|
+
To fix orphaned memories, the agent can consolidate them into
|
|
42
|
+
project-wide memories (re-save without the `branch` param) or archive
|
|
43
|
+
them.
|
|
44
|
+
|
|
45
|
+
## CLI
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
thatch hygiene # print the hygiene report standalone
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The session-start reminder (`thatch reminder`, called automatically by
|
|
52
|
+
hooks) also includes the hygiene report when any signal is non-zero.
|
|
53
|
+
|
|
54
|
+
## What you see
|
|
55
|
+
|
|
56
|
+
You do not see the hygiene report directly. The agent sees it in the
|
|
57
|
+
session-start reminder and may mention it: "Your store has 3 duplicate
|
|
58
|
+
candidates and 2 stale memories. Want me to clean those up?"
|
|
59
|
+
|
|
60
|
+
On opencode, the reminder is a synthetic prompt injected at session
|
|
61
|
+
start. On Claude Code and Cursor, it is printed by the SessionStart
|
|
62
|
+
hook and fed into the session as context.
|
|
63
|
+
|
|
64
|
+
## Limitations
|
|
65
|
+
|
|
66
|
+
- The plugin never deletes memories. It surfaces problems; the agent
|
|
67
|
+
decides what to do.
|
|
68
|
+
- Stale entries use a 90-day window (hardcoded `STALE_DAYS = 90`).
|
|
69
|
+
There is no configuration for this.
|
|
70
|
+
- The duplicate threshold (0.85) is higher than the recall nudge
|
|
71
|
+
threshold (0.55) because "duplicate" is a stronger claim than
|
|
72
|
+
"relates to."
|
|
73
|
+
- Orphaned branch check is skipped outside a git repo.
|
|
74
|
+
|
|
75
|
+
See [memory.md](memory.md) for the memory system and
|
|
76
|
+
[deduplication.md](deduplication.md) for the dedup workflow.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Persistent Memory
|
|
2
|
+
|
|
3
|
+
Thatch gives your AI coding agent the ability to remember information across
|
|
4
|
+
sessions. Memories are stored in a local SQLite database using local
|
|
5
|
+
embeddings (no network calls, no API keys).
|
|
6
|
+
|
|
7
|
+
## How it works
|
|
8
|
+
|
|
9
|
+
Each memory has a label, content, and an embedding vector. When you ask the
|
|
10
|
+
agent to save something, it embeds the text and stores it. When a future
|
|
11
|
+
session's prompt semantically matches a stored memory, thatch injects a
|
|
12
|
+
nudge telling the agent to recall it.
|
|
13
|
+
|
|
14
|
+
You never see the nudge. The agent sees it and decides whether to act
|
|
15
|
+
on it. The memory surfaces as context the agent reads, not as text
|
|
16
|
+
printed to your terminal.
|
|
17
|
+
|
|
18
|
+
## Stores
|
|
19
|
+
|
|
20
|
+
Memories are organized into stores:
|
|
21
|
+
|
|
22
|
+
- **global**: user preferences, personality, system environment. Shared
|
|
23
|
+
across all projects.
|
|
24
|
+
- **Per-project**: detected from your git remote. Each repo gets its
|
|
25
|
+
own store.
|
|
26
|
+
- **Branch-scoped**: memories can be scoped to a git branch for
|
|
27
|
+
feature-specific context (design decisions, WIP notes, PR status).
|
|
28
|
+
|
|
29
|
+
The agent defaults to searching both the project store and the global
|
|
30
|
+
store when recalling.
|
|
31
|
+
|
|
32
|
+
## Tools
|
|
33
|
+
|
|
34
|
+
| Tool | What it does |
|
|
35
|
+
|------|-------------|
|
|
36
|
+
| `thatch_memory_remember` | Save a memory with a label and content. Optional: branch, confidence (1-10), archived flag, overwrite. |
|
|
37
|
+
| `thatch_memory_recall` | Semantic search across stores. Returns matching memories with similarity scores. |
|
|
38
|
+
| `thatch_memory_list` | List all memories in a store (metadata only, no content). |
|
|
39
|
+
| `thatch_memory_show` | Show full content of a single memory by label. |
|
|
40
|
+
| `thatch_memory_forget` | Delete a memory by label. |
|
|
41
|
+
| `thatch_store_list` | List all active stores. |
|
|
42
|
+
| `thatch_find_duplicates` | Surface pairs of memories with suspiciously similar content. |
|
|
43
|
+
| `thatch_dedup_mark_checked` | Record a verdict for a reviewed pair so it stops being re-reported. |
|
|
44
|
+
|
|
45
|
+
## Configuration
|
|
46
|
+
|
|
47
|
+
- `THATCH_DB_PATH`: path to the SQLite database file. Defaults to
|
|
48
|
+
`~/.config/thatch/thatch.db` (opencode) or the project directory
|
|
49
|
+
(MCP).
|
|
50
|
+
- `THATCH_MODEL`: Hugging Face model name for embeddings. Defaults to
|
|
51
|
+
`Xenova/bge-small-en-v1.5` (34 MB, 384-dimensional).
|
|
52
|
+
- `THATCH_RECALL_THRESHOLD`: cosine score threshold for the recall
|
|
53
|
+
nudge. Defaults to 0.55. Lower surfaces more matches (noisier);
|
|
54
|
+
higher surfaces fewer (stricter).
|
|
55
|
+
|
|
56
|
+
## Limitations
|
|
57
|
+
|
|
58
|
+
- Search is brute-force cosine similarity across all entries in the
|
|
59
|
+
store. No approximate nearest neighbor index. Fine for thousands of
|
|
60
|
+
memories; would slow down at tens of thousands.
|
|
61
|
+
- Embeddings are local (bge-small-en-v1.5). Quality is good for
|
|
62
|
+
code-related content but not state-of-the-art compared to large
|
|
63
|
+
API-based models.
|
|
64
|
+
- There is no full-text search. Recall is semantic only (cosine on
|
|
65
|
+
embeddings). If you need exact string matching, use the agent's
|
|
66
|
+
grep or read tools.
|
|
67
|
+
- Memories are not versioned. Updating a memory with `overwrite: true`
|
|
68
|
+
replaces the content and embedding. The old content is lost (git
|
|
69
|
+
history does not track it).
|
|
70
|
+
|
|
71
|
+
## Archived memories
|
|
72
|
+
|
|
73
|
+
Memories can be marked `archived` to exclude them from default search
|
|
74
|
+
results without deleting them. Archived memories are useful for
|
|
75
|
+
consolidating branch-scoped context after a merge: the branch-scoped
|
|
76
|
+
memories become a single archived record that future sessions can find
|
|
77
|
+
with `includeArchived: true` but that does not clutter normal recall.
|
|
78
|
+
|
|
79
|
+
See [prediction-engine.md](prediction-engine.md) for the user decision
|
|
80
|
+
model and [behavior-engine.md](behavior-engine.md) for self-discipline
|
|
81
|
+
rules.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Prediction Engine
|
|
2
|
+
|
|
3
|
+
Thatch learns your decision-making preferences over time. When you correct
|
|
4
|
+
the agent, answer its questions, or express a preference, thatch
|
|
5
|
+
records it as a prediction. On future sessions, if your prompt
|
|
6
|
+
semantically matches a learned context, thatch surfaces the prediction
|
|
7
|
+
as a nudge the agent can follow.
|
|
8
|
+
|
|
9
|
+
## How it works
|
|
10
|
+
|
|
11
|
+
Predictions are context-dependent. Each prediction has two parts:
|
|
12
|
+
|
|
13
|
+
- **Matcher**: a description of the situation ("when reviewing a PR
|
|
14
|
+
with tech debt"). This is what gets embedded and matched against
|
|
15
|
+
your prompt.
|
|
16
|
+
- **Prediction**: the preference statement ("flag tech debt before
|
|
17
|
+
reviewing the diff"). This is what the agent reads and follows.
|
|
18
|
+
|
|
19
|
+
A matcher can link to multiple predictions (different preferences for the
|
|
20
|
+
same situation). A prediction can be linked from multiple matchers (the
|
|
21
|
+
same preference applies in different situations).
|
|
22
|
+
|
|
23
|
+
## Confidence
|
|
24
|
+
|
|
25
|
+
Each prediction has a confidence score from 0 to 1, based on a Bayesian
|
|
26
|
+
posterior:
|
|
27
|
+
|
|
28
|
+
- Starts at 0.5 (no evidence either way).
|
|
29
|
+
- Each confirm moves it up. Each disconfirm moves it down.
|
|
30
|
+
- Soft signals count as 0.25 of a full signal (weak disconfirm).
|
|
31
|
+
- No wall-clock decay. Confidence only moves when you provide feedback.
|
|
32
|
+
|
|
33
|
+
The agent follows strong predictions silently, surfaces ambiguous or
|
|
34
|
+
competing predictions to you, and updates the model when you respond.
|
|
35
|
+
|
|
36
|
+
## Auto-fire
|
|
37
|
+
|
|
38
|
+
When you send a prompt, thatch embeds it and searches for matching
|
|
39
|
+
matchers. If any match above the threshold (default 0.60), thatch injects
|
|
40
|
+
a `User decision model` block into the agent's context. The block looks
|
|
41
|
+
like:
|
|
42
|
+
|
|
43
|
+
```text
|
|
44
|
+
[thatch] User decision model
|
|
45
|
+
- [0.72 conf, 4 tests] When reviewing a PR with tech debt: you tend to
|
|
46
|
+
flag tech debt before reviewing the diff
|
|
47
|
+
- [0.61 conf, 2 tests] When choosing test scope: you may prefer
|
|
48
|
+
integration tests over unit tests
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The agent reads this and decides whether to follow the prediction,
|
|
52
|
+
surface it to you, or ignore it. You never see the nudge directly.
|
|
53
|
+
|
|
54
|
+
## Tools
|
|
55
|
+
|
|
56
|
+
| Tool | What it does |
|
|
57
|
+
|------|-------------|
|
|
58
|
+
| `thatch_prediction_query` | Query the model for predictions matching a context. Returns scored predictions with confidence and evidence count. |
|
|
59
|
+
| `thatch_prediction_update` | Create, reinforce, or weaken a prediction. Takes a matcher (situation), prediction (preference), signal (confirm/disconfirm/soft/create), and rationale. |
|
|
60
|
+
| `thatch_prediction_list` | List all predictions with matchers, confidence, evidence count, and provenance history. |
|
|
61
|
+
| `thatch_prediction_delete` | Delete a prediction by semantic match. Edges and provenance are cascade-deleted. |
|
|
62
|
+
|
|
63
|
+
## Configuration
|
|
64
|
+
|
|
65
|
+
- `THATCH_PREDICTION_THRESHOLD`: cosine score threshold for prediction
|
|
66
|
+
auto-fire. Defaults to 0.60. Lower surfaces more predictions
|
|
67
|
+
(noisier); higher surfaces fewer (stricter).
|
|
68
|
+
|
|
69
|
+
## Limitations
|
|
70
|
+
|
|
71
|
+
- The agent creates predictions based on your feedback. Thatch never
|
|
72
|
+
creates predictions on its own. If the agent does not proactively
|
|
73
|
+
record your preferences, the model stays empty.
|
|
74
|
+
- There is no formation nudge. The system prompt instructs the agent
|
|
75
|
+
to watch for preference signals, but the agent must decide to act on
|
|
76
|
+
those instructions.
|
|
77
|
+
- 0-evidence predictions (confidence 0.5) still surface. The nudge
|
|
78
|
+
uses "you may prefer" language for these, which hedges appropriately.
|
|
79
|
+
- Confidence never reaches 0 or 1. A disconfirmed prediction can still
|
|
80
|
+
fire if its matcher matches strongly. Delete it with
|
|
81
|
+
`thatch_prediction_delete` if it is wrong.
|
|
82
|
+
- Predictions are per-store. A preference learned in one project's
|
|
83
|
+
store does not automatically apply to other projects. The global
|
|
84
|
+
store is shared across projects.
|
|
85
|
+
|
|
86
|
+
See [memory.md](memory.md) for the base memory system and
|
|
87
|
+
[behavior-engine.md](behavior-engine.md) for the self-discipline rules.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Setup and Configuration
|
|
2
|
+
|
|
3
|
+
Thatch works with three AI coding tools. Each has a different
|
|
4
|
+
integration mechanism, but all share the same core: local embeddings,
|
|
5
|
+
SQLite, and the same tool definitions.
|
|
6
|
+
|
|
7
|
+
## OpenCode
|
|
8
|
+
|
|
9
|
+
OpenCode is the primary integration. Thatch runs as a plugin.
|
|
10
|
+
|
|
11
|
+
### Install
|
|
12
|
+
|
|
13
|
+
```jsonc
|
|
14
|
+
// opencode.jsonc or opencode.json
|
|
15
|
+
{
|
|
16
|
+
"plugin": ["@jeffober/thatch"]
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
OpenCode installs the plugin and its dependencies automatically on
|
|
21
|
+
next start.
|
|
22
|
+
|
|
23
|
+
### Async extraction
|
|
24
|
+
|
|
25
|
+
For background extraction (child sessions run in the background):
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
export OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Without this env var, extraction still works. The child session runs
|
|
32
|
+
synchronously instead of asynchronously (blocks the turn briefly).
|
|
33
|
+
Put it in your shell rc, `mise.toml [env]`, or `.envrc`.
|
|
34
|
+
|
|
35
|
+
### Local development
|
|
36
|
+
|
|
37
|
+
Before publishing, use a file path:
|
|
38
|
+
|
|
39
|
+
```jsonc
|
|
40
|
+
{ "plugin": ["./path/to/thatch/src/index.ts"] }
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Or place the thatch repo in `.opencode/plugins/` for auto-loading.
|
|
44
|
+
|
|
45
|
+
## Claude Code
|
|
46
|
+
|
|
47
|
+
Thatch runs as an MCP server. The setup command installs the server
|
|
48
|
+
config, instructions, hooks, and skills:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
thatch setup --claude # project-local (writes .mcp.json, CLAUDE.md, .claude/)
|
|
52
|
+
thatch setup --claude --global # user-scoped (~/.claude/)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`bun` must be on PATH (the thatch binary runs under bun). Setup is
|
|
56
|
+
idempotent. Re-running it updates drifted content without clobbering
|
|
57
|
+
unrelated config.
|
|
58
|
+
|
|
59
|
+
For a global install, setup prints the `claude mcp add --scope user`
|
|
60
|
+
command to run instead of writing a project `.mcp.json`.
|
|
61
|
+
|
|
62
|
+
## Cursor
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
thatch setup --cursor # project-local (.cursor/mcp.json, AGENTS.md, .cursor/hooks.json)
|
|
66
|
+
thatch setup --cursor --global # user-scoped (~/.cursor/)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Cursor uses a flat hooks format (`{version, hooks:{event:[{command}]}}`)
|
|
70
|
+
and `--json` output for hook commands.
|
|
71
|
+
|
|
72
|
+
## Environment variables
|
|
73
|
+
|
|
74
|
+
| Variable | Default | What it controls |
|
|
75
|
+
|----------|---------|-----------------|
|
|
76
|
+
| `THATCH_DB_PATH` | `~/.config/thatch/thatch.db` | SQLite database path |
|
|
77
|
+
| `THATCH_MODEL` | `Xenova/bge-small-en-v1.5` | Hugging Face model name for embeddings |
|
|
78
|
+
| `THATCH_RECALL_THRESHOLD` | `0.55` | Cosine threshold for recall nudge |
|
|
79
|
+
| `THATCH_PREDICTION_THRESHOLD` | `0.60` | Cosine threshold for prediction auto-fire |
|
|
80
|
+
| `THATCH_BEHAVIOR_THRESHOLD` | `0.60` | Cosine threshold for behavior auto-fire |
|
|
81
|
+
| `OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS` | (unset) | Enables async extraction (opencode only) |
|
|
82
|
+
|
|
83
|
+
## Setup detection
|
|
84
|
+
|
|
85
|
+
When the MCP server starts (Claude Code or Cursor), it checks whether
|
|
86
|
+
`thatch setup` was run for the current host. If setup was never run,
|
|
87
|
+
or if the instruction markers are broken (e.g., the file was edited
|
|
88
|
+
externally), the server emits a warning the agent can see and tell you
|
|
89
|
+
to run setup.
|
|
90
|
+
|
|
91
|
+
The MCP server also auto-refreshes skills, instructions, and hooks on
|
|
92
|
+
every startup. This ensures the latest skill files and prompt
|
|
93
|
+
instructions are deployed without requiring you to manually re-run
|
|
94
|
+
`thatch setup` after updating thatch.
|
|
95
|
+
|
|
96
|
+
## Limitations
|
|
97
|
+
|
|
98
|
+
- `bun` must be installed and on PATH. Thatch does not bundle its
|
|
99
|
+
own runtime.
|
|
100
|
+
- The embedding model (~34 MB) is downloaded once on first use and
|
|
101
|
+
cached in `node_modules/@huggingface/transformers/.cache/`.
|
|
102
|
+
- There is no web UI or dashboard. All interaction is through the
|
|
103
|
+
agent's tool calls and the `thatch` CLI.
|
|
104
|
+
- The MCP server is a long-lived process that holds the warm embedding
|
|
105
|
+
model in memory. Hook commands connect to it via a Unix domain
|
|
106
|
+
socket (the sideband) to avoid loading the model themselves.
|
|
107
|
+
|
|
108
|
+
See [memory.md](memory.md) for the memory system,
|
|
109
|
+
[skills.md](skills.md) for skills, and [extraction.md](extraction.md)
|
|
110
|
+
for the fact extraction pipeline.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Skills
|
|
2
|
+
|
|
3
|
+
Thatch ships skills that guide the agent through structured workflows:
|
|
4
|
+
code review, project investigation, memory management, PR and ticket
|
|
5
|
+
writing, and code change planning. Skills are markdown files with
|
|
6
|
+
frontmatter (name, description) that opencode auto-discovers.
|
|
7
|
+
|
|
8
|
+
## How they work
|
|
9
|
+
|
|
10
|
+
On startup, thatch installs skill files into your global config
|
|
11
|
+
(`~/.config/opencode/skills/` for opencode, `~/.claude/skills/` for
|
|
12
|
+
Claude Code, `~/.cursor/skills/` for Cursor). The host's skill
|
|
13
|
+
discovery scans these directories and presents them to the agent as
|
|
14
|
+
available skills.
|
|
15
|
+
|
|
16
|
+
The agent loads a skill when the user's request matches the skill's
|
|
17
|
+
description, or when another skill or the system prompt directs the
|
|
18
|
+
agent to load it. Once loaded, the skill body (the markdown content)
|
|
19
|
+
becomes part of the agent's context.
|
|
20
|
+
|
|
21
|
+
## What ships
|
|
22
|
+
|
|
23
|
+
24 skills total (23 shared, 1 opencode-only):
|
|
24
|
+
|
|
25
|
+
### Memory skills
|
|
26
|
+
|
|
27
|
+
| Skill | Purpose |
|
|
28
|
+
|-------|---------|
|
|
29
|
+
| `thatch-fact-extractor` | Guides the agent through turning buffered tool interactions into memories. Auto-triggered by the extraction pipeline. |
|
|
30
|
+
| `thatch-dedup-classifier` | Guides the agent through classifying and resolving duplicate-candidate pairs. |
|
|
31
|
+
| `thatch-project-primer` | Investigates a new project from multiple angles and writes foundational memories. |
|
|
32
|
+
| `thatch-session-reflection` | End-of-session skill for recording what was learned about the project, user, tools, and self. |
|
|
33
|
+
|
|
34
|
+
### Code review skills
|
|
35
|
+
|
|
36
|
+
| Skill | Focus |
|
|
37
|
+
|-------|-------|
|
|
38
|
+
| `thatch-review-pedantic` | Mechanical correctness: spelling, naming, doc accuracy, specs, guidelines, stale artifacts. |
|
|
39
|
+
| `thatch-review-acceptance` | Behavioral/product review: UX coherency, behavioral delta, integration effects, user assumptions. |
|
|
40
|
+
| `thatch-review-state-flow` | Data flow and contracts: module boundaries, implicit state machines, error propagation, separation of concerns. |
|
|
41
|
+
| `thatch-review-economy` | Design simplicity and maintainability: is the complexity earned? Evaluates both the overall change design (forest) and individual touch points (trees) for unnecessary complexity, redundancy, and simpler available alternatives. |
|
|
42
|
+
| `thatch-review-no-slop` | AI writing anti-patterns: change narration, fourth wall breaks, em dashes, hedging, filler. |
|
|
43
|
+
| `thatch-review-breadcrumbs` | Comment narrative: do comments form a coherent outline of the code's behavior? |
|
|
44
|
+
| `thatch-review-mark-and-sweep` | Mechanical change completeness: whole-repo sweep for stragglers after renames, flag removals, API substitutions. |
|
|
45
|
+
| `thatch-review-highlights` | Positive finding detection: notably clever solutions, cleanup done along the way, documentation that helps. Medium-high bar. |
|
|
46
|
+
| `thatch-review-synthesizer` | Verifies and synthesizes findings from multiple specialists into a deduplicated, severity-grouped report. |
|
|
47
|
+
| `thatch-review-context` | Gathers project context (PRs, tickets, TODOs, deferred work) before a review. Prevents false positives about intentionally deferred work. |
|
|
48
|
+
| `thatch-code-archaeology` | Investigates an existing feature, debugs an unfamiliar area, or begins a new ticket. The research skill; pair with `thatch-coding-workflow`. |
|
|
49
|
+
| `thatch-review-followup` | Verifies whether the author's responses and code changes since your last review adequately addressed your prior findings. |
|
|
50
|
+
| `thatch-review-response` | Author-side review response: triage findings on your own PR, fix bugs one by one, reply on each thread, post a top-level summary comment. |
|
|
51
|
+
| `thatch-code-review` | Multi-agent review coordinator (opencode-only). Dispatches parallel sub-agents for specialist fan-out and synthesis. |
|
|
52
|
+
|
|
53
|
+
### Walkthrough skills
|
|
54
|
+
|
|
55
|
+
| Skill | Purpose |
|
|
56
|
+
|-------|---------|
|
|
57
|
+
| `thatch-change-walkthrough` | Explains a change to you as a teaching walkthrough: researches each affected workflow at the merge-base, teaches current behavior, then overlays the modifications with file:line citations. |
|
|
58
|
+
| `thatch-code-walkthrough` | Explains a feature, module, or workflow as a teaching walkthrough with file:line citations. Also used to draft high-level docs for new or undocumented features. |
|
|
59
|
+
|
|
60
|
+
### Writing skills
|
|
61
|
+
|
|
62
|
+
| Skill | Purpose |
|
|
63
|
+
|-------|---------|
|
|
64
|
+
| `thatch-pr-description` | Drafts PR descriptions with SYNOPSIS / PURPOSE / DESCRIPTION / WALK-THROUGH / NOTES, using bold and italic emphasis for scanning. |
|
|
65
|
+
| `thatch-ticket-description` | Drafts ticket or issue descriptions (Linear or Jira) with clear sections and bold/italic emphasis for scanning. |
|
|
66
|
+
| `thatch-split-overlarge-pr` | Splits already-completed work from an overlarge PR into human-reviewable, release-safe PRs targeting main. |
|
|
67
|
+
|
|
68
|
+
### Workflow skills
|
|
69
|
+
|
|
70
|
+
| Skill | Purpose |
|
|
71
|
+
|-------|---------|
|
|
72
|
+
| `thatch-coding-workflow` | Plans and executes code changes with a task-list-driven workflow: complexity triage, milestone planning, research before coding, post-coding verification. Pairs with `thatch-code-archaeology` (research first, then plan). |
|
|
73
|
+
|
|
74
|
+
## Stale skill cleanup
|
|
75
|
+
|
|
76
|
+
When skills are renamed or removed in a new release, thatch automatically
|
|
77
|
+
deletes the old skill directories from your config on next startup.
|
|
78
|
+
Prefixed skills (`thatch-*`) are always cleaned up. Non-prefixed
|
|
79
|
+
skills (from early releases) are cleaned up through a version-gated
|
|
80
|
+
window.
|
|
81
|
+
|
|
82
|
+
## Limitations
|
|
83
|
+
|
|
84
|
+
- Skills are installed as copies, not symlinks. If you manually edit
|
|
85
|
+
an installed copy, your changes are lost on next startup (thatch
|
|
86
|
+
re-installs from its canonical source). Edit the canonical source
|
|
87
|
+
in the thatch repo instead.
|
|
88
|
+
- The opencode-only coordinator (`thatch-code-review`) requires
|
|
89
|
+
sub-agent support. It is not available on Claude Code or Cursor.
|
|
90
|
+
- Skill content is markdown read at runtime, not compiled into the
|
|
91
|
+
binary. If the skill file is missing or corrupted, the skill will
|
|
92
|
+
not load (silent failure).
|
|
93
|
+
|
|
94
|
+
See [code-review.md](code-review.md) for the review pipeline,
|
|
95
|
+
[extraction.md](extraction.md) for the fact-extractor skill,
|
|
96
|
+
[memory.md](memory.md) for the memory skills, and
|
|
97
|
+
[setup.md](setup.md) for installation.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jeffober/thatch",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.29",
|
|
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",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@opencode-ai/plugin": "^1.17.13",
|
|
31
|
-
"@types/bun": "
|
|
31
|
+
"@types/bun": "^1.3.14",
|
|
32
32
|
"markdownlint-cli2": "^0.23.0",
|
|
33
33
|
"typescript": "^5.7.0",
|
|
34
34
|
"yaml": "^2.7.0"
|
package/src/behavior.ts
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import { ScoringEngine } from "./scoring-engine";
|
|
3
|
+
|
|
4
|
+
export interface BehaviorRow {
|
|
5
|
+
id: string;
|
|
6
|
+
store: string;
|
|
7
|
+
statement: string;
|
|
8
|
+
rationale: string | null;
|
|
9
|
+
confidence: number;
|
|
10
|
+
confirm_count: number;
|
|
11
|
+
disconfirm_count: number;
|
|
12
|
+
created_at: string;
|
|
13
|
+
updated_at: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface BehaviorNudgeItem {
|
|
17
|
+
confidence: number;
|
|
18
|
+
evidence_count: number;
|
|
19
|
+
matcher_description: string;
|
|
20
|
+
statement: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ScoredBehavior {
|
|
24
|
+
matcher_id: string;
|
|
25
|
+
matcher_description: string;
|
|
26
|
+
behavior_id: string;
|
|
27
|
+
statement: string;
|
|
28
|
+
confidence: number;
|
|
29
|
+
evidence_count: number;
|
|
30
|
+
score: number;
|
|
31
|
+
rationale: string | null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const config = {
|
|
35
|
+
matchersTable: "behavior_matchers",
|
|
36
|
+
itemsTable: "behaviors",
|
|
37
|
+
edgesTable: "behavior_edges",
|
|
38
|
+
provenanceTable: "behavior_provenance",
|
|
39
|
+
itemForeignKey: "behavior_id",
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Behavior engine: the LLM's codified self-discipline rules.
|
|
44
|
+
* Same four-table shape as the prediction engine but separate
|
|
45
|
+
* tables because the semantics differ. Predictions model what
|
|
46
|
+
* the USER wants; behaviors model what the LLM should do.
|
|
47
|
+
* Delegates to ScoringEngine for all SQL and scoring logic.
|
|
48
|
+
*/
|
|
49
|
+
export class BehaviorEngine {
|
|
50
|
+
#engine: ScoringEngine;
|
|
51
|
+
|
|
52
|
+
constructor(db: Database) {
|
|
53
|
+
this.#engine = new ScoringEngine(db, config);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
findBehaviorMatchers(stores: string[], queryEmbedding: Float32Array, opts?: { limit?: number }) {
|
|
57
|
+
return this.#engine.findMatchers(stores, queryEmbedding, opts);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
scoreBehaviors(matchers: { id: string; description: string; score: number }[]): ScoredBehavior[] {
|
|
61
|
+
return this.#engine.scoreItems(matchers).map((s) => ({
|
|
62
|
+
matcher_id: s.matcher_id,
|
|
63
|
+
matcher_description: s.matcher_description,
|
|
64
|
+
behavior_id: s.item_id,
|
|
65
|
+
statement: s.statement,
|
|
66
|
+
confidence: s.confidence,
|
|
67
|
+
evidence_count: s.evidence_count,
|
|
68
|
+
score: s.score,
|
|
69
|
+
rationale: s.rationale,
|
|
70
|
+
}));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
scoreBehaviorNudge(stores: string[], embedding: Float32Array, threshold: number, limit = 5): BehaviorNudgeItem[] {
|
|
74
|
+
return this.#engine.scoreNudge(stores, embedding, threshold, limit);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
findNearestBehaviorMatcher(store: string, embedding: Float32Array, threshold: number): { id: string; description: string } | null {
|
|
78
|
+
return this.#engine.findNearestMatcher(store, embedding, threshold);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
createBehaviorMatcher(store: string, description: string, embedding: Float32Array, model: string): string {
|
|
82
|
+
return this.#engine.createMatcher(store, description, embedding, model);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
findNearestBehavior(store: string, embedding: Float32Array, threshold: number): BehaviorRow | null {
|
|
86
|
+
return this.#engine.findNearestItem(store, embedding, threshold) as BehaviorRow | null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
createBehavior(store: string, statement: string, rationale: string, embedding: Float32Array, model: string): string {
|
|
90
|
+
return this.#engine.createItem(store, statement, rationale, embedding, model);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
createBehaviorEdge(matcherId: string, behaviorId: string, weight: number): void {
|
|
94
|
+
return this.#engine.createEdge(matcherId, behaviorId, weight);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
adjustBehaviorConfidence(behaviorId: string, signal: "confirm" | "disconfirm" | "soft"): void {
|
|
98
|
+
return this.#engine.adjustConfidence(behaviorId, signal);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
getBehavior(behaviorId: string): BehaviorRow | null {
|
|
102
|
+
return this.#engine.getItem(behaviorId) as BehaviorRow | null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
addBehaviorProvenance(behaviorId: string, signal: string, detail: string): void {
|
|
106
|
+
return this.#engine.addProvenance(behaviorId, signal, detail);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
getBehaviorProvenance(behaviorId: string): { signal: string; detail: string | null; created_at: string }[] {
|
|
110
|
+
return this.#engine.getProvenance(behaviorId);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
deleteBehavior(behaviorId: string): boolean {
|
|
114
|
+
return this.#engine.deleteItem(behaviorId);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
listBehaviors(store: string) {
|
|
118
|
+
return this.#engine.listItems(store);
|
|
119
|
+
}
|
|
120
|
+
}
|