@jeffober/thatch 0.2.3 → 0.2.4
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/bin/thatch +68 -41
- package/docs/dev/features/setup.md +7 -0
- package/docs/user/cli.md +2 -1
- package/docs/user/setup.md +13 -0
- package/package.json +1 -1
- package/src/setup.ts +50 -0
package/bin/thatch
CHANGED
|
@@ -8,7 +8,7 @@ import { detectRepo } from "../src/git";
|
|
|
8
8
|
import { BgeEmbeddingModel } from "../src/embeddings";
|
|
9
9
|
import { hygieneReport } from "../src/hygiene";
|
|
10
10
|
import { claudeSessionStartReminder } from "../src/prompts";
|
|
11
|
-
import { setupClaudeCode, setupCursor, registerClaudeMcpServer } from "../src/setup";
|
|
11
|
+
import { setupClaudeCode, setupCursor, setupSkillsOnly, registerClaudeMcpServer } from "../src/setup";
|
|
12
12
|
import { resolveOpencodeDbPath, SessionDB, partToTimelineEntry, partToTranscriptEntries, partToFullJson, messageToFullJson, type SessionPart } from "../src/session-db";
|
|
13
13
|
import { chatTailDiff, chatTailBacklog, filterChatTailRows, formatChatTailJsonl, CHAT_TAIL_DEFAULT_LIMIT, parseChatTimeBound, CHAT_STALE_MS, CHAT_RENUDGE_MINUTES, chatLiveness, humanAge, splitChatRoster, sortChatRoster, mcpSessionID, isoMinutesAgo, renderChatParticipant, resolveRegisteredPredecessor, type ChatSessionRow, type ChatTailFilter } from "../src/chat";
|
|
14
14
|
import { chatEnabled, loadConfig } from "../src/config";
|
|
@@ -38,8 +38,10 @@ function usage(): never {
|
|
|
38
38
|
thatch flush-tools [--json] Peek the queue + print an extraction nudge (hook only)
|
|
39
39
|
thatch chat-notify Print a chat follow-up message when mail is unread (Cursor stop hook)
|
|
40
40
|
thatch flush-predictions [--json] Query predictions for the prompt (hook only)
|
|
41
|
-
thatch setup --claude [--cursor] [--local|--global]
|
|
42
|
-
Install config + instructions + hooks + skills
|
|
41
|
+
thatch setup --claude [--cursor] [--local|--global] [--skills-only]
|
|
42
|
+
Install config + instructions + hooks + skills.
|
|
43
|
+
--skills-only refreshes just the skill files
|
|
44
|
+
(skips MCP config, instructions, hooks, commands)
|
|
43
45
|
thatch session list -s <id> [--after T] [--before T]
|
|
44
46
|
JSONL timeline of a session's parts (one line per part)
|
|
45
47
|
thatch session get --id <part_id|msg_id>
|
|
@@ -839,6 +841,10 @@ switch (command) {
|
|
|
839
841
|
const isLocal = args.includes("--local");
|
|
840
842
|
const isClaude = args.includes("--claude");
|
|
841
843
|
const isCursor = args.includes("--cursor");
|
|
844
|
+
// --skills-only narrows the run to the skill install: same host and
|
|
845
|
+
// scope rules, but MCP config, instructions, hooks, and commands are
|
|
846
|
+
// left untouched.
|
|
847
|
+
const skillsOnly = args.includes("--skills-only");
|
|
842
848
|
|
|
843
849
|
if (isGlobal && isLocal) {
|
|
844
850
|
console.error("Choose one scope: --local or --global.");
|
|
@@ -875,52 +881,73 @@ switch (command) {
|
|
|
875
881
|
};
|
|
876
882
|
|
|
877
883
|
if (isClaude) {
|
|
878
|
-
|
|
884
|
+
if (skillsOnly) {
|
|
885
|
+
const result = setupSkillsOnly("claude", isGlobal, dir);
|
|
879
886
|
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
console.log(
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
console.log(
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
if (reg.status === "registered") {
|
|
899
|
-
console.log(` MCP server: ${reg.detail}`);
|
|
900
|
-
} else if (reg.status === "already-registered") {
|
|
901
|
-
console.log(` MCP server: already registered (claude mcp get thatch to inspect)`);
|
|
902
|
-
} else {
|
|
903
|
-
console.log("To register the MCP server globally, run:");
|
|
904
|
-
console.log(` ${result.mcpAddCommand}`);
|
|
905
|
-
console.log(` (${reg.detail})`);
|
|
887
|
+
console.log(`Thatch skills updated for Claude Code (${isGlobal ? "global" : "project-local"}).`);
|
|
888
|
+
console.log();
|
|
889
|
+
printSkills(result);
|
|
890
|
+
console.log();
|
|
891
|
+
console.log("Restart Claude Code for the changes to take effect.");
|
|
892
|
+
} else {
|
|
893
|
+
const result = setupClaudeCode(thatchBin, isGlobal, dir);
|
|
894
|
+
|
|
895
|
+
console.log(`Thatch setup complete for Claude Code (${isGlobal ? "global" : "project-local"}).`);
|
|
896
|
+
console.log();
|
|
897
|
+
if (result.mcpConfig) {
|
|
898
|
+
console.log(` MCP config: ${result.mcpConfig}`);
|
|
899
|
+
}
|
|
900
|
+
console.log(` Instructions: ${result.claudeMd}`);
|
|
901
|
+
console.log(` Hooks: ${result.settings}`);
|
|
902
|
+
printSkills(result);
|
|
903
|
+
if (result.commands.length > 0) {
|
|
904
|
+
console.log(` Commands: ${result.commands.length} written (${result.commands.map((p) => p.split("/").pop()).join(", ")})`);
|
|
906
905
|
}
|
|
907
906
|
console.log();
|
|
907
|
+
if (result.mcpAddCommand) {
|
|
908
|
+
// Global setup cannot write ~/.claude.json directly, so run the
|
|
909
|
+
// registration on the user's behalf. Idempotent: an existing
|
|
910
|
+
// registration is left alone; a missing or failing claude CLI
|
|
911
|
+
// falls back to printing the manual command.
|
|
912
|
+
const reg = await registerClaudeMcpServer(thatchBin);
|
|
913
|
+
if (reg.status === "registered") {
|
|
914
|
+
console.log(` MCP server: ${reg.detail}`);
|
|
915
|
+
} else if (reg.status === "already-registered") {
|
|
916
|
+
console.log(` MCP server: already registered (claude mcp get thatch to inspect)`);
|
|
917
|
+
} else {
|
|
918
|
+
console.log("To register the MCP server globally, run:");
|
|
919
|
+
console.log(` ${result.mcpAddCommand}`);
|
|
920
|
+
console.log(` (${reg.detail})`);
|
|
921
|
+
}
|
|
922
|
+
console.log();
|
|
923
|
+
}
|
|
924
|
+
console.log("Restart Claude Code for the changes to take effect.");
|
|
908
925
|
}
|
|
909
|
-
console.log("Restart Claude Code for the changes to take effect.");
|
|
910
926
|
}
|
|
911
927
|
|
|
912
928
|
if (isCursor) {
|
|
913
|
-
|
|
929
|
+
if (skillsOnly) {
|
|
930
|
+
const result = setupSkillsOnly("cursor", isGlobal, dir);
|
|
914
931
|
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
932
|
+
console.log();
|
|
933
|
+
console.log(`Thatch skills updated for Cursor (${isGlobal ? "global" : "project-local"}).`);
|
|
934
|
+
console.log();
|
|
935
|
+
printSkills(result);
|
|
936
|
+
console.log();
|
|
937
|
+
console.log("Restart Cursor for the changes to take effect.");
|
|
938
|
+
} else {
|
|
939
|
+
const result = setupCursor(thatchBin, isGlobal, dir);
|
|
940
|
+
|
|
941
|
+
console.log();
|
|
942
|
+
console.log(`Thatch setup complete for Cursor (${isGlobal ? "global" : "project-local"}).`);
|
|
943
|
+
console.log();
|
|
944
|
+
console.log(` MCP config: ${result.mcpConfig}`);
|
|
945
|
+
console.log(` Instructions: ${result.agentsMd}`);
|
|
946
|
+
console.log(` Hooks: ${result.hooks}`);
|
|
947
|
+
printSkills(result);
|
|
948
|
+
console.log();
|
|
949
|
+
console.log("Restart Cursor for the changes to take effect.");
|
|
950
|
+
}
|
|
924
951
|
}
|
|
925
952
|
break;
|
|
926
953
|
}
|
|
@@ -18,6 +18,8 @@ resolution fit together.
|
|
|
18
18
|
hooks, and skills for Cursor
|
|
19
19
|
- `--global` flag --- installs to user-scoped config dirs instead of
|
|
20
20
|
project-local
|
|
21
|
+
- `--skills-only` flag --- refreshes just the skill files (same host and
|
|
22
|
+
scope rules), skipping MCP config, instructions, hooks, and commands
|
|
21
23
|
- **Marker system** --- idempotent replacement of the thatch instruction block
|
|
22
24
|
in `CLAUDE.md` or `AGENTS.md` via start/end markers
|
|
23
25
|
- **`checkSetup`** --- detects installed, not-installed, or markers-broken at
|
|
@@ -35,6 +37,11 @@ resolution fit together.
|
|
|
35
37
|
|
|
36
38
|
At least one of `--claude` or `--cursor` is required. Both can be passed
|
|
37
39
|
together. `--global` applies to whichever host or hosts are selected.
|
|
40
|
+
`--skills-only` narrows the run to the skill install (`setupSkillsOnly`
|
|
41
|
+
in `src/setup.ts`): it reuses the same path resolution and other-scope
|
|
42
|
+
probe as the full setup paths but only calls `installSkills`, so it is
|
|
43
|
+
safe to run in an already-set-up host when only the skill files need
|
|
44
|
+
refreshing.
|
|
38
45
|
|
|
39
46
|
### Claude Code setup (`setupClaudeCode` in `src/setup.ts`)
|
|
40
47
|
|
package/docs/user/cli.md
CHANGED
|
@@ -148,7 +148,8 @@ thatch buffer-batch # append tool batch to queue (Claude Code hook)
|
|
|
148
148
|
thatch buffer-tool # append single tool to queue (Cursor hook)
|
|
149
149
|
thatch flush-tools [--json] # peek queue + print nudges (hook)
|
|
150
150
|
thatch flush-predictions [--json] # prediction-only nudge (hook)
|
|
151
|
-
thatch setup --claude [--cursor] [--global]
|
|
151
|
+
thatch setup --claude [--cursor] [--global] # install config + hooks + skills
|
|
152
|
+
thatch setup --claude --skills-only # refresh just the skill files
|
|
152
153
|
```
|
|
153
154
|
|
|
154
155
|
## Environment variables
|
package/docs/user/setup.md
CHANGED
|
@@ -60,6 +60,16 @@ Skills follow the install scope: project-local installs them to the
|
|
|
60
60
|
repo's `.claude/skills/` so they version with the project; `--global`
|
|
61
61
|
installs them to `~/.claude/skills/` (or `$CLAUDE_CONFIG_DIR/skills/`).
|
|
62
62
|
|
|
63
|
+
To refresh just the skill files without rewriting anything else, add
|
|
64
|
+
`--skills-only`:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
thatch setup --claude --skills-only
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
This skips the MCP config, instructions, hooks, and commands, and
|
|
71
|
+
reports the same skills directory and install counts as a full run.
|
|
72
|
+
|
|
63
73
|
`bun` must be on PATH (the thatch binary runs under bun). Setup is
|
|
64
74
|
idempotent. Re-running it updates drifted content without clobbering
|
|
65
75
|
unrelated config, and reports what it did: the skills directory plus
|
|
@@ -84,6 +94,9 @@ thatch setup --cursor --global # user-scoped (~/.cursor/)
|
|
|
84
94
|
Skills follow the install scope: project-local installs them to the
|
|
85
95
|
repo's `.cursor/skills/`; `--global` installs them to `~/.cursor/skills/`.
|
|
86
96
|
|
|
97
|
+
As with Claude Code, `--skills-only` refreshes just the skill files and
|
|
98
|
+
skips the MCP config, instructions, and hooks.
|
|
99
|
+
|
|
87
100
|
Cursor uses a flat hooks format (`{version, hooks:{event:[{command}]}}`)
|
|
88
101
|
and `--json` output for hook commands.
|
|
89
102
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jeffober/thatch",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "Persistent memory for AI coding agents — local embeddings, SQLite stores, zero config. Works with OpenCode (as a plugin), Claude Code (as an MCP server), and Cursor (as an MCP server).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
package/src/setup.ts
CHANGED
|
@@ -643,6 +643,56 @@ export function setupCursor(
|
|
|
643
643
|
};
|
|
644
644
|
}
|
|
645
645
|
|
|
646
|
+
// ---------------------------------------------------------------------------
|
|
647
|
+
// Skills-only setup - refresh skill files without touching anything else
|
|
648
|
+
// ---------------------------------------------------------------------------
|
|
649
|
+
|
|
650
|
+
export interface SkillsOnlyResult {
|
|
651
|
+
/** Skill install report for the directory this run wrote to. */
|
|
652
|
+
skills: InstallReport;
|
|
653
|
+
/** Thatch skills in the opposite scope's dir, if any; never written by this run. */
|
|
654
|
+
otherScopeSkills: OtherScopeSkills | null;
|
|
655
|
+
global: boolean;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Installs only the skill files, skipping MCP config, instructions, hooks,
|
|
660
|
+
* and commands. Intended for refreshing skills in an already-set-up host
|
|
661
|
+
* without rewriting the rest of the configuration. Same scope rules as full
|
|
662
|
+
* setup: project-local runs write to the repo's host skills dir, global runs
|
|
663
|
+
* to the host config dir, and the other scope is probed (surfaced in the
|
|
664
|
+
* report) but never written.
|
|
665
|
+
*/
|
|
666
|
+
export function setupSkillsOnly(
|
|
667
|
+
host: HostKind,
|
|
668
|
+
global: boolean,
|
|
669
|
+
projectDir: string,
|
|
670
|
+
homeDir?: string,
|
|
671
|
+
): SkillsOnlyResult {
|
|
672
|
+
const { homedir } = require("node:os");
|
|
673
|
+
const home = homeDir ?? homedir();
|
|
674
|
+
|
|
675
|
+
const skillsDir = host === "claude"
|
|
676
|
+
? resolvePaths(global, projectDir, home).skillsDir
|
|
677
|
+
: resolveCursorPaths(global, projectDir, home).skillsDir;
|
|
678
|
+
|
|
679
|
+
// Same other-scope logic as the full setup paths: surface skills this run
|
|
680
|
+
// did not touch so the CLI can warn about leftovers.
|
|
681
|
+
const otherDir = host === "claude"
|
|
682
|
+
? (global
|
|
683
|
+
? join(projectDir, ".claude", "skills")
|
|
684
|
+
: join(claudeConfigDir(home), "skills"))
|
|
685
|
+
: (global
|
|
686
|
+
? join(projectDir, ".cursor", "skills")
|
|
687
|
+
: join(cursorConfigDir(home), "skills"));
|
|
688
|
+
|
|
689
|
+
return {
|
|
690
|
+
skills: installSkills(skillsDir),
|
|
691
|
+
otherScopeSkills: otherScopeInfo(otherDir),
|
|
692
|
+
global,
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
|
|
646
696
|
// ---------------------------------------------------------------------------
|
|
647
697
|
// Setup detection - check whether setup was completed for the current host
|
|
648
698
|
// ---------------------------------------------------------------------------
|