claude-launchpad 0.6.1 → 0.7.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.
@@ -0,0 +1,217 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ detectProject
4
+ } from "./chunk-NAW47BYA.js";
5
+ import {
6
+ initStorage
7
+ } from "./chunk-EBM7RBPB.js";
8
+ import "./chunk-TALTTAMW.js";
9
+ import "./chunk-IILH26C7.js";
10
+ import "./chunk-2H7UOFLK.js";
11
+
12
+ // src/commands/memory/subcommands/extract.ts
13
+ import { existsSync, readFileSync } from "fs";
14
+
15
+ // src/commands/memory/services/session-service.ts
16
+ function parseTranscript(jsonlContent, maxMessages = 50) {
17
+ const lines = jsonlContent.split("\n").filter((l) => l.trim().length > 0);
18
+ const textParts = [];
19
+ const startIdx = Math.max(0, lines.length - maxMessages);
20
+ for (let i = startIdx; i < lines.length; i++) {
21
+ try {
22
+ const msg = JSON.parse(lines[i]);
23
+ if (msg.type !== "user" && msg.type !== "assistant") continue;
24
+ const content = msg.message?.content;
25
+ if (typeof content === "string") {
26
+ textParts.push(content);
27
+ } else if (Array.isArray(content)) {
28
+ for (const block of content) {
29
+ if (typeof block === "object" && block !== null && "type" in block && block.type === "text" && "text" in block) {
30
+ textParts.push(block.text);
31
+ }
32
+ }
33
+ }
34
+ } catch {
35
+ }
36
+ }
37
+ return textParts.join("\n");
38
+ }
39
+ var MIN_FACT_LENGTH = 30;
40
+ function isNoiseLine(line) {
41
+ if (/<\/?[a-z-]+>/i.test(line)) return true;
42
+ if (/^\|.*\|$/.test(line)) return true;
43
+ if (/^```/.test(line)) return true;
44
+ const alphaRatio = (line.match(/[a-zA-Z]/g)?.length ?? 0) / line.length;
45
+ if (alphaRatio < 0.4 && line.length > 20) return true;
46
+ return false;
47
+ }
48
+ function extractFacts(transcript) {
49
+ const facts = [];
50
+ const lines = transcript.split("\n");
51
+ for (const line of lines) {
52
+ const trimmed = line.trim();
53
+ if (!trimmed || trimmed.length < 10) continue;
54
+ if (isNoiseLine(trimmed)) continue;
55
+ const decisionMatch = trimmed.match(/decided?\s+(?:to\s+)?(.+?)\s+because\s+(.+)/i);
56
+ if (decisionMatch?.[1] && decisionMatch[2]) {
57
+ facts.push({
58
+ type: "semantic",
59
+ content: `Decision: ${decisionMatch[1].trim()}. Reason: ${decisionMatch[2].trim()}`,
60
+ tags: ["decision"],
61
+ importance: 0.7
62
+ });
63
+ continue;
64
+ }
65
+ const fixMatch = trimmed.match(/(?:fixed|resolved|solved)\s+(.+?)\s+by\s+(.+)/i);
66
+ if (fixMatch?.[1] && fixMatch[2]) {
67
+ facts.push({
68
+ type: "episodic",
69
+ content: `Fixed: ${fixMatch[1].trim()}. Solution: ${fixMatch[2].trim()}`,
70
+ tags: ["bugfix"],
71
+ importance: 0.6
72
+ });
73
+ continue;
74
+ }
75
+ const learnMatch = trimmed.match(/(?:learned|discovered|found out|realized)\s+(?:that\s+)?(.+)/i);
76
+ if (learnMatch?.[1]) {
77
+ facts.push({
78
+ type: "semantic",
79
+ content: learnMatch[1].trim(),
80
+ tags: ["learning"],
81
+ importance: 0.6
82
+ });
83
+ continue;
84
+ }
85
+ const proceduralMatch = trimmed.match(/(?:to\s+(.+?),\s+(?:use|run|execute|call)\s+(.+))/i);
86
+ if (proceduralMatch?.[1] && proceduralMatch[2]) {
87
+ facts.push({
88
+ type: "procedural",
89
+ content: `To ${proceduralMatch[1].trim()}: ${proceduralMatch[2].trim()}`,
90
+ tags: ["howto"],
91
+ importance: 0.6
92
+ });
93
+ continue;
94
+ }
95
+ const gotchaMatch = trimmed.match(/(?:gotcha|watch out|pitfall)[:!]\s*(.+)/i);
96
+ if (gotchaMatch?.[1] && gotchaMatch[1].trim().length >= MIN_FACT_LENGTH) {
97
+ facts.push({
98
+ type: "semantic",
99
+ content: `Gotcha: ${gotchaMatch[1].trim()}`,
100
+ tags: ["gotcha"],
101
+ importance: 0.7
102
+ });
103
+ continue;
104
+ }
105
+ }
106
+ return facts;
107
+ }
108
+
109
+ // src/commands/memory/subcommands/extract.ts
110
+ async function runExtract() {
111
+ let stdinData = "";
112
+ try {
113
+ stdinData = await readStdin(5e3);
114
+ } catch {
115
+ process.stderr.write("[agentic-memory] extract: no stdin data received\n");
116
+ return;
117
+ }
118
+ if (!stdinData.trim()) {
119
+ process.stderr.write("[agentic-memory] extract: empty stdin\n");
120
+ return;
121
+ }
122
+ let hookInput;
123
+ try {
124
+ hookInput = JSON.parse(stdinData);
125
+ } catch {
126
+ process.stderr.write("[agentic-memory] extract: invalid JSON on stdin\n");
127
+ return;
128
+ }
129
+ const transcriptPath = hookInput.transcript_path;
130
+ if (!transcriptPath || !existsSync(transcriptPath)) {
131
+ process.stderr.write(`[agentic-memory] extract: transcript not found: ${transcriptPath}
132
+ `);
133
+ return;
134
+ }
135
+ let transcriptContent;
136
+ try {
137
+ transcriptContent = readFileSync(transcriptPath, "utf-8");
138
+ } catch (err) {
139
+ process.stderr.write(`[agentic-memory] extract: failed to read transcript: ${err instanceof Error ? err.message : err}
140
+ `);
141
+ return;
142
+ }
143
+ const text = parseTranscript(transcriptContent);
144
+ if (!text || text.length < 50) return;
145
+ const facts = extractFacts(text);
146
+ if (facts.length === 0) return;
147
+ const ctx = initStorage();
148
+ const project = detectProject(hookInput.cwd ?? process.cwd());
149
+ try {
150
+ let stored = 0;
151
+ for (const fact of facts) {
152
+ try {
153
+ const existing = ctx.searchRepo.searchFts({
154
+ query: fact.content.slice(0, 100),
155
+ limit: 1
156
+ });
157
+ if (existing.length > 0 && Math.abs(existing[0].rank) > 15) {
158
+ continue;
159
+ }
160
+ } catch {
161
+ }
162
+ ctx.memoryRepo.create(
163
+ {
164
+ type: fact.type,
165
+ content: fact.content,
166
+ tags: [...fact.tags],
167
+ importance: fact.importance,
168
+ source: "hook",
169
+ project: project ?? void 0
170
+ },
171
+ null
172
+ );
173
+ stored++;
174
+ }
175
+ if (stored > 0) {
176
+ process.stderr.write(`[agentic-memory] extract: stored ${stored} facts from transcript
177
+ `);
178
+ }
179
+ } finally {
180
+ ctx.close();
181
+ }
182
+ }
183
+ function readStdin(timeoutMs) {
184
+ if (process.stdin.isTTY) return Promise.resolve("");
185
+ return new Promise((resolve, reject) => {
186
+ let settled = false;
187
+ const chunks = [];
188
+ const settle = (fn) => {
189
+ if (settled) return;
190
+ settled = true;
191
+ clearTimeout(timer);
192
+ process.stdin.removeListener("data", onData);
193
+ process.stdin.removeListener("end", onEnd);
194
+ process.stdin.removeListener("error", onError);
195
+ fn();
196
+ };
197
+ const timer = setTimeout(() => {
198
+ settle(() => resolve(chunks.length > 0 ? Buffer.concat(chunks).toString("utf-8") : ""));
199
+ }, timeoutMs);
200
+ const onData = (chunk) => {
201
+ chunks.push(chunk);
202
+ };
203
+ const onEnd = () => {
204
+ settle(() => resolve(Buffer.concat(chunks).toString("utf-8")));
205
+ };
206
+ const onError = (err) => {
207
+ settle(() => reject(err));
208
+ };
209
+ process.stdin.on("data", onData);
210
+ process.stdin.on("end", onEnd);
211
+ process.stdin.on("error", onError);
212
+ });
213
+ }
214
+ export {
215
+ runExtract
216
+ };
217
+ //# sourceMappingURL=extract-NVAXO5CK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/memory/subcommands/extract.ts","../src/commands/memory/services/session-service.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { parseTranscript, extractFacts } from '../services/session-service.js';\nimport { detectProject } from '../utils/project.js';\nimport { initStorage } from './init-storage.js';\n\nexport async function runExtract(): Promise<void> {\n // Read hook input from stdin (Claude Code pipes JSON)\n let stdinData = '';\n try {\n stdinData = await readStdin(5000);\n } catch {\n process.stderr.write('[agentic-memory] extract: no stdin data received\\n');\n return;\n }\n\n if (!stdinData.trim()) {\n process.stderr.write('[agentic-memory] extract: empty stdin\\n');\n return;\n }\n\n let hookInput: { transcript_path?: string; cwd?: string };\n try {\n hookInput = JSON.parse(stdinData) as { transcript_path?: string; cwd?: string };\n } catch {\n process.stderr.write('[agentic-memory] extract: invalid JSON on stdin\\n');\n return;\n }\n\n const transcriptPath = hookInput.transcript_path;\n if (!transcriptPath || !existsSync(transcriptPath)) {\n process.stderr.write(`[agentic-memory] extract: transcript not found: ${transcriptPath}\\n`);\n return;\n }\n\n let transcriptContent: string;\n try {\n transcriptContent = readFileSync(transcriptPath, 'utf-8');\n } catch (err) {\n process.stderr.write(`[agentic-memory] extract: failed to read transcript: ${err instanceof Error ? err.message : err}\\n`);\n return;\n }\n\n const text = parseTranscript(transcriptContent);\n if (!text || text.length < 50) return;\n\n const facts = extractFacts(text);\n if (facts.length === 0) return;\n\n const ctx = initStorage();\n const project = detectProject(hookInput.cwd ?? process.cwd());\n\n try {\n let stored = 0;\n for (const fact of facts) {\n // Dedup: check if similar content already exists\n try {\n const existing = ctx.searchRepo.searchFts({\n query: fact.content.slice(0, 100),\n limit: 1,\n });\n if (existing.length > 0 && Math.abs(existing[0]!.rank) > 15) {\n continue; // Strong text match = likely duplicate\n }\n } catch {\n // Dedup is best-effort\n }\n\n ctx.memoryRepo.create(\n {\n type: fact.type,\n content: fact.content,\n tags: [...fact.tags],\n importance: fact.importance,\n source: 'hook',\n project: project ?? undefined,\n },\n null,\n );\n stored++;\n }\n\n if (stored > 0) {\n process.stderr.write(`[agentic-memory] extract: stored ${stored} facts from transcript\\n`);\n }\n } finally {\n ctx.close();\n }\n}\n\nfunction readStdin(timeoutMs: number): Promise<string> {\n if (process.stdin.isTTY) return Promise.resolve('');\n\n return new Promise((resolve, reject) => {\n let settled = false;\n const chunks: Buffer[] = [];\n\n const settle = (fn: () => void) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n process.stdin.removeListener('data', onData);\n process.stdin.removeListener('end', onEnd);\n process.stdin.removeListener('error', onError);\n fn();\n };\n\n const timer = setTimeout(() => {\n settle(() => resolve(chunks.length > 0 ? Buffer.concat(chunks).toString('utf-8') : ''));\n }, timeoutMs);\n\n const onData = (chunk: Buffer) => { chunks.push(chunk); };\n const onEnd = () => { settle(() => resolve(Buffer.concat(chunks).toString('utf-8'))); };\n const onError = (err: Error) => { settle(() => reject(err)); };\n\n process.stdin.on('data', onData);\n process.stdin.on('end', onEnd);\n process.stdin.on('error', onError);\n });\n}\n","// ── Transcript Parsing ──────────────────────────────────────\n\n/**\n * Parse a Claude Code JSONL transcript file into plain text.\n * Extracts text content from user and assistant messages.\n * Only processes the last `maxMessages` messages to stay focused.\n */\nexport function parseTranscript(jsonlContent: string, maxMessages = 50): string {\n const lines = jsonlContent.split('\\n').filter(l => l.trim().length > 0);\n const textParts: string[] = [];\n\n const startIdx = Math.max(0, lines.length - maxMessages);\n\n for (let i = startIdx; i < lines.length; i++) {\n try {\n const msg = JSON.parse(lines[i]!) as {\n type?: string;\n message?: { role?: string; content?: unknown };\n };\n\n if (msg.type !== 'user' && msg.type !== 'assistant') continue;\n\n const content = msg.message?.content;\n if (typeof content === 'string') {\n textParts.push(content);\n } else if (Array.isArray(content)) {\n for (const block of content) {\n if (typeof block === 'object' && block !== null && 'type' in block && block.type === 'text' && 'text' in block) {\n textParts.push(block.text as string);\n }\n }\n }\n } catch {\n // Skip malformed lines\n }\n }\n\n return textParts.join('\\n');\n}\n\n// ── Fact Extraction (for future session-end processing) ──────\n\nexport interface ExtractedFact {\n readonly type: 'episodic' | 'semantic' | 'procedural' | 'pattern';\n readonly content: string;\n readonly tags: readonly string[];\n readonly importance: number;\n}\n\nconst MIN_FACT_LENGTH = 30;\n\nfunction isNoiseLine(line: string): boolean {\n if (/<\\/?[a-z-]+>/i.test(line)) return true;\n if (/^\\|.*\\|$/.test(line)) return true;\n if (/^```/.test(line)) return true;\n const alphaRatio = (line.match(/[a-zA-Z]/g)?.length ?? 0) / line.length;\n if (alphaRatio < 0.4 && line.length > 20) return true;\n return false;\n}\n\nexport function extractFacts(transcript: string): readonly ExtractedFact[] {\n const facts: ExtractedFact[] = [];\n const lines = transcript.split('\\n');\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.length < 10) continue;\n\n if (isNoiseLine(trimmed)) continue;\n\n // Pattern: \"decided to X because Y\" -> semantic decision\n const decisionMatch = trimmed.match(/decided?\\s+(?:to\\s+)?(.+?)\\s+because\\s+(.+)/i);\n if (decisionMatch?.[1] && decisionMatch[2]) {\n facts.push({\n type: 'semantic',\n content: `Decision: ${decisionMatch[1].trim()}. Reason: ${decisionMatch[2].trim()}`,\n tags: ['decision'],\n importance: 0.7,\n });\n continue;\n }\n\n // Pattern: \"fixed X by Y\" -> episodic fix\n const fixMatch = trimmed.match(/(?:fixed|resolved|solved)\\s+(.+?)\\s+by\\s+(.+)/i);\n if (fixMatch?.[1] && fixMatch[2]) {\n facts.push({\n type: 'episodic',\n content: `Fixed: ${fixMatch[1].trim()}. Solution: ${fixMatch[2].trim()}`,\n tags: ['bugfix'],\n importance: 0.6,\n });\n continue;\n }\n\n // Pattern: \"learned that X\" -> semantic\n const learnMatch = trimmed.match(/(?:learned|discovered|found out|realized)\\s+(?:that\\s+)?(.+)/i);\n if (learnMatch?.[1]) {\n facts.push({\n type: 'semantic',\n content: learnMatch[1].trim(),\n tags: ['learning'],\n importance: 0.6,\n });\n continue;\n }\n\n // Pattern: \"to X, use Y\" -> procedural\n const proceduralMatch = trimmed.match(/(?:to\\s+(.+?),\\s+(?:use|run|execute|call)\\s+(.+))/i);\n if (proceduralMatch?.[1] && proceduralMatch[2]) {\n facts.push({\n type: 'procedural',\n content: `To ${proceduralMatch[1].trim()}: ${proceduralMatch[2].trim()}`,\n tags: ['howto'],\n importance: 0.6,\n });\n continue;\n }\n\n // Pattern: \"gotcha: X\" -> semantic gotcha\n const gotchaMatch = trimmed.match(/(?:gotcha|watch out|pitfall)[:!]\\s*(.+)/i);\n if (gotchaMatch?.[1] && gotchaMatch[1].trim().length >= MIN_FACT_LENGTH) {\n facts.push({\n type: 'semantic',\n content: `Gotcha: ${gotchaMatch[1].trim()}`,\n tags: ['gotcha'],\n importance: 0.7,\n });\n continue;\n }\n }\n\n return facts;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,230 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ readSettingsJson,
4
+ writeSettingsJson
5
+ } from "./chunk-CSLWJEGD.js";
6
+ import {
7
+ log
8
+ } from "./chunk-6ZVXZ4EF.js";
9
+ import {
10
+ closeDatabase,
11
+ createDatabase,
12
+ loadConfig,
13
+ migrate,
14
+ resolveDataDir
15
+ } from "./chunk-IILH26C7.js";
16
+ import "./chunk-2H7UOFLK.js";
17
+
18
+ // src/commands/memory/subcommands/install.ts
19
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
20
+ import { join } from "path";
21
+ import { execSync } from "child_process";
22
+ async function runInstall(opts) {
23
+ log.blank();
24
+ log.step("Memory system - install");
25
+ log.blank();
26
+ const config = loadConfig(opts.dbPath ? { dataDir: opts.dbPath } : void 0);
27
+ const dataDir = resolveDataDir(config.dataDir);
28
+ log.step("[1/4] Setting up database...");
29
+ if (!existsSync(dataDir)) {
30
+ mkdirSync(dataDir, { recursive: true });
31
+ }
32
+ const db = createDatabase({ dataDir });
33
+ migrate(db);
34
+ closeDatabase(db);
35
+ log.success(`${dataDir}/memory.db ready`);
36
+ log.step("[2/4] Configuring Claude Code...");
37
+ await configureSettings(process.cwd());
38
+ log.step("[3/4] Registering MCP server...");
39
+ const registered = registerMcpServer();
40
+ if (registered) {
41
+ log.success("MCP server registered via `claude mcp add`");
42
+ } else {
43
+ log.warn("Could not register MCP server automatically.");
44
+ log.info("Run: claude mcp add agentic-memory -- npx claude-launchpad memory serve");
45
+ }
46
+ log.step("[4/4] Injecting guidance...");
47
+ const guidanceAdded = injectClaudeMdGuidance(process.cwd());
48
+ if (guidanceAdded) {
49
+ log.success("Memory guidance added to CLAUDE.md");
50
+ }
51
+ const skillsInstalled = installSkills(process.cwd());
52
+ if (skillsInstalled > 0) {
53
+ log.success(`Installed ${skillsInstalled} skill(s) to .claude/skills/`);
54
+ }
55
+ log.blank();
56
+ log.success("Memory system installed.");
57
+ log.info("Restart your Claude Code session for the MCP server to connect.");
58
+ log.blank();
59
+ }
60
+ async function configureSettings(projectDir) {
61
+ const settings = await readSettingsJson(projectDir);
62
+ settings["autoMemoryEnabled"] = false;
63
+ log.info("Built-in auto-memory disabled");
64
+ const hooks = settings["hooks"] ?? {};
65
+ addSessionStartHook(hooks);
66
+ addStopHook(hooks);
67
+ settings["hooks"] = hooks;
68
+ addToolPermissions(settings);
69
+ await writeSettingsJson(projectDir, settings);
70
+ log.success("settings.json updated");
71
+ }
72
+ function addSessionStartHook(hooks) {
73
+ const sessionStartHooks = hooks["SessionStart"] ?? [];
74
+ const hookCommand = "npx claude-launchpad memory context --json 2>/dev/null; exit 0";
75
+ const alreadyHooked = sessionStartHooks.some((h) => {
76
+ const innerHooks = h["hooks"];
77
+ return innerHooks?.some(
78
+ (ih) => typeof ih["command"] === "string" && ih["command"].includes("claude-launchpad memory context")
79
+ );
80
+ });
81
+ if (!alreadyHooked) {
82
+ sessionStartHooks.push({
83
+ matcher: "startup|resume",
84
+ hooks: [{ type: "command", command: hookCommand }]
85
+ });
86
+ hooks["SessionStart"] = sessionStartHooks;
87
+ log.info("SessionStart hook added (injects memory context)");
88
+ }
89
+ }
90
+ function addStopHook(hooks) {
91
+ const stopHooks = hooks["Stop"] ?? [];
92
+ const extractCommand = "npx claude-launchpad memory extract 2>/dev/null; exit 0";
93
+ const alreadyHasExtract = stopHooks.some((h) => {
94
+ const innerHooks = h["hooks"];
95
+ return innerHooks?.some(
96
+ (ih) => typeof ih["command"] === "string" && ih["command"].includes("claude-launchpad memory extract")
97
+ );
98
+ });
99
+ if (!alreadyHasExtract) {
100
+ stopHooks.push({
101
+ hooks: [{ type: "command", command: extractCommand, async: true }]
102
+ });
103
+ hooks["Stop"] = stopHooks;
104
+ log.info("Stop hook added (extracts facts from transcript)");
105
+ }
106
+ }
107
+ function addToolPermissions(settings) {
108
+ const permissions = settings["permissions"] ?? {};
109
+ const allowList = permissions["allow"] ?? [];
110
+ const memoryTools = [
111
+ "mcp__agentic-memory__memory_store",
112
+ "mcp__agentic-memory__memory_search",
113
+ "mcp__agentic-memory__memory_recent",
114
+ "mcp__agentic-memory__memory_forget",
115
+ "mcp__agentic-memory__memory_relate",
116
+ "mcp__agentic-memory__memory_stats",
117
+ "mcp__agentic-memory__memory_update"
118
+ ];
119
+ let added = 0;
120
+ for (const tool of memoryTools) {
121
+ if (!allowList.includes(tool)) {
122
+ allowList.push(tool);
123
+ added++;
124
+ }
125
+ }
126
+ if (added > 0) {
127
+ permissions["allow"] = allowList;
128
+ settings["permissions"] = permissions;
129
+ log.info(`Auto-allowed ${added} MCP tools`);
130
+ }
131
+ }
132
+ function registerMcpServer() {
133
+ try {
134
+ execSync(
135
+ "claude mcp add --scope user agentic-memory -- npx claude-launchpad memory serve",
136
+ { stdio: "pipe", timeout: 1e4 }
137
+ );
138
+ return true;
139
+ } catch {
140
+ return false;
141
+ }
142
+ }
143
+ var MEMORY_GUIDANCE = `
144
+ ## Memory (agentic-memory)
145
+ This project uses **agentic-memory** for persistent memory across sessions.
146
+ - **DO NOT** use the built-in auto-memory system (~/.claude/projects/*/memory/)
147
+ - Memory context is **automatically injected** at session start via SessionStart hook - no need to call memory_recent manually
148
+ - Use \`memory_search\` to find specific memories by keyword
149
+ - Use \`memory_store\` to save decisions, gotchas, and learnings worth remembering
150
+ - Use \`memory_stats\` to check memory health
151
+ `;
152
+ function injectClaudeMdGuidance(projectDir) {
153
+ const claudeMdPath = join(projectDir, "CLAUDE.md");
154
+ let content = "";
155
+ try {
156
+ content = readFileSync(claudeMdPath, "utf-8");
157
+ } catch {
158
+ return false;
159
+ }
160
+ if (content.includes("## Memory (agentic-memory)")) {
161
+ return false;
162
+ }
163
+ const updated = content.trimEnd() + "\n" + MEMORY_GUIDANCE;
164
+ writeFileSync(claudeMdPath, updated, "utf-8");
165
+ return true;
166
+ }
167
+ var MIGRATE_MEMORY_SKILL = `---
168
+ name: lp-migrate-memory
169
+ description: Migrate legacy Claude Code auto-memory files (~/.claude/projects/*/memory/*.md) into agentic-memory. Use when setting up agentic-memory on a project that already has built-in memories.
170
+ allowed-tools: Read, Glob, Grep, mcp__agentic-memory__memory_store, mcp__agentic-memory__memory_search
171
+ ---
172
+
173
+ # Migrate Legacy Claude Code Memories
174
+
175
+ Migrate memory files from Claude Code's built-in auto-memory system into agentic-memory.
176
+
177
+ ## Steps
178
+
179
+ 1. **Find legacy memory files** for this project:
180
+ - Scan \`~/.claude/projects/*/memory/*.md\` for directories whose slug matches the current project path
181
+ - The slug format is the absolute path with \`/\` replaced by \`-\` and leading \`-\` (e.g. \`-Users-john-projects-myapp\`)
182
+ - Also check \`~/.claude/projects/*/memory/team/*.md\` for team memories
183
+
184
+ 2. **For each memory file found**, read it and parse:
185
+ - YAML frontmatter: \`name\`, \`description\`, \`type\` (user/feedback/project/reference)
186
+ - Body content (everything after the frontmatter closing \`---\`)
187
+ - Skip \`MEMORY.md\` (it's just an index file, not a memory)
188
+
189
+ 3. **Before storing**, check for duplicates:
190
+ - Call \`memory_search\` with the memory description or first 100 chars of content
191
+ - If a close match exists (same topic), skip it and report
192
+
193
+ 4. **Map types and store** each memory via \`memory_store\`:
194
+ - \`user\` -> type: \`semantic\`, tags: [\`user\`, \`migrated\`], importance: 0.7
195
+ - \`feedback\` -> type: \`semantic\`, tags: [\`feedback\`, \`migrated\`], importance: 0.8
196
+ - \`project\` -> type: \`semantic\`, tags: [\`project\`, \`migrated\`], importance: 0.6
197
+ - \`reference\` -> type: \`semantic\`, tags: [\`reference\`, \`migrated\`], importance: 0.5
198
+ - Use the frontmatter \`name\` as the title
199
+ - Use the body content as the memory content
200
+ - Set source: \`import\`
201
+ - Adjust importance up/down based on the content (decisions and gotchas deserve higher importance)
202
+
203
+ 5. **Report results**: list what was migrated, what was skipped (duplicates), and what failed
204
+
205
+ ## Important
206
+
207
+ - Do NOT delete the original files - the user can do that manually after verifying
208
+ - Do NOT migrate content that is purely derived from code (architecture, file structure) - it belongs in CLAUDE.md, not memory
209
+ - If unsure about a memory's value, migrate it anyway - the decay system will naturally prune low-value memories over time
210
+ `;
211
+ var SKILLS = {
212
+ "lp-migrate-memory": MIGRATE_MEMORY_SKILL
213
+ };
214
+ function installSkills(projectDir) {
215
+ const skillsDir = join(projectDir, ".claude", "skills");
216
+ let installed = 0;
217
+ for (const [name, content] of Object.entries(SKILLS)) {
218
+ const skillDir = join(skillsDir, name);
219
+ const skillPath = join(skillDir, "SKILL.md");
220
+ if (existsSync(skillPath)) continue;
221
+ mkdirSync(skillDir, { recursive: true });
222
+ writeFileSync(skillPath, content.trimStart(), "utf-8");
223
+ installed++;
224
+ }
225
+ return installed;
226
+ }
227
+ export {
228
+ runInstall
229
+ };
230
+ //# sourceMappingURL=install-65P6LMUN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/memory/subcommands/install.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { execSync } from 'node:child_process';\nimport { createDatabase, closeDatabase } from '../storage/database.js';\nimport { migrate } from '../storage/migrator.js';\nimport { loadConfig, resolveDataDir } from '../config.js';\nimport { readSettingsJson, writeSettingsJson } from '../../../lib/settings.js';\nimport { log } from '../../../lib/output.js';\n\ninterface InstallOpts {\n readonly dbPath?: string;\n}\n\nexport async function runInstall(opts: InstallOpts): Promise<void> {\n log.blank();\n log.step('Memory system - install');\n log.blank();\n\n const config = loadConfig(opts.dbPath ? { dataDir: opts.dbPath } : undefined);\n const dataDir = resolveDataDir(config.dataDir);\n\n // Step 1: Database\n log.step('[1/4] Setting up database...');\n if (!existsSync(dataDir)) {\n mkdirSync(dataDir, { recursive: true });\n }\n const db = createDatabase({ dataDir });\n migrate(db);\n closeDatabase(db);\n log.success(`${dataDir}/memory.db ready`);\n\n // Step 2: Configure Claude Code settings\n log.step('[2/4] Configuring Claude Code...');\n await configureSettings(process.cwd());\n\n // Step 3: Register MCP server\n log.step('[3/4] Registering MCP server...');\n const registered = registerMcpServer();\n if (registered) {\n log.success('MCP server registered via `claude mcp add`');\n } else {\n log.warn('Could not register MCP server automatically.');\n log.info('Run: claude mcp add agentic-memory -- npx claude-launchpad memory serve');\n }\n\n // Step 4: CLAUDE.md + skills\n log.step('[4/4] Injecting guidance...');\n const guidanceAdded = injectClaudeMdGuidance(process.cwd());\n if (guidanceAdded) {\n log.success('Memory guidance added to CLAUDE.md');\n }\n const skillsInstalled = installSkills(process.cwd());\n if (skillsInstalled > 0) {\n log.success(`Installed ${skillsInstalled} skill(s) to .claude/skills/`);\n }\n\n log.blank();\n log.success('Memory system installed.');\n log.info('Restart your Claude Code session for the MCP server to connect.');\n log.blank();\n}\n\nasync function configureSettings(projectDir: string): Promise<void> {\n const settings = await readSettingsJson(projectDir);\n\n // Disable built-in auto-memory\n settings['autoMemoryEnabled'] = false;\n log.info('Built-in auto-memory disabled');\n\n // SessionStart hook\n const hooks = (settings['hooks'] ?? {}) as Record<string, unknown[]>;\n addSessionStartHook(hooks);\n addStopHook(hooks);\n settings['hooks'] = hooks;\n\n // Auto-allow MCP tools\n addToolPermissions(settings);\n\n await writeSettingsJson(projectDir, settings);\n log.success('settings.json updated');\n}\n\nfunction addSessionStartHook(hooks: Record<string, unknown[]>): void {\n const sessionStartHooks = (hooks['SessionStart'] ?? []) as Record<string, unknown>[];\n const hookCommand = 'npx claude-launchpad memory context --json 2>/dev/null; exit 0';\n\n const alreadyHooked = sessionStartHooks.some((h) => {\n const innerHooks = h['hooks'] as Record<string, unknown>[] | undefined;\n return innerHooks?.some(\n ih => typeof ih['command'] === 'string' && (ih['command'] as string).includes('claude-launchpad memory context'),\n );\n });\n\n if (!alreadyHooked) {\n sessionStartHooks.push({\n matcher: 'startup|resume',\n hooks: [{ type: 'command', command: hookCommand }],\n });\n hooks['SessionStart'] = sessionStartHooks;\n log.info('SessionStart hook added (injects memory context)');\n }\n}\n\nfunction addStopHook(hooks: Record<string, unknown[]>): void {\n const stopHooks = (hooks['Stop'] ?? []) as Record<string, unknown>[];\n const extractCommand = 'npx claude-launchpad memory extract 2>/dev/null; exit 0';\n\n const alreadyHasExtract = stopHooks.some((h) => {\n const innerHooks = h['hooks'] as Record<string, unknown>[] | undefined;\n return innerHooks?.some(\n ih => typeof ih['command'] === 'string' && (ih['command'] as string).includes('claude-launchpad memory extract'),\n );\n });\n\n if (!alreadyHasExtract) {\n stopHooks.push({\n hooks: [{ type: 'command', command: extractCommand, async: true }],\n });\n hooks['Stop'] = stopHooks;\n log.info('Stop hook added (extracts facts from transcript)');\n }\n}\n\nfunction addToolPermissions(settings: Record<string, unknown>): void {\n const permissions = (settings['permissions'] ?? {}) as Record<string, unknown>;\n const allowList = (permissions['allow'] ?? []) as string[];\n\n const memoryTools = [\n 'mcp__agentic-memory__memory_store',\n 'mcp__agentic-memory__memory_search',\n 'mcp__agentic-memory__memory_recent',\n 'mcp__agentic-memory__memory_forget',\n 'mcp__agentic-memory__memory_relate',\n 'mcp__agentic-memory__memory_stats',\n 'mcp__agentic-memory__memory_update',\n ];\n\n let added = 0;\n for (const tool of memoryTools) {\n if (!allowList.includes(tool)) {\n allowList.push(tool);\n added++;\n }\n }\n\n if (added > 0) {\n permissions['allow'] = allowList;\n settings['permissions'] = permissions;\n log.info(`Auto-allowed ${added} MCP tools`);\n }\n}\n\nfunction registerMcpServer(): boolean {\n try {\n execSync(\n 'claude mcp add --scope user agentic-memory -- npx claude-launchpad memory serve',\n { stdio: 'pipe', timeout: 10000 },\n );\n return true;\n } catch {\n return false;\n }\n}\n\nconst MEMORY_GUIDANCE = `\n## Memory (agentic-memory)\nThis project uses **agentic-memory** for persistent memory across sessions.\n- **DO NOT** use the built-in auto-memory system (~/.claude/projects/*/memory/)\n- Memory context is **automatically injected** at session start via SessionStart hook - no need to call memory_recent manually\n- Use \\`memory_search\\` to find specific memories by keyword\n- Use \\`memory_store\\` to save decisions, gotchas, and learnings worth remembering\n- Use \\`memory_stats\\` to check memory health\n`;\n\nfunction injectClaudeMdGuidance(projectDir: string): boolean {\n const claudeMdPath = join(projectDir, 'CLAUDE.md');\n\n let content = '';\n try {\n content = readFileSync(claudeMdPath, 'utf-8');\n } catch {\n return false;\n }\n\n if (content.includes('## Memory (agentic-memory)')) {\n return false;\n }\n\n const updated = content.trimEnd() + '\\n' + MEMORY_GUIDANCE;\n writeFileSync(claudeMdPath, updated, 'utf-8');\n return true;\n}\n\nconst MIGRATE_MEMORY_SKILL = `---\nname: lp-migrate-memory\ndescription: Migrate legacy Claude Code auto-memory files (~/.claude/projects/*/memory/*.md) into agentic-memory. Use when setting up agentic-memory on a project that already has built-in memories.\nallowed-tools: Read, Glob, Grep, mcp__agentic-memory__memory_store, mcp__agentic-memory__memory_search\n---\n\n# Migrate Legacy Claude Code Memories\n\nMigrate memory files from Claude Code's built-in auto-memory system into agentic-memory.\n\n## Steps\n\n1. **Find legacy memory files** for this project:\n - Scan \\`~/.claude/projects/*/memory/*.md\\` for directories whose slug matches the current project path\n - The slug format is the absolute path with \\`/\\` replaced by \\`-\\` and leading \\`-\\` (e.g. \\`-Users-john-projects-myapp\\`)\n - Also check \\`~/.claude/projects/*/memory/team/*.md\\` for team memories\n\n2. **For each memory file found**, read it and parse:\n - YAML frontmatter: \\`name\\`, \\`description\\`, \\`type\\` (user/feedback/project/reference)\n - Body content (everything after the frontmatter closing \\`---\\`)\n - Skip \\`MEMORY.md\\` (it's just an index file, not a memory)\n\n3. **Before storing**, check for duplicates:\n - Call \\`memory_search\\` with the memory description or first 100 chars of content\n - If a close match exists (same topic), skip it and report\n\n4. **Map types and store** each memory via \\`memory_store\\`:\n - \\`user\\` -> type: \\`semantic\\`, tags: [\\`user\\`, \\`migrated\\`], importance: 0.7\n - \\`feedback\\` -> type: \\`semantic\\`, tags: [\\`feedback\\`, \\`migrated\\`], importance: 0.8\n - \\`project\\` -> type: \\`semantic\\`, tags: [\\`project\\`, \\`migrated\\`], importance: 0.6\n - \\`reference\\` -> type: \\`semantic\\`, tags: [\\`reference\\`, \\`migrated\\`], importance: 0.5\n - Use the frontmatter \\`name\\` as the title\n - Use the body content as the memory content\n - Set source: \\`import\\`\n - Adjust importance up/down based on the content (decisions and gotchas deserve higher importance)\n\n5. **Report results**: list what was migrated, what was skipped (duplicates), and what failed\n\n## Important\n\n- Do NOT delete the original files - the user can do that manually after verifying\n- Do NOT migrate content that is purely derived from code (architecture, file structure) - it belongs in CLAUDE.md, not memory\n- If unsure about a memory's value, migrate it anyway - the decay system will naturally prune low-value memories over time\n`;\n\nconst SKILLS: Readonly<Record<string, string>> = {\n 'lp-migrate-memory': MIGRATE_MEMORY_SKILL,\n};\n\nfunction installSkills(projectDir: string): number {\n const skillsDir = join(projectDir, '.claude', 'skills');\n let installed = 0;\n\n for (const [name, content] of Object.entries(SKILLS)) {\n const skillDir = join(skillsDir, name);\n const skillPath = join(skillDir, 'SKILL.md');\n\n if (existsSync(skillPath)) continue;\n\n mkdirSync(skillDir, { recursive: true });\n writeFileSync(skillPath, content.trimStart(), 'utf-8');\n installed++;\n }\n\n return installed;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ log
4
+ } from "./chunk-6ZVXZ4EF.js";
5
+ import {
6
+ initStorage
7
+ } from "./chunk-EBM7RBPB.js";
8
+ import "./chunk-TALTTAMW.js";
9
+ import "./chunk-IILH26C7.js";
10
+ import "./chunk-2H7UOFLK.js";
11
+
12
+ // src/commands/memory/subcommands/stats.ts
13
+ import { existsSync, statSync } from "fs";
14
+ import { join } from "path";
15
+ function formatBytes(bytes) {
16
+ if (bytes < 1024) return `${bytes}B`;
17
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
18
+ return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
19
+ }
20
+ async function runStats(opts) {
21
+ const ctx = initStorage(opts.dbPath);
22
+ try {
23
+ const total = ctx.memoryRepo.count();
24
+ const byType = ctx.memoryRepo.countByType();
25
+ const relations = ctx.relationRepo.count();
26
+ const dateRange = ctx.memoryRepo.dateRange();
27
+ const topInjected = ctx.memoryRepo.topInjected(5);
28
+ let dbSize = 0;
29
+ try {
30
+ const dbPath = join(ctx.dataDir, "memory.db");
31
+ if (existsSync(dbPath)) {
32
+ dbSize = statSync(dbPath).size;
33
+ }
34
+ } catch {
35
+ }
36
+ if (opts.json) {
37
+ process.stdout.write(JSON.stringify({
38
+ totalMemories: total,
39
+ byType,
40
+ totalRelations: relations,
41
+ dbSizeBytes: dbSize,
42
+ oldestMemory: dateRange.oldest,
43
+ newestMemory: dateRange.newest,
44
+ topInjected
45
+ }, null, 2) + "\n");
46
+ } else {
47
+ log.blank();
48
+ log.step("Memory stats");
49
+ log.info(`Memories: ${total}`);
50
+ for (const [type, count] of Object.entries(byType)) {
51
+ log.info(` ${type}: ${count}`);
52
+ }
53
+ log.info(`Relations: ${relations}`);
54
+ log.info(`DB size: ${formatBytes(dbSize)}`);
55
+ log.info(`Oldest: ${dateRange.oldest ?? "none"}`);
56
+ log.info(`Newest: ${dateRange.newest ?? "none"}`);
57
+ if (topInjected.length > 0) {
58
+ log.blank();
59
+ log.info("Top injected:");
60
+ for (const m of topInjected) {
61
+ log.info(` ${m.title ?? m.id} (${m.injectionCount}x)`);
62
+ }
63
+ }
64
+ log.blank();
65
+ }
66
+ } finally {
67
+ ctx.close();
68
+ }
69
+ }
70
+ export {
71
+ runStats
72
+ };
73
+ //# sourceMappingURL=stats-FYAK7KZW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/memory/subcommands/stats.ts"],"sourcesContent":["import { existsSync, statSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { log } from '../../../lib/output.js';\nimport { initStorage } from './init-storage.js';\n\ninterface StatsOpts {\n readonly json?: boolean;\n readonly dbPath?: string;\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes}B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n}\n\nexport async function runStats(opts: StatsOpts): Promise<void> {\n const ctx = initStorage(opts.dbPath);\n\n try {\n const total = ctx.memoryRepo.count();\n const byType = ctx.memoryRepo.countByType();\n const relations = ctx.relationRepo.count();\n const dateRange = ctx.memoryRepo.dateRange();\n const topInjected = ctx.memoryRepo.topInjected(5);\n\n let dbSize = 0;\n try {\n const dbPath = join(ctx.dataDir, 'memory.db');\n if (existsSync(dbPath)) {\n dbSize = statSync(dbPath).size;\n }\n } catch { /* ignore */ }\n\n if (opts.json) {\n process.stdout.write(JSON.stringify({\n totalMemories: total,\n byType,\n totalRelations: relations,\n dbSizeBytes: dbSize,\n oldestMemory: dateRange.oldest,\n newestMemory: dateRange.newest,\n topInjected,\n }, null, 2) + '\\n');\n } else {\n log.blank();\n log.step('Memory stats');\n log.info(`Memories: ${total}`);\n for (const [type, count] of Object.entries(byType)) {\n log.info(` ${type}: ${count}`);\n }\n log.info(`Relations: ${relations}`);\n log.info(`DB size: ${formatBytes(dbSize)}`);\n log.info(`Oldest: ${dateRange.oldest ?? 'none'}`);\n log.info(`Newest: ${dateRange.newest ?? 'none'}`);\n if (topInjected.length > 0) {\n log.blank();\n log.info('Top injected:');\n for (const m of topInjected) {\n log.info(` ${m.title ?? m.id} (${m.injectionCount}x)`);\n }\n }\n log.blank();\n }\n } finally {\n ctx.close();\n }\n}\n"],"mappings":";;;;;;;;;;;;AAAA,SAAS,YAAY,gBAAgB;AACrC,SAAS,YAAY;AASrB,SAAS,YAAY,OAAuB;AAC1C,MAAI,QAAQ,KAAM,QAAO,GAAG,KAAK;AACjC,MAAI,QAAQ,OAAO,KAAM,QAAO,IAAI,QAAQ,MAAM,QAAQ,CAAC,CAAC;AAC5D,SAAO,IAAI,SAAS,OAAO,OAAO,QAAQ,CAAC,CAAC;AAC9C;AAEA,eAAsB,SAAS,MAAgC;AAC7D,QAAM,MAAM,YAAY,KAAK,MAAM;AAEnC,MAAI;AACF,UAAM,QAAQ,IAAI,WAAW,MAAM;AACnC,UAAM,SAAS,IAAI,WAAW,YAAY;AAC1C,UAAM,YAAY,IAAI,aAAa,MAAM;AACzC,UAAM,YAAY,IAAI,WAAW,UAAU;AAC3C,UAAM,cAAc,IAAI,WAAW,YAAY,CAAC;AAEhD,QAAI,SAAS;AACb,QAAI;AACF,YAAM,SAAS,KAAK,IAAI,SAAS,WAAW;AAC5C,UAAI,WAAW,MAAM,GAAG;AACtB,iBAAS,SAAS,MAAM,EAAE;AAAA,MAC5B;AAAA,IACF,QAAQ;AAAA,IAAe;AAEvB,QAAI,KAAK,MAAM;AACb,cAAQ,OAAO,MAAM,KAAK,UAAU;AAAA,QAClC,eAAe;AAAA,QACf;AAAA,QACA,gBAAgB;AAAA,QAChB,aAAa;AAAA,QACb,cAAc,UAAU;AAAA,QACxB,cAAc,UAAU;AAAA,QACxB;AAAA,MACF,GAAG,MAAM,CAAC,IAAI,IAAI;AAAA,IACpB,OAAO;AACL,UAAI,MAAM;AACV,UAAI,KAAK,cAAc;AACvB,UAAI,KAAK,eAAe,KAAK,EAAE;AAC/B,iBAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AAClD,YAAI,KAAK,KAAK,IAAI,KAAK,KAAK,EAAE;AAAA,MAChC;AACA,UAAI,KAAK,eAAe,SAAS,EAAE;AACnC,UAAI,KAAK,eAAe,YAAY,MAAM,CAAC,EAAE;AAC7C,UAAI,KAAK,eAAe,UAAU,UAAU,MAAM,EAAE;AACpD,UAAI,KAAK,eAAe,UAAU,UAAU,MAAM,EAAE;AACpD,UAAI,YAAY,SAAS,GAAG;AAC1B,YAAI,MAAM;AACV,YAAI,KAAK,eAAe;AACxB,mBAAW,KAAK,aAAa;AAC3B,cAAI,KAAK,KAAK,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE,cAAc,IAAI;AAAA,QACxD;AAAA,MACF;AACA,UAAI,MAAM;AAAA,IACZ;AAAA,EACF,UAAE;AACA,QAAI,MAAM;AAAA,EACZ;AACF;","names":[]}