gitmem-mcp 1.0.0 → 1.0.1
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/CLAUDE.md.template +63 -55
- package/README.md +60 -29
- package/bin/gitmem.js +233 -109
- package/bin/init-wizard.js +642 -0
- package/bin/uninstall.js +288 -0
- package/dist/commands/check.js +20 -20
- package/dist/commands/check.js.map +1 -1
- package/dist/constants/closing-questions.d.ts +6 -0
- package/dist/constants/closing-questions.d.ts.map +1 -1
- package/dist/constants/closing-questions.js +65 -0
- package/dist/constants/closing-questions.js.map +1 -1
- package/dist/hooks/format-utils.d.ts +52 -0
- package/dist/hooks/format-utils.d.ts.map +1 -0
- package/dist/hooks/format-utils.js +89 -0
- package/dist/hooks/format-utils.js.map +1 -0
- package/dist/hooks/quick-retrieve.d.ts +30 -0
- package/dist/hooks/quick-retrieve.d.ts.map +1 -0
- package/dist/hooks/quick-retrieve.js +149 -0
- package/dist/hooks/quick-retrieve.js.map +1 -0
- package/dist/index.js +0 -0
- package/dist/schemas/active-sessions.d.ts +8 -8
- package/dist/schemas/analyze.d.ts +3 -3
- package/dist/schemas/common.d.ts +2 -2
- package/dist/schemas/common.d.ts.map +1 -1
- package/dist/schemas/common.js +1 -1
- package/dist/schemas/common.js.map +1 -1
- package/dist/schemas/create-decision.d.ts +3 -3
- package/dist/schemas/create-learning.d.ts +13 -13
- package/dist/schemas/log.d.ts +3 -3
- package/dist/schemas/prepare-context.d.ts +3 -3
- package/dist/schemas/recall.d.ts +3 -3
- package/dist/schemas/record-scar-usage-batch.d.ts +8 -3
- package/dist/schemas/record-scar-usage-batch.d.ts.map +1 -1
- package/dist/schemas/record-scar-usage.d.ts +3 -0
- package/dist/schemas/record-scar-usage.d.ts.map +1 -1
- package/dist/schemas/record-scar-usage.js +1 -0
- package/dist/schemas/record-scar-usage.js.map +1 -1
- package/dist/schemas/registry.d.ts +18 -0
- package/dist/schemas/registry.d.ts.map +1 -0
- package/dist/schemas/registry.js +158 -0
- package/dist/schemas/registry.js.map +1 -0
- package/dist/schemas/save-transcript.d.ts +3 -3
- package/dist/schemas/search-transcripts.d.ts +33 -0
- package/dist/schemas/search-transcripts.d.ts.map +1 -0
- package/dist/schemas/search-transcripts.js +26 -0
- package/dist/schemas/search-transcripts.js.map +1 -0
- package/dist/schemas/search.d.ts +3 -3
- package/dist/schemas/session-close.d.ts +43 -15
- package/dist/schemas/session-close.d.ts.map +1 -1
- package/dist/schemas/session-close.js +7 -2
- package/dist/schemas/session-close.js.map +1 -1
- package/dist/schemas/session-start.d.ts +3 -3
- package/dist/schemas/thread.d.ts +3 -3
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +82 -28
- package/dist/server.js.map +1 -1
- package/dist/services/active-sessions.d.ts +2 -1
- package/dist/services/active-sessions.d.ts.map +1 -1
- package/dist/services/active-sessions.js +130 -84
- package/dist/services/active-sessions.js.map +1 -1
- package/dist/services/analytics.d.ts.map +1 -1
- package/dist/services/analytics.js +1 -0
- package/dist/services/analytics.js.map +1 -1
- package/dist/services/behavioral-decay.d.ts +40 -0
- package/dist/services/behavioral-decay.d.ts.map +1 -0
- package/dist/services/behavioral-decay.js +110 -0
- package/dist/services/behavioral-decay.js.map +1 -0
- package/dist/services/bm25.d.ts +39 -0
- package/dist/services/bm25.d.ts.map +1 -0
- package/dist/services/bm25.js +132 -0
- package/dist/services/bm25.js.map +1 -0
- package/dist/services/cache.d.ts.map +1 -1
- package/dist/services/cache.js +9 -8
- package/dist/services/cache.js.map +1 -1
- package/dist/services/cache.test.js +17 -17
- package/dist/services/cache.test.js.map +1 -1
- package/dist/services/compliance-validator.d.ts.map +1 -1
- package/dist/services/compliance-validator.js +12 -1
- package/dist/services/compliance-validator.js.map +1 -1
- package/dist/services/display-protocol.d.ts +31 -0
- package/dist/services/display-protocol.d.ts.map +1 -0
- package/dist/services/display-protocol.js +73 -0
- package/dist/services/display-protocol.js.map +1 -0
- package/dist/services/effect-tracker.d.ts +81 -0
- package/dist/services/effect-tracker.d.ts.map +1 -0
- package/dist/services/effect-tracker.js +181 -0
- package/dist/services/effect-tracker.js.map +1 -0
- package/dist/services/file-lock.d.ts +31 -0
- package/dist/services/file-lock.d.ts.map +1 -0
- package/dist/services/file-lock.js +124 -0
- package/dist/services/file-lock.js.map +1 -0
- package/dist/services/gitmem-dir.d.ts +7 -0
- package/dist/services/gitmem-dir.d.ts.map +1 -1
- package/dist/services/gitmem-dir.js +21 -0
- package/dist/services/gitmem-dir.js.map +1 -1
- package/dist/services/local-file-storage.d.ts +3 -2
- package/dist/services/local-file-storage.d.ts.map +1 -1
- package/dist/services/local-file-storage.js +30 -43
- package/dist/services/local-file-storage.js.map +1 -1
- package/dist/services/local-vector-search.d.ts +10 -9
- package/dist/services/local-vector-search.d.ts.map +1 -1
- package/dist/services/local-vector-search.js +28 -23
- package/dist/services/local-vector-search.js.map +1 -1
- package/dist/services/metrics.d.ts +7 -2
- package/dist/services/metrics.d.ts.map +1 -1
- package/dist/services/metrics.js +41 -33
- package/dist/services/metrics.js.map +1 -1
- package/dist/services/session-state.d.ts +8 -0
- package/dist/services/session-state.d.ts.map +1 -1
- package/dist/services/session-state.js +9 -2
- package/dist/services/session-state.js.map +1 -1
- package/dist/services/startup.d.ts +12 -13
- package/dist/services/startup.d.ts.map +1 -1
- package/dist/services/startup.js +104 -57
- package/dist/services/startup.js.map +1 -1
- package/dist/services/supabase-client.d.ts +2 -1
- package/dist/services/supabase-client.d.ts.map +1 -1
- package/dist/services/supabase-client.js +22 -16
- package/dist/services/supabase-client.js.map +1 -1
- package/dist/services/thread-dedup.d.ts +9 -0
- package/dist/services/thread-dedup.d.ts.map +1 -1
- package/dist/services/thread-dedup.js +27 -0
- package/dist/services/thread-dedup.js.map +1 -1
- package/dist/services/thread-manager.d.ts.map +1 -1
- package/dist/services/thread-manager.js +38 -16
- package/dist/services/thread-manager.js.map +1 -1
- package/dist/services/thread-suggestions.d.ts.map +1 -1
- package/dist/services/thread-suggestions.js +1 -1
- package/dist/services/thread-suggestions.js.map +1 -1
- package/dist/services/thread-supabase.d.ts +0 -1
- package/dist/services/thread-supabase.d.ts.map +1 -1
- package/dist/services/thread-supabase.js +83 -54
- package/dist/services/thread-supabase.js.map +1 -1
- package/dist/services/timezone.d.ts.map +1 -1
- package/dist/services/timezone.js +1 -0
- package/dist/services/timezone.js.map +1 -1
- package/dist/services/transcript-chunker.d.ts.map +1 -1
- package/dist/services/transcript-chunker.js +18 -4
- package/dist/services/transcript-chunker.js.map +1 -1
- package/dist/services/variant-generation.d.ts +41 -0
- package/dist/services/variant-generation.d.ts.map +1 -0
- package/dist/services/variant-generation.js +263 -0
- package/dist/services/variant-generation.js.map +1 -0
- package/dist/tools/absorb-observations.d.ts.map +1 -1
- package/dist/tools/absorb-observations.js +9 -0
- package/dist/tools/absorb-observations.js.map +1 -1
- package/dist/tools/analyze.d.ts.map +1 -1
- package/dist/tools/analyze.js +13 -2
- package/dist/tools/analyze.js.map +1 -1
- package/dist/tools/archive-learning.d.ts +28 -0
- package/dist/tools/archive-learning.d.ts.map +1 -0
- package/dist/tools/archive-learning.js +81 -0
- package/dist/tools/archive-learning.js.map +1 -0
- package/dist/tools/cleanup-threads.d.ts +1 -0
- package/dist/tools/cleanup-threads.d.ts.map +1 -1
- package/dist/tools/cleanup-threads.js +111 -18
- package/dist/tools/cleanup-threads.js.map +1 -1
- package/dist/tools/confirm-scars.d.ts.map +1 -1
- package/dist/tools/confirm-scars.js +8 -2
- package/dist/tools/confirm-scars.js.map +1 -1
- package/dist/tools/create-decision.d.ts.map +1 -1
- package/dist/tools/create-decision.js +11 -8
- package/dist/tools/create-decision.js.map +1 -1
- package/dist/tools/create-learning.d.ts.map +1 -1
- package/dist/tools/create-learning.js +35 -11
- package/dist/tools/create-learning.js.map +1 -1
- package/dist/tools/create-linear-issue.d.ts +18 -0
- package/dist/tools/create-linear-issue.d.ts.map +1 -0
- package/dist/tools/create-linear-issue.js +197 -0
- package/dist/tools/create-linear-issue.js.map +1 -0
- package/dist/tools/create-thread.d.ts +2 -1
- package/dist/tools/create-thread.d.ts.map +1 -1
- package/dist/tools/create-thread.js +9 -4
- package/dist/tools/create-thread.js.map +1 -1
- package/dist/tools/definitions.d.ts +785 -34
- package/dist/tools/definitions.d.ts.map +1 -1
- package/dist/tools/definitions.js +239 -95
- package/dist/tools/definitions.js.map +1 -1
- package/dist/tools/dismiss-suggestion.d.ts +1 -0
- package/dist/tools/dismiss-suggestion.d.ts.map +1 -1
- package/dist/tools/dismiss-suggestion.js +4 -0
- package/dist/tools/dismiss-suggestion.js.map +1 -1
- package/dist/tools/graph-traverse.d.ts +1 -0
- package/dist/tools/graph-traverse.d.ts.map +1 -1
- package/dist/tools/graph-traverse.js +24 -9
- package/dist/tools/graph-traverse.js.map +1 -1
- package/dist/tools/list-threads.d.ts.map +1 -1
- package/dist/tools/list-threads.js +49 -5
- package/dist/tools/list-threads.js.map +1 -1
- package/dist/tools/log.d.ts +1 -0
- package/dist/tools/log.d.ts.map +1 -1
- package/dist/tools/log.js +84 -17
- package/dist/tools/log.js.map +1 -1
- package/dist/tools/prepare-context.d.ts +1 -0
- package/dist/tools/prepare-context.d.ts.map +1 -1
- package/dist/tools/prepare-context.js +15 -85
- package/dist/tools/prepare-context.js.map +1 -1
- package/dist/tools/promote-suggestion.d.ts +1 -0
- package/dist/tools/promote-suggestion.d.ts.map +1 -1
- package/dist/tools/promote-suggestion.js +5 -0
- package/dist/tools/promote-suggestion.js.map +1 -1
- package/dist/tools/recall.d.ts +2 -0
- package/dist/tools/recall.d.ts.map +1 -1
- package/dist/tools/recall.js +43 -10
- package/dist/tools/recall.js.map +1 -1
- package/dist/tools/recall.test.js +6 -6
- package/dist/tools/recall.test.js.map +1 -1
- package/dist/tools/record-scar-usage-batch.d.ts.map +1 -1
- package/dist/tools/record-scar-usage-batch.js +13 -0
- package/dist/tools/record-scar-usage-batch.js.map +1 -1
- package/dist/tools/record-scar-usage.d.ts.map +1 -1
- package/dist/tools/record-scar-usage.js +6 -0
- package/dist/tools/record-scar-usage.js.map +1 -1
- package/dist/tools/resolve-thread.d.ts.map +1 -1
- package/dist/tools/resolve-thread.js +57 -6
- package/dist/tools/resolve-thread.js.map +1 -1
- package/dist/tools/save-transcript.d.ts +1 -0
- package/dist/tools/save-transcript.d.ts.map +1 -1
- package/dist/tools/save-transcript.js +3 -1
- package/dist/tools/save-transcript.js.map +1 -1
- package/dist/tools/search-transcripts.d.ts +44 -0
- package/dist/tools/search-transcripts.d.ts.map +1 -0
- package/dist/tools/search-transcripts.js +158 -0
- package/dist/tools/search-transcripts.js.map +1 -0
- package/dist/tools/search.d.ts +1 -0
- package/dist/tools/search.d.ts.map +1 -1
- package/dist/tools/search.js +74 -3
- package/dist/tools/search.js.map +1 -1
- package/dist/tools/session-close.d.ts.map +1 -1
- package/dist/tools/session-close.js +563 -326
- package/dist/tools/session-close.js.map +1 -1
- package/dist/tools/session-start.d.ts +10 -6
- package/dist/tools/session-start.d.ts.map +1 -1
- package/dist/tools/session-start.js +317 -426
- package/dist/tools/session-start.js.map +1 -1
- package/dist/types/index.d.ts +37 -4
- package/dist/types/index.d.ts.map +1 -1
- package/hooks/hooks/hooks.json +8 -37
- package/hooks/scripts/auto-retrieve-hook.sh +163 -0
- package/hooks/scripts/post-tool-use.sh +0 -16
- package/hooks/scripts/recall-check.sh +0 -11
- package/hooks/scripts/session-close-check.sh +1 -1
- package/hooks/scripts/session-start.sh +89 -13
- package/hooks/tests/test-hooks.sh +3 -49
- package/package.json +3 -2
- package/schema/setup.sql +1 -1
|
@@ -0,0 +1,642 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* GitMem Init Wizard
|
|
5
|
+
*
|
|
6
|
+
* Interactive setup that detects existing config, prompts, and merges.
|
|
7
|
+
* Usage: npx gitmem init [--yes] [--dry-run] [--project <name>]
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
readFileSync,
|
|
12
|
+
writeFileSync,
|
|
13
|
+
mkdirSync,
|
|
14
|
+
existsSync,
|
|
15
|
+
readdirSync,
|
|
16
|
+
chmodSync,
|
|
17
|
+
} from "fs";
|
|
18
|
+
import { dirname, join } from "path";
|
|
19
|
+
import { fileURLToPath } from "url";
|
|
20
|
+
import { createInterface } from "readline/promises";
|
|
21
|
+
|
|
22
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
23
|
+
const cwd = process.cwd();
|
|
24
|
+
|
|
25
|
+
// Parse flags
|
|
26
|
+
const args = process.argv.slice(2);
|
|
27
|
+
const autoYes = args.includes("--yes") || args.includes("-y");
|
|
28
|
+
const dryRun = args.includes("--dry-run");
|
|
29
|
+
const projectIdx = args.indexOf("--project");
|
|
30
|
+
const projectName = projectIdx !== -1 ? args[projectIdx + 1] : null;
|
|
31
|
+
|
|
32
|
+
// Paths
|
|
33
|
+
const gitmemDir = join(cwd, ".gitmem");
|
|
34
|
+
const mcpJsonPath = join(cwd, ".mcp.json");
|
|
35
|
+
const claudeMdPath = join(cwd, "CLAUDE.md");
|
|
36
|
+
const claudeDir = join(cwd, ".claude");
|
|
37
|
+
const settingsPath = join(claudeDir, "settings.json");
|
|
38
|
+
const settingsLocalPath = join(claudeDir, "settings.local.json");
|
|
39
|
+
const gitignorePath = join(cwd, ".gitignore");
|
|
40
|
+
const templatePath = join(__dirname, "..", "CLAUDE.md.template");
|
|
41
|
+
const starterScarsPath = join(__dirname, "..", "schema", "starter-scars.json");
|
|
42
|
+
const hooksScriptsDir = join(__dirname, "..", "hooks", "scripts");
|
|
43
|
+
|
|
44
|
+
let rl;
|
|
45
|
+
|
|
46
|
+
async function confirm(message, defaultYes = true) {
|
|
47
|
+
if (autoYes) return true;
|
|
48
|
+
if (!rl) {
|
|
49
|
+
rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
50
|
+
}
|
|
51
|
+
const suffix = defaultYes ? "[Y/n]" : "[y/N]";
|
|
52
|
+
const answer = await rl.question(` ${message} ${suffix} `);
|
|
53
|
+
const trimmed = answer.trim().toLowerCase();
|
|
54
|
+
if (trimmed === "") return defaultYes;
|
|
55
|
+
return trimmed === "y" || trimmed === "yes";
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function readJson(path) {
|
|
59
|
+
try {
|
|
60
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
61
|
+
} catch {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function writeJson(path, data) {
|
|
67
|
+
writeFileSync(path, JSON.stringify(data, null, 2) + "\n");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function buildMcpConfig() {
|
|
71
|
+
const supabaseUrl = process.env.SUPABASE_URL;
|
|
72
|
+
if (!supabaseUrl) {
|
|
73
|
+
return { command: "npx", args: ["-y", "gitmem-mcp"] };
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
command: "npx",
|
|
77
|
+
args: ["-y", "gitmem-mcp"],
|
|
78
|
+
env: {
|
|
79
|
+
SUPABASE_URL: supabaseUrl,
|
|
80
|
+
SUPABASE_SERVICE_ROLE_KEY:
|
|
81
|
+
process.env.SUPABASE_SERVICE_ROLE_KEY || "<your-service-role-key>",
|
|
82
|
+
OPENAI_API_KEY:
|
|
83
|
+
process.env.OPENAI_API_KEY || "<your-openai-key-or-remove>",
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function buildHooks() {
|
|
89
|
+
const relScripts = "node_modules/gitmem-mcp/hooks/scripts";
|
|
90
|
+
return {
|
|
91
|
+
SessionStart: [
|
|
92
|
+
{
|
|
93
|
+
hooks: [
|
|
94
|
+
{
|
|
95
|
+
type: "command",
|
|
96
|
+
command: `bash ${relScripts}/session-start.sh`,
|
|
97
|
+
statusMessage: "Initializing GitMem session...",
|
|
98
|
+
timeout: 5000,
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
PreToolUse: [
|
|
104
|
+
{
|
|
105
|
+
matcher: "Bash",
|
|
106
|
+
hooks: [
|
|
107
|
+
{
|
|
108
|
+
type: "command",
|
|
109
|
+
command: `bash ${relScripts}/recall-check.sh`,
|
|
110
|
+
timeout: 5000,
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
matcher: "Write",
|
|
116
|
+
hooks: [
|
|
117
|
+
{
|
|
118
|
+
type: "command",
|
|
119
|
+
command: `bash ${relScripts}/recall-check.sh`,
|
|
120
|
+
timeout: 5000,
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
matcher: "Edit",
|
|
126
|
+
hooks: [
|
|
127
|
+
{
|
|
128
|
+
type: "command",
|
|
129
|
+
command: `bash ${relScripts}/recall-check.sh`,
|
|
130
|
+
timeout: 5000,
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
PostToolUse: [
|
|
136
|
+
{
|
|
137
|
+
matcher: "mcp__gitmem__recall",
|
|
138
|
+
hooks: [
|
|
139
|
+
{
|
|
140
|
+
type: "command",
|
|
141
|
+
command: `bash ${relScripts}/post-tool-use.sh`,
|
|
142
|
+
timeout: 3000,
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
matcher: "mcp__gitmem__search",
|
|
148
|
+
hooks: [
|
|
149
|
+
{
|
|
150
|
+
type: "command",
|
|
151
|
+
command: `bash ${relScripts}/post-tool-use.sh`,
|
|
152
|
+
timeout: 3000,
|
|
153
|
+
},
|
|
154
|
+
],
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
matcher: "Bash",
|
|
158
|
+
hooks: [
|
|
159
|
+
{
|
|
160
|
+
type: "command",
|
|
161
|
+
command: `bash ${relScripts}/post-tool-use.sh`,
|
|
162
|
+
timeout: 3000,
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
matcher: "Write",
|
|
168
|
+
hooks: [
|
|
169
|
+
{
|
|
170
|
+
type: "command",
|
|
171
|
+
command: `bash ${relScripts}/post-tool-use.sh`,
|
|
172
|
+
timeout: 3000,
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
matcher: "Edit",
|
|
178
|
+
hooks: [
|
|
179
|
+
{
|
|
180
|
+
type: "command",
|
|
181
|
+
command: `bash ${relScripts}/post-tool-use.sh`,
|
|
182
|
+
timeout: 3000,
|
|
183
|
+
},
|
|
184
|
+
],
|
|
185
|
+
},
|
|
186
|
+
],
|
|
187
|
+
Stop: [
|
|
188
|
+
{
|
|
189
|
+
hooks: [
|
|
190
|
+
{
|
|
191
|
+
type: "command",
|
|
192
|
+
command: `bash ${relScripts}/session-close-check.sh`,
|
|
193
|
+
timeout: 5000,
|
|
194
|
+
},
|
|
195
|
+
],
|
|
196
|
+
},
|
|
197
|
+
],
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function isGitmemHook(entry) {
|
|
202
|
+
if (!entry.hooks || !Array.isArray(entry.hooks)) return false;
|
|
203
|
+
return entry.hooks.some(
|
|
204
|
+
(h) => typeof h.command === "string" && h.command.includes("gitmem")
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function getClaudeMdTemplate() {
|
|
209
|
+
try {
|
|
210
|
+
return readFileSync(templatePath, "utf-8");
|
|
211
|
+
} catch {
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ── Steps ──
|
|
217
|
+
|
|
218
|
+
async function stepMemoryStore() {
|
|
219
|
+
const learningsPath = join(gitmemDir, "learnings.json");
|
|
220
|
+
const exists = existsSync(learningsPath);
|
|
221
|
+
let existingCount = 0;
|
|
222
|
+
if (exists) {
|
|
223
|
+
try {
|
|
224
|
+
existingCount = JSON.parse(readFileSync(learningsPath, "utf-8")).length;
|
|
225
|
+
} catch {}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
let starterScars;
|
|
229
|
+
try {
|
|
230
|
+
starterScars = JSON.parse(readFileSync(starterScarsPath, "utf-8"));
|
|
231
|
+
} catch {
|
|
232
|
+
console.log(" ! Could not read starter-scars.json. Skipping.");
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (exists && existingCount >= starterScars.length) {
|
|
237
|
+
console.log(
|
|
238
|
+
` Already configured (${existingCount} scars in .gitmem/). Skipping.`
|
|
239
|
+
);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const prompt = exists
|
|
244
|
+
? `Merge ${starterScars.length} starter scars into .gitmem/? (${existingCount} existing)`
|
|
245
|
+
: `Create .gitmem/ with ${starterScars.length} starter scars?`;
|
|
246
|
+
|
|
247
|
+
if (!(await confirm(prompt))) {
|
|
248
|
+
console.log(" Skipped.");
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (dryRun) {
|
|
253
|
+
console.log(` [dry-run] Would create .gitmem/ with ${starterScars.length} starter scars`);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (!existsSync(gitmemDir)) {
|
|
258
|
+
mkdirSync(gitmemDir, { recursive: true });
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Config
|
|
262
|
+
const configPath = join(gitmemDir, "config.json");
|
|
263
|
+
if (!existsSync(configPath)) {
|
|
264
|
+
const config = {};
|
|
265
|
+
if (projectName) config.project = projectName;
|
|
266
|
+
writeJson(configPath, config);
|
|
267
|
+
} else if (projectName) {
|
|
268
|
+
const config = readJson(configPath) || {};
|
|
269
|
+
config.project = projectName;
|
|
270
|
+
writeJson(configPath, config);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Merge scars
|
|
274
|
+
let existing = [];
|
|
275
|
+
if (existsSync(learningsPath)) {
|
|
276
|
+
existing = readJson(learningsPath) || [];
|
|
277
|
+
}
|
|
278
|
+
const existingIds = new Set(existing.map((s) => s.id));
|
|
279
|
+
let added = 0;
|
|
280
|
+
const now = new Date().toISOString();
|
|
281
|
+
for (const scar of starterScars) {
|
|
282
|
+
if (!existingIds.has(scar.id)) {
|
|
283
|
+
existing.push({ ...scar, created_at: now, source_date: now.slice(0, 10) });
|
|
284
|
+
added++;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
writeJson(learningsPath, existing);
|
|
288
|
+
|
|
289
|
+
// Empty collection files
|
|
290
|
+
for (const file of ["sessions.json", "decisions.json", "scar-usage.json"]) {
|
|
291
|
+
const filePath = join(gitmemDir, file);
|
|
292
|
+
if (!existsSync(filePath)) {
|
|
293
|
+
writeFileSync(filePath, "[]");
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
console.log(
|
|
298
|
+
` Created .gitmem/ with ${starterScars.length} starter scars` +
|
|
299
|
+
(added < starterScars.length
|
|
300
|
+
? ` (${added} new, ${starterScars.length - added} already existed)`
|
|
301
|
+
: "")
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
async function stepMcpServer() {
|
|
306
|
+
const existing = readJson(mcpJsonPath);
|
|
307
|
+
const hasGitmem =
|
|
308
|
+
existing?.mcpServers?.gitmem || existing?.mcpServers?.["gitmem-mcp"];
|
|
309
|
+
|
|
310
|
+
if (hasGitmem) {
|
|
311
|
+
console.log(" Already configured in .mcp.json. Skipping.");
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const serverCount = existing?.mcpServers
|
|
316
|
+
? Object.keys(existing.mcpServers).length
|
|
317
|
+
: 0;
|
|
318
|
+
const tierLabel = process.env.SUPABASE_URL ? "pro" : "free";
|
|
319
|
+
const prompt = existing
|
|
320
|
+
? `Add gitmem to .mcp.json? (${serverCount} existing server${serverCount !== 1 ? "s" : ""} preserved)`
|
|
321
|
+
: "Create .mcp.json with gitmem server?";
|
|
322
|
+
|
|
323
|
+
if (!(await confirm(prompt))) {
|
|
324
|
+
console.log(" Skipped.");
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (dryRun) {
|
|
329
|
+
console.log(` [dry-run] Would add gitmem entry to .mcp.json (${tierLabel} tier)`);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const config = existing || { mcpServers: {} };
|
|
334
|
+
if (!config.mcpServers) config.mcpServers = {};
|
|
335
|
+
config.mcpServers.gitmem = buildMcpConfig();
|
|
336
|
+
writeJson(mcpJsonPath, config);
|
|
337
|
+
|
|
338
|
+
console.log(
|
|
339
|
+
` Added gitmem entry to .mcp.json (${tierLabel} tier` +
|
|
340
|
+
(process.env.SUPABASE_URL ? " — Supabase detected" : " — local storage") +
|
|
341
|
+
")"
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
async function stepClaudeMd() {
|
|
346
|
+
const template = getClaudeMdTemplate();
|
|
347
|
+
if (!template) {
|
|
348
|
+
console.log(" ! CLAUDE.md.template not found. Skipping.");
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const exists = existsSync(claudeMdPath);
|
|
353
|
+
let content = exists ? readFileSync(claudeMdPath, "utf-8") : "";
|
|
354
|
+
|
|
355
|
+
if (content.includes("<!-- gitmem:start -->")) {
|
|
356
|
+
console.log(" Already configured in CLAUDE.md. Skipping.");
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const prompt = exists
|
|
361
|
+
? "Append gitmem section to CLAUDE.md?"
|
|
362
|
+
: "Create CLAUDE.md with gitmem instructions?";
|
|
363
|
+
|
|
364
|
+
if (!(await confirm(prompt))) {
|
|
365
|
+
console.log(" Skipped.");
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (dryRun) {
|
|
370
|
+
console.log(
|
|
371
|
+
` [dry-run] Would ${exists ? "append gitmem section to" : "create"} CLAUDE.md`
|
|
372
|
+
);
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Template should already have delimiters, but ensure they're there
|
|
377
|
+
let block = template;
|
|
378
|
+
if (!block.includes("<!-- gitmem:start -->")) {
|
|
379
|
+
block = `<!-- gitmem:start -->\n${block}\n<!-- gitmem:end -->`;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (exists) {
|
|
383
|
+
content = content.trimEnd() + "\n\n" + block + "\n";
|
|
384
|
+
} else {
|
|
385
|
+
content = block + "\n";
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
writeFileSync(claudeMdPath, content);
|
|
389
|
+
console.log(
|
|
390
|
+
` ${exists ? "Added gitmem section to" : "Created"} CLAUDE.md`
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
async function stepPermissions() {
|
|
395
|
+
const existing = readJson(settingsPath);
|
|
396
|
+
const allow = existing?.permissions?.allow || [];
|
|
397
|
+
const pattern = "mcp__gitmem__*";
|
|
398
|
+
|
|
399
|
+
if (allow.includes(pattern)) {
|
|
400
|
+
console.log(" Already configured in .claude/settings.json. Skipping.");
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (!(await confirm("Add mcp__gitmem__* to .claude/settings.json?"))) {
|
|
405
|
+
console.log(" Skipped.");
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (dryRun) {
|
|
410
|
+
console.log(" [dry-run] Would add gitmem tool permissions");
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const settings = existing || {};
|
|
415
|
+
if (!existsSync(claudeDir)) {
|
|
416
|
+
mkdirSync(claudeDir, { recursive: true });
|
|
417
|
+
}
|
|
418
|
+
const permissions = settings.permissions || {};
|
|
419
|
+
const newAllow = permissions.allow || [];
|
|
420
|
+
newAllow.push(pattern);
|
|
421
|
+
settings.permissions = { ...permissions, allow: newAllow };
|
|
422
|
+
writeJson(settingsPath, settings);
|
|
423
|
+
|
|
424
|
+
console.log(" Added gitmem tool permissions");
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
async function stepHooks() {
|
|
428
|
+
const existing = readJson(settingsPath);
|
|
429
|
+
const hooks = existing?.hooks || {};
|
|
430
|
+
const hasGitmem = JSON.stringify(hooks).includes("gitmem");
|
|
431
|
+
|
|
432
|
+
if (hasGitmem) {
|
|
433
|
+
console.log(" Already configured in .claude/settings.json. Skipping.");
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Count existing non-gitmem hooks
|
|
438
|
+
let existingHookCount = 0;
|
|
439
|
+
for (const entries of Object.values(hooks)) {
|
|
440
|
+
if (Array.isArray(entries)) {
|
|
441
|
+
existingHookCount += entries.filter((e) => !isGitmemHook(e)).length;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const prompt =
|
|
446
|
+
existingHookCount > 0
|
|
447
|
+
? `Merge gitmem hooks into .claude/settings.json? (${existingHookCount} existing hook${existingHookCount !== 1 ? "s" : ""} preserved)`
|
|
448
|
+
: "Add gitmem lifecycle hooks to .claude/settings.json?";
|
|
449
|
+
|
|
450
|
+
if (!(await confirm(prompt))) {
|
|
451
|
+
console.log(" Skipped.");
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if (dryRun) {
|
|
456
|
+
console.log(" [dry-run] Would merge 4 gitmem hook types");
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Make hook scripts executable
|
|
461
|
+
if (existsSync(hooksScriptsDir)) {
|
|
462
|
+
try {
|
|
463
|
+
for (const file of readdirSync(hooksScriptsDir)) {
|
|
464
|
+
if (file.endsWith(".sh")) {
|
|
465
|
+
chmodSync(join(hooksScriptsDir, file), 0o755);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
} catch {
|
|
469
|
+
// Non-critical
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const settings = existing || {};
|
|
474
|
+
if (!existsSync(claudeDir)) {
|
|
475
|
+
mkdirSync(claudeDir, { recursive: true });
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
const gitmemHooks = buildHooks();
|
|
479
|
+
const merged = { ...(settings.hooks || {}) };
|
|
480
|
+
|
|
481
|
+
for (const [eventType, gitmemEntries] of Object.entries(gitmemHooks)) {
|
|
482
|
+
const existingEntries = merged[eventType] || [];
|
|
483
|
+
const nonGitmem = existingEntries.filter((e) => !isGitmemHook(e));
|
|
484
|
+
merged[eventType] = [...nonGitmem, ...gitmemEntries];
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
settings.hooks = merged;
|
|
488
|
+
writeJson(settingsPath, settings);
|
|
489
|
+
|
|
490
|
+
const preservedMsg =
|
|
491
|
+
existingHookCount > 0
|
|
492
|
+
? ` (${existingHookCount} existing hook${existingHookCount !== 1 ? "s" : ""} preserved)`
|
|
493
|
+
: "";
|
|
494
|
+
console.log(` Merged 4 gitmem hook types${preservedMsg}`);
|
|
495
|
+
|
|
496
|
+
// Warn about settings.local.json
|
|
497
|
+
if (existsSync(settingsLocalPath)) {
|
|
498
|
+
const local = readJson(settingsLocalPath);
|
|
499
|
+
if (local?.hooks) {
|
|
500
|
+
console.log("");
|
|
501
|
+
console.log(
|
|
502
|
+
" Note: .claude/settings.local.json also has hooks."
|
|
503
|
+
);
|
|
504
|
+
console.log(
|
|
505
|
+
" Local hooks take precedence. You may need to manually merge."
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
async function stepGitignore() {
|
|
512
|
+
const exists = existsSync(gitignorePath);
|
|
513
|
+
let content = exists ? readFileSync(gitignorePath, "utf-8") : "";
|
|
514
|
+
|
|
515
|
+
if (content.includes(".gitmem/")) {
|
|
516
|
+
console.log(" Already configured in .gitignore. Skipping.");
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
if (!(await confirm("Add .gitmem/ to .gitignore?"))) {
|
|
521
|
+
console.log(" Skipped.");
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
if (dryRun) {
|
|
526
|
+
console.log(" [dry-run] Would add .gitmem/ to .gitignore");
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
if (exists) {
|
|
531
|
+
content = content.trimEnd() + "\n.gitmem/\n";
|
|
532
|
+
} else {
|
|
533
|
+
content = ".gitmem/\n";
|
|
534
|
+
}
|
|
535
|
+
writeFileSync(gitignorePath, content);
|
|
536
|
+
|
|
537
|
+
console.log(` ${exists ? "Updated" : "Created"} .gitignore`);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// ── Main ──
|
|
541
|
+
|
|
542
|
+
async function main() {
|
|
543
|
+
const pkg = readJson(join(__dirname, "..", "package.json"));
|
|
544
|
+
const version = pkg?.version || "1.0.0";
|
|
545
|
+
|
|
546
|
+
console.log("");
|
|
547
|
+
console.log(` gitmem v${version} — Setup`);
|
|
548
|
+
if (dryRun) {
|
|
549
|
+
console.log(" (dry-run mode — no files will be written)");
|
|
550
|
+
}
|
|
551
|
+
console.log("");
|
|
552
|
+
|
|
553
|
+
// Detect environment
|
|
554
|
+
console.log(" Detecting environment...");
|
|
555
|
+
const detections = [];
|
|
556
|
+
|
|
557
|
+
if (existsSync(mcpJsonPath)) {
|
|
558
|
+
const mcp = readJson(mcpJsonPath);
|
|
559
|
+
const count = mcp?.mcpServers ? Object.keys(mcp.mcpServers).length : 0;
|
|
560
|
+
detections.push(
|
|
561
|
+
` .mcp.json found (${count} server${count !== 1 ? "s" : ""})`
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (existsSync(claudeMdPath)) {
|
|
566
|
+
const content = readFileSync(claudeMdPath, "utf-8");
|
|
567
|
+
const hasGitmem = content.includes("<!-- gitmem:start -->");
|
|
568
|
+
detections.push(
|
|
569
|
+
` CLAUDE.md found (${hasGitmem ? "has gitmem section" : "no gitmem section"})`
|
|
570
|
+
);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
if (existsSync(settingsPath)) {
|
|
574
|
+
const settings = readJson(settingsPath);
|
|
575
|
+
const hookCount = settings?.hooks
|
|
576
|
+
? Object.values(settings.hooks).flat().length
|
|
577
|
+
: 0;
|
|
578
|
+
detections.push(
|
|
579
|
+
` .claude/settings.json found (${hookCount} hook${hookCount !== 1 ? "s" : ""})`
|
|
580
|
+
);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
if (existsSync(gitignorePath)) {
|
|
584
|
+
detections.push(" .gitignore found");
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
if (existsSync(gitmemDir)) {
|
|
588
|
+
detections.push(" .gitmem/ found");
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
for (const d of detections) {
|
|
592
|
+
console.log(d);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
const tier = process.env.SUPABASE_URL ? "pro" : "free";
|
|
596
|
+
console.log(
|
|
597
|
+
` Tier: ${tier}` +
|
|
598
|
+
(tier === "free" ? " (no SUPABASE_URL detected)" : " (SUPABASE_URL detected)")
|
|
599
|
+
);
|
|
600
|
+
console.log("");
|
|
601
|
+
|
|
602
|
+
// Run steps
|
|
603
|
+
console.log(" Step 1/6 — Memory Store");
|
|
604
|
+
await stepMemoryStore();
|
|
605
|
+
console.log("");
|
|
606
|
+
|
|
607
|
+
console.log(" Step 2/6 — MCP Server");
|
|
608
|
+
await stepMcpServer();
|
|
609
|
+
console.log("");
|
|
610
|
+
|
|
611
|
+
console.log(" Step 3/6 — Project Instructions");
|
|
612
|
+
await stepClaudeMd();
|
|
613
|
+
console.log("");
|
|
614
|
+
|
|
615
|
+
console.log(" Step 4/6 — Tool Permissions");
|
|
616
|
+
await stepPermissions();
|
|
617
|
+
console.log("");
|
|
618
|
+
|
|
619
|
+
console.log(" Step 5/6 — Lifecycle Hooks");
|
|
620
|
+
await stepHooks();
|
|
621
|
+
console.log("");
|
|
622
|
+
|
|
623
|
+
console.log(" Step 6/6 — Gitignore");
|
|
624
|
+
await stepGitignore();
|
|
625
|
+
console.log("");
|
|
626
|
+
|
|
627
|
+
if (dryRun) {
|
|
628
|
+
console.log(" Dry run complete — no files were modified.");
|
|
629
|
+
} else {
|
|
630
|
+
console.log(" Setup complete! Start Claude Code — memory is active.");
|
|
631
|
+
console.log(" To remove: npx gitmem uninstall");
|
|
632
|
+
}
|
|
633
|
+
console.log("");
|
|
634
|
+
|
|
635
|
+
if (rl) rl.close();
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
main().catch((err) => {
|
|
639
|
+
console.error("Error:", err.message || err);
|
|
640
|
+
if (rl) rl.close();
|
|
641
|
+
process.exit(1);
|
|
642
|
+
});
|