@jeffober/thatch 0.1.28 → 0.1.30

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.
Files changed (61) hide show
  1. package/README.md +24 -10
  2. package/artifacts/skills/thatch-code-review.md +9 -7
  3. package/artifacts/skills/thatch-review-economy.md +97 -0
  4. package/artifacts/skills/thatch-review-followup.md +1 -1
  5. package/artifacts/skills/thatch-review-synthesizer.md +15 -5
  6. package/bin/qa-run +38 -0
  7. package/bin/release +21 -0
  8. package/bin/thatch +17 -5
  9. package/docs/dev/README.md +71 -18
  10. package/docs/dev/features/README.md +33 -0
  11. package/docs/dev/features/behavior-engine.md +271 -0
  12. package/docs/dev/features/cicd.md +89 -0
  13. package/docs/dev/features/cli.md +81 -0
  14. package/docs/dev/features/compaction-recovery.md +48 -0
  15. package/docs/dev/features/database.md +244 -0
  16. package/docs/dev/features/deduplication.md +102 -0
  17. package/docs/dev/features/extraction.md +253 -0
  18. package/docs/dev/features/hygiene.md +81 -0
  19. package/docs/dev/features/memory-store.md +154 -0
  20. package/docs/dev/features/multi-host.md +228 -0
  21. package/docs/dev/features/nudge-pipeline.md +282 -0
  22. package/docs/dev/features/prediction-engine.md +253 -0
  23. package/docs/dev/features/qa-system.md +205 -0
  24. package/docs/dev/features/session-lifecycle.md +110 -0
  25. package/docs/dev/features/setup.md +199 -0
  26. package/docs/dev/features/sideband.md +104 -0
  27. package/docs/dev/gotchas.md +3 -3
  28. package/docs/dev/mcp-parity.md +6 -4
  29. package/docs/dev/setup-and-hooks.md +4 -4
  30. package/docs/dev/skills.md +10 -8
  31. package/docs/plans/README.md +2 -2
  32. package/docs/user/README.md +58 -20
  33. package/docs/user/behavior-engine.md +85 -0
  34. package/docs/user/cli.md +98 -0
  35. package/docs/user/code-review.md +73 -0
  36. package/docs/user/deduplication.md +70 -0
  37. package/docs/user/default-behaviors.md +75 -0
  38. package/docs/user/extraction.md +74 -0
  39. package/docs/user/hygiene.md +76 -0
  40. package/docs/user/memory.md +81 -0
  41. package/docs/user/prediction-engine.md +87 -0
  42. package/docs/user/setup.md +110 -0
  43. package/docs/user/skills.md +97 -0
  44. package/package.json +2 -2
  45. package/src/behavior.ts +120 -0
  46. package/src/db.ts +160 -417
  47. package/src/embeddings.ts +0 -41
  48. package/src/extraction.ts +2 -2
  49. package/src/git.ts +3 -1
  50. package/src/index.ts +55 -9
  51. package/src/mcp.ts +30 -1
  52. package/src/prediction.ts +127 -0
  53. package/src/prompts.ts +135 -188
  54. package/src/scoring-engine.ts +374 -0
  55. package/src/seed-behaviors.ts +339 -0
  56. package/src/setup.ts +2 -6
  57. package/src/sideband.ts +36 -8
  58. package/src/skills.ts +1 -0
  59. package/src/tool-defs.ts +172 -25
  60. package/src/vector-math.ts +30 -0
  61. 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.28",
3
+ "version": "0.1.30",
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": "latest",
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"
@@ -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
+ }