claude-launchpad 0.14.0 → 0.14.2

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.
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ computeContextScore,
4
+ getGitContext
5
+ } from "../../chunk-MQJA7TGY.js";
2
6
  import {
3
7
  MEMORY_SOURCES,
4
8
  MEMORY_TYPES,
5
9
  RELATION_TYPES
6
10
  } from "../../chunk-GRL3DOCP.js";
7
- import {
8
- computeContextScore,
9
- getGitContext
10
- } from "../../chunk-MQJA7TGY.js";
11
11
  import {
12
12
  detectProject
13
13
  } from "../../chunk-NAW47BYA.js";
@@ -64,7 +64,6 @@ async function configureSettings(projectDir) {
64
64
  log.info("Built-in auto-memory disabled (replaced by knowledge base)");
65
65
  const hooks = settings["hooks"] ?? {};
66
66
  addSessionStartHook(hooks);
67
- addStopHook(hooks);
68
67
  settings["hooks"] = hooks;
69
68
  addToolPermissions(settings);
70
69
  await writeSettingsJson(projectDir, settings);
@@ -88,23 +87,6 @@ function addSessionStartHook(hooks) {
88
87
  log.info("Session start: Claude will recall relevant context automatically");
89
88
  }
90
89
  }
91
- function addStopHook(hooks) {
92
- const stopHooks = hooks["Stop"] ?? [];
93
- const extractCommand = "npx claude-launchpad memory extract 2>/dev/null; exit 0";
94
- const alreadyHasExtract = stopHooks.some((h) => {
95
- const innerHooks = h["hooks"];
96
- return innerHooks?.some(
97
- (ih) => typeof ih["command"] === "string" && ih["command"].includes("claude-launchpad memory extract")
98
- );
99
- });
100
- if (!alreadyHasExtract) {
101
- stopHooks.push({
102
- hooks: [{ type: "command", command: extractCommand, async: true }]
103
- });
104
- hooks["Stop"] = stopHooks;
105
- log.info("Session end: Claude will save important facts from the conversation");
106
- }
107
- }
108
90
  function addToolPermissions(settings) {
109
91
  const permissions = settings["permissions"] ?? {};
110
92
  const allowList = permissions["allow"] ?? [];
@@ -175,7 +157,7 @@ This project uses **agentic-memory** for persistent memory across sessions.
175
157
  - Use \`memory_search\` to find specific memories by keyword
176
158
  - Use \`memory_store\` to save decisions, gotchas, and learnings worth remembering
177
159
  - Use \`memory_stats\` to check memory health
178
- - **STORE IMMEDIATELY** when: a dependency strategy changes, an architecture decision is made, a convention is established, a bug pattern is discovered, or a feature is killed/added. Don't wait for session end - the Stop hook only catches obvious patterns.
160
+ - **STORE IMMEDIATELY** when: a dependency strategy changes, an architecture decision is made, a convention is established, a bug pattern is discovered, or a feature is killed/added
179
161
  `;
180
162
  function injectClaudeMdGuidance(projectDir) {
181
163
  const claudeMdPath = join(projectDir, "CLAUDE.md");
@@ -255,4 +237,4 @@ function installSkills(projectDir) {
255
237
  export {
256
238
  runInstall
257
239
  };
258
- //# sourceMappingURL=install-H6GW4P6K.js.map
240
+ //# sourceMappingURL=install-4GQ57KCQ.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('Setting up your knowledge base');\n log.blank();\n\n // Step 0: Ensure native deps are installed globally\n await ensureNativeDeps();\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] Creating knowledge base...');\n if (!existsSync(dataDir)) {\n mkdirSync(dataDir, { recursive: true });\n }\n const db = createDatabase({ dataDir });\n migrate(db);\n closeDatabase(db);\n log.success(`Knowledge base created at ${dataDir}/memory.db`);\n\n // Step 2: Configure Claude Code settings\n log.step('[2/4] Connecting to Claude Code...');\n await configureSettings(process.cwd());\n\n // Step 3: Register MCP server\n log.step('[3/4] Enabling memory tools...');\n const registered = registerMcpServer();\n if (registered) {\n log.success('Memory tools available in Claude Code');\n } else {\n log.warn('Could not enable memory tools automatically.');\n log.info('Run: claude mcp add --scope user agentic-memory npx claude-launchpad memory serve');\n }\n\n // Step 4: CLAUDE.md + skills\n log.step('[4/4] Adding instructions...');\n const guidanceAdded = injectClaudeMdGuidance(process.cwd());\n if (guidanceAdded) {\n log.success('CLAUDE.md updated with memory instructions');\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('Knowledge base is ready. Claude will now remember across sessions.');\n log.info('Restart your Claude Code session to activate.');\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 (replaced by knowledge base)');\n\n // SessionStart hook\n const hooks = (settings['hooks'] ?? {}) as Record<string, unknown[]>;\n addSessionStartHook(hooks);\n settings['hooks'] = hooks;\n\n // Auto-allow MCP tools\n addToolPermissions(settings);\n\n await writeSettingsJson(projectDir, settings);\n log.success('Claude Code configured for knowledge base');\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('Session start: Claude will recall relevant context automatically');\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(`${added} memory tools auto-approved`);\n }\n}\n\nasync function ensureNativeDeps(): Promise<void> {\n const { cwdRequire } = await import('../utils/require-deps.js');\n try {\n cwdRequire('better-sqlite3');\n return;\n } catch {\n // Not installed — install globally\n }\n\n log.step('Installing required database libraries...');\n try {\n execSync('npm install -g better-sqlite3 sqlite-vec', { stdio: 'pipe', timeout: 120000 });\n log.success('Database libraries installed');\n } catch {\n log.error('Could not install database libraries automatically.');\n log.blank();\n log.info('Install manually:');\n log.step(' npm install -g better-sqlite3 sqlite-vec');\n log.blank();\n log.info('Requires a C++ compiler (Xcode on macOS, build-essential on Linux).');\n process.exit(1);\n }\n}\n\nfunction registerMcpServer(): boolean {\n try {\n const existing = execSync('claude mcp list', { stdio: 'pipe', timeout: 10000, encoding: 'utf-8' });\n if (existing.includes('agentic-memory')) {\n log.info('Memory tools already registered');\n return true;\n }\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- **STORE IMMEDIATELY** when: a dependency strategy changes, an architecture decision is made, a convention is established, a bug pattern is discovered, or a feature is killed/added\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":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-launchpad",
3
- "version": "0.14.0",
3
+ "version": "0.14.2",
4
4
  "description": "CLI toolkit for Claude Code — scaffold CLAUDE.md, diagnose config, enforce hooks, test with eval, add persistent memory",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,218 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- detectProject
4
- } from "./chunk-NAW47BYA.js";
5
- import {
6
- initStorage
7
- } from "./chunk-QMWQOL75.js";
8
- import "./chunk-4D3EBDNB.js";
9
- import "./chunk-JWT7EHTU.js";
10
- import "./chunk-Z6FBT44W.js";
11
- import "./chunk-RJGXPH7P.js";
12
-
13
- // src/commands/memory/subcommands/extract.ts
14
- import { existsSync, readFileSync } from "fs";
15
-
16
- // src/commands/memory/services/session-service.ts
17
- function parseTranscript(jsonlContent, maxMessages = 50) {
18
- const lines = jsonlContent.split("\n").filter((l) => l.trim().length > 0);
19
- const textParts = [];
20
- const startIdx = Math.max(0, lines.length - maxMessages);
21
- for (let i = startIdx; i < lines.length; i++) {
22
- try {
23
- const msg = JSON.parse(lines[i]);
24
- if (msg.type !== "user" && msg.type !== "assistant") continue;
25
- const content = msg.message?.content;
26
- if (typeof content === "string") {
27
- textParts.push(content);
28
- } else if (Array.isArray(content)) {
29
- for (const block of content) {
30
- if (typeof block === "object" && block !== null && "type" in block && block.type === "text" && "text" in block) {
31
- textParts.push(block.text);
32
- }
33
- }
34
- }
35
- } catch {
36
- }
37
- }
38
- return textParts.join("\n");
39
- }
40
- var MIN_FACT_LENGTH = 30;
41
- function isNoiseLine(line) {
42
- if (/<\/?[a-z-]+>/i.test(line)) return true;
43
- if (/^\|.*\|$/.test(line)) return true;
44
- if (/^```/.test(line)) return true;
45
- const alphaRatio = (line.match(/[a-zA-Z]/g)?.length ?? 0) / line.length;
46
- if (alphaRatio < 0.4 && line.length > 20) return true;
47
- return false;
48
- }
49
- function extractFacts(transcript) {
50
- const facts = [];
51
- const lines = transcript.split("\n");
52
- for (const line of lines) {
53
- const trimmed = line.trim();
54
- if (!trimmed || trimmed.length < 10) continue;
55
- if (isNoiseLine(trimmed)) continue;
56
- const decisionMatch = trimmed.match(/decided?\s+(?:to\s+)?(.+?)\s+because\s+(.+)/i);
57
- if (decisionMatch?.[1] && decisionMatch[2]) {
58
- facts.push({
59
- type: "semantic",
60
- content: `Decision: ${decisionMatch[1].trim()}. Reason: ${decisionMatch[2].trim()}`,
61
- tags: ["decision"],
62
- importance: 0.7
63
- });
64
- continue;
65
- }
66
- const fixMatch = trimmed.match(/(?:fixed|resolved|solved)\s+(.+?)\s+by\s+(.+)/i);
67
- if (fixMatch?.[1] && fixMatch[2]) {
68
- facts.push({
69
- type: "episodic",
70
- content: `Fixed: ${fixMatch[1].trim()}. Solution: ${fixMatch[2].trim()}`,
71
- tags: ["bugfix"],
72
- importance: 0.6
73
- });
74
- continue;
75
- }
76
- const learnMatch = trimmed.match(/(?:learned|discovered|found out|realized)\s+(?:that\s+)?(.+)/i);
77
- if (learnMatch?.[1]) {
78
- facts.push({
79
- type: "semantic",
80
- content: learnMatch[1].trim(),
81
- tags: ["learning"],
82
- importance: 0.6
83
- });
84
- continue;
85
- }
86
- const proceduralMatch = trimmed.match(/(?:to\s+(.+?),\s+(?:use|run|execute|call)\s+(.+))/i);
87
- if (proceduralMatch?.[1] && proceduralMatch[2]) {
88
- facts.push({
89
- type: "procedural",
90
- content: `To ${proceduralMatch[1].trim()}: ${proceduralMatch[2].trim()}`,
91
- tags: ["howto"],
92
- importance: 0.6
93
- });
94
- continue;
95
- }
96
- const gotchaMatch = trimmed.match(/(?:gotcha|watch out|pitfall)[:!]\s*(.+)/i);
97
- if (gotchaMatch?.[1] && gotchaMatch[1].trim().length >= MIN_FACT_LENGTH) {
98
- facts.push({
99
- type: "semantic",
100
- content: `Gotcha: ${gotchaMatch[1].trim()}`,
101
- tags: ["gotcha"],
102
- importance: 0.7
103
- });
104
- continue;
105
- }
106
- }
107
- return facts;
108
- }
109
-
110
- // src/commands/memory/subcommands/extract.ts
111
- async function runExtract() {
112
- let stdinData = "";
113
- try {
114
- stdinData = await readStdin(5e3);
115
- } catch {
116
- process.stderr.write("[agentic-memory] extract: no stdin data received\n");
117
- return;
118
- }
119
- if (!stdinData.trim()) {
120
- process.stderr.write("[agentic-memory] extract: empty stdin\n");
121
- return;
122
- }
123
- let hookInput;
124
- try {
125
- hookInput = JSON.parse(stdinData);
126
- } catch {
127
- process.stderr.write("[agentic-memory] extract: invalid JSON on stdin\n");
128
- return;
129
- }
130
- const transcriptPath = hookInput.transcript_path;
131
- if (!transcriptPath || !existsSync(transcriptPath)) {
132
- process.stderr.write(`[agentic-memory] extract: transcript not found: ${transcriptPath}
133
- `);
134
- return;
135
- }
136
- let transcriptContent;
137
- try {
138
- transcriptContent = readFileSync(transcriptPath, "utf-8");
139
- } catch (err) {
140
- process.stderr.write(`[agentic-memory] extract: failed to read transcript: ${err instanceof Error ? err.message : err}
141
- `);
142
- return;
143
- }
144
- const text = parseTranscript(transcriptContent);
145
- if (!text || text.length < 50) return;
146
- const facts = extractFacts(text);
147
- if (facts.length === 0) return;
148
- const ctx = initStorage();
149
- const project = detectProject(hookInput.cwd ?? process.cwd());
150
- try {
151
- let stored = 0;
152
- for (const fact of facts) {
153
- try {
154
- const existing = ctx.searchRepo.searchFts({
155
- query: fact.content.slice(0, 100),
156
- limit: 1
157
- });
158
- if (existing.length > 0 && Math.abs(existing[0].rank) > 15) {
159
- continue;
160
- }
161
- } catch {
162
- }
163
- ctx.memoryRepo.create(
164
- {
165
- type: fact.type,
166
- content: fact.content,
167
- tags: [...fact.tags],
168
- importance: fact.importance,
169
- source: "hook",
170
- project: project ?? void 0
171
- },
172
- null
173
- );
174
- stored++;
175
- }
176
- if (stored > 0) {
177
- process.stderr.write(`[agentic-memory] extract: stored ${stored} facts from transcript
178
- `);
179
- }
180
- } finally {
181
- ctx.close();
182
- }
183
- }
184
- function readStdin(timeoutMs) {
185
- if (process.stdin.isTTY) return Promise.resolve("");
186
- return new Promise((resolve, reject) => {
187
- let settled = false;
188
- const chunks = [];
189
- const settle = (fn) => {
190
- if (settled) return;
191
- settled = true;
192
- clearTimeout(timer);
193
- process.stdin.removeListener("data", onData);
194
- process.stdin.removeListener("end", onEnd);
195
- process.stdin.removeListener("error", onError);
196
- fn();
197
- };
198
- const timer = setTimeout(() => {
199
- settle(() => resolve(chunks.length > 0 ? Buffer.concat(chunks).toString("utf-8") : ""));
200
- }, timeoutMs);
201
- const onData = (chunk) => {
202
- chunks.push(chunk);
203
- };
204
- const onEnd = () => {
205
- settle(() => resolve(Buffer.concat(chunks).toString("utf-8")));
206
- };
207
- const onError = (err) => {
208
- settle(() => reject(err));
209
- };
210
- process.stdin.on("data", onData);
211
- process.stdin.on("end", onEnd);
212
- process.stdin.on("error", onError);
213
- });
214
- }
215
- export {
216
- runExtract
217
- };
218
- //# sourceMappingURL=extract-HMAN7RW4.js.map
@@ -1 +0,0 @@
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":[]}
@@ -1 +0,0 @@
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('Setting up your knowledge base');\n log.blank();\n\n // Step 0: Ensure native deps are installed globally\n await ensureNativeDeps();\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] Creating knowledge base...');\n if (!existsSync(dataDir)) {\n mkdirSync(dataDir, { recursive: true });\n }\n const db = createDatabase({ dataDir });\n migrate(db);\n closeDatabase(db);\n log.success(`Knowledge base created at ${dataDir}/memory.db`);\n\n // Step 2: Configure Claude Code settings\n log.step('[2/4] Connecting to Claude Code...');\n await configureSettings(process.cwd());\n\n // Step 3: Register MCP server\n log.step('[3/4] Enabling memory tools...');\n const registered = registerMcpServer();\n if (registered) {\n log.success('Memory tools available in Claude Code');\n } else {\n log.warn('Could not enable memory tools automatically.');\n log.info('Run: claude mcp add --scope user agentic-memory npx claude-launchpad memory serve');\n }\n\n // Step 4: CLAUDE.md + skills\n log.step('[4/4] Adding instructions...');\n const guidanceAdded = injectClaudeMdGuidance(process.cwd());\n if (guidanceAdded) {\n log.success('CLAUDE.md updated with memory instructions');\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('Knowledge base is ready. Claude will now remember across sessions.');\n log.info('Restart your Claude Code session to activate.');\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 (replaced by knowledge base)');\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('Claude Code configured for knowledge base');\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('Session start: Claude will recall relevant context automatically');\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('Session end: Claude will save important facts from the conversation');\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(`${added} memory tools auto-approved`);\n }\n}\n\nasync function ensureNativeDeps(): Promise<void> {\n const { cwdRequire } = await import('../utils/require-deps.js');\n try {\n cwdRequire('better-sqlite3');\n return;\n } catch {\n // Not installed — install globally\n }\n\n log.step('Installing required database libraries...');\n try {\n execSync('npm install -g better-sqlite3 sqlite-vec', { stdio: 'pipe', timeout: 120000 });\n log.success('Database libraries installed');\n } catch {\n log.error('Could not install database libraries automatically.');\n log.blank();\n log.info('Install manually:');\n log.step(' npm install -g better-sqlite3 sqlite-vec');\n log.blank();\n log.info('Requires a C++ compiler (Xcode on macOS, build-essential on Linux).');\n process.exit(1);\n }\n}\n\nfunction registerMcpServer(): boolean {\n try {\n const existing = execSync('claude mcp list', { stdio: 'pipe', timeout: 10000, encoding: 'utf-8' });\n if (existing.includes('agentic-memory')) {\n log.info('Memory tools already registered');\n return true;\n }\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- **STORE IMMEDIATELY** when: a dependency strategy changes, an architecture decision is made, a convention is established, a bug pattern is discovered, or a feature is killed/added. Don't wait for session end - the Stop hook only catches obvious patterns.\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":";;;;;;;;;;;;;;;;;;AAAA,SAAS,YAAY,WAAW,cAAc,qBAAqB;AACnE,SAAS,YAAY;AACrB,SAAS,gBAAgB;AAWzB,eAAsB,WAAW,MAAkC;AACjE,MAAI,MAAM;AACV,MAAI,KAAK,gCAAgC;AACzC,MAAI,MAAM;AAGV,QAAM,iBAAiB;AAEvB,QAAM,SAAS,WAAW,KAAK,SAAS,EAAE,SAAS,KAAK,OAAO,IAAI,MAAS;AAC5E,QAAM,UAAU,eAAe,OAAO,OAAO;AAG7C,MAAI,KAAK,kCAAkC;AAC3C,MAAI,CAAC,WAAW,OAAO,GAAG;AACxB,cAAU,SAAS,EAAE,WAAW,KAAK,CAAC;AAAA,EACxC;AACA,QAAM,KAAK,eAAe,EAAE,QAAQ,CAAC;AACrC,UAAQ,EAAE;AACV,gBAAc,EAAE;AAChB,MAAI,QAAQ,6BAA6B,OAAO,YAAY;AAG5D,MAAI,KAAK,oCAAoC;AAC7C,QAAM,kBAAkB,QAAQ,IAAI,CAAC;AAGrC,MAAI,KAAK,gCAAgC;AACzC,QAAM,aAAa,kBAAkB;AACrC,MAAI,YAAY;AACd,QAAI,QAAQ,uCAAuC;AAAA,EACrD,OAAO;AACL,QAAI,KAAK,8CAA8C;AACvD,QAAI,KAAK,mFAAmF;AAAA,EAC9F;AAGA,MAAI,KAAK,8BAA8B;AACvC,QAAM,gBAAgB,uBAAuB,QAAQ,IAAI,CAAC;AAC1D,MAAI,eAAe;AACjB,QAAI,QAAQ,4CAA4C;AAAA,EAC1D;AACA,QAAM,kBAAkB,cAAc,QAAQ,IAAI,CAAC;AACnD,MAAI,kBAAkB,GAAG;AACvB,QAAI,QAAQ,aAAa,eAAe,8BAA8B;AAAA,EACxE;AAEA,MAAI,MAAM;AACV,MAAI,QAAQ,oEAAoE;AAChF,MAAI,KAAK,+CAA+C;AACxD,MAAI,MAAM;AACZ;AAEA,eAAe,kBAAkB,YAAmC;AAClE,QAAM,WAAW,MAAM,iBAAiB,UAAU;AAGlD,WAAS,mBAAmB,IAAI;AAChC,MAAI,KAAK,4DAA4D;AAGrE,QAAM,QAAS,SAAS,OAAO,KAAK,CAAC;AACrC,sBAAoB,KAAK;AACzB,cAAY,KAAK;AACjB,WAAS,OAAO,IAAI;AAGpB,qBAAmB,QAAQ;AAE3B,QAAM,kBAAkB,YAAY,QAAQ;AAC5C,MAAI,QAAQ,2CAA2C;AACzD;AAEA,SAAS,oBAAoB,OAAwC;AACnE,QAAM,oBAAqB,MAAM,cAAc,KAAK,CAAC;AACrD,QAAM,cAAc;AAEpB,QAAM,gBAAgB,kBAAkB,KAAK,CAAC,MAAM;AAClD,UAAM,aAAa,EAAE,OAAO;AAC5B,WAAO,YAAY;AAAA,MACjB,QAAM,OAAO,GAAG,SAAS,MAAM,YAAa,GAAG,SAAS,EAAa,SAAS,iCAAiC;AAAA,IACjH;AAAA,EACF,CAAC;AAED,MAAI,CAAC,eAAe;AAClB,sBAAkB,KAAK;AAAA,MACrB,SAAS;AAAA,MACT,OAAO,CAAC,EAAE,MAAM,WAAW,SAAS,YAAY,CAAC;AAAA,IACnD,CAAC;AACD,UAAM,cAAc,IAAI;AACxB,QAAI,KAAK,kEAAkE;AAAA,EAC7E;AACF;AAEA,SAAS,YAAY,OAAwC;AAC3D,QAAM,YAAa,MAAM,MAAM,KAAK,CAAC;AACrC,QAAM,iBAAiB;AAEvB,QAAM,oBAAoB,UAAU,KAAK,CAAC,MAAM;AAC9C,UAAM,aAAa,EAAE,OAAO;AAC5B,WAAO,YAAY;AAAA,MACjB,QAAM,OAAO,GAAG,SAAS,MAAM,YAAa,GAAG,SAAS,EAAa,SAAS,iCAAiC;AAAA,IACjH;AAAA,EACF,CAAC;AAED,MAAI,CAAC,mBAAmB;AACtB,cAAU,KAAK;AAAA,MACb,OAAO,CAAC,EAAE,MAAM,WAAW,SAAS,gBAAgB,OAAO,KAAK,CAAC;AAAA,IACnE,CAAC;AACD,UAAM,MAAM,IAAI;AAChB,QAAI,KAAK,qEAAqE;AAAA,EAChF;AACF;AAEA,SAAS,mBAAmB,UAAyC;AACnE,QAAM,cAAe,SAAS,aAAa,KAAK,CAAC;AACjD,QAAM,YAAa,YAAY,OAAO,KAAK,CAAC;AAE5C,QAAM,cAAc;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,MAAI,QAAQ;AACZ,aAAW,QAAQ,aAAa;AAC9B,QAAI,CAAC,UAAU,SAAS,IAAI,GAAG;AAC7B,gBAAU,KAAK,IAAI;AACnB;AAAA,IACF;AAAA,EACF;AAEA,MAAI,QAAQ,GAAG;AACb,gBAAY,OAAO,IAAI;AACvB,aAAS,aAAa,IAAI;AAC1B,QAAI,KAAK,GAAG,KAAK,6BAA6B;AAAA,EAChD;AACF;AAEA,eAAe,mBAAkC;AAC/C,QAAM,EAAE,WAAW,IAAI,MAAM,OAAO,4BAA0B;AAC9D,MAAI;AACF,eAAW,gBAAgB;AAC3B;AAAA,EACF,QAAQ;AAAA,EAER;AAEA,MAAI,KAAK,2CAA2C;AACpD,MAAI;AACF,aAAS,4CAA4C,EAAE,OAAO,QAAQ,SAAS,KAAO,CAAC;AACvF,QAAI,QAAQ,8BAA8B;AAAA,EAC5C,QAAQ;AACN,QAAI,MAAM,qDAAqD;AAC/D,QAAI,MAAM;AACV,QAAI,KAAK,mBAAmB;AAC5B,QAAI,KAAK,4CAA4C;AACrD,QAAI,MAAM;AACV,QAAI,KAAK,qEAAqE;AAC9E,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEA,SAAS,oBAA6B;AACpC,MAAI;AACF,UAAM,WAAW,SAAS,mBAAmB,EAAE,OAAO,QAAQ,SAAS,KAAO,UAAU,QAAQ,CAAC;AACjG,QAAI,SAAS,SAAS,gBAAgB,GAAG;AACvC,UAAI,KAAK,iCAAiC;AAC1C,aAAO;AAAA,IACT;AACA;AAAA,MACE;AAAA,MACA,EAAE,OAAO,QAAQ,SAAS,IAAM;AAAA,IAClC;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,IAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWxB,SAAS,uBAAuB,YAA6B;AAC3D,QAAM,eAAe,KAAK,YAAY,WAAW;AAEjD,MAAI,UAAU;AACd,MAAI;AACF,cAAU,aAAa,cAAc,OAAO;AAAA,EAC9C,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ,SAAS,4BAA4B,GAAG;AAClD,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,QAAQ,QAAQ,IAAI,OAAO;AAC3C,gBAAc,cAAc,SAAS,OAAO;AAC5C,SAAO;AACT;AAEA,IAAM,uBAAuB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA6C7B,IAAM,SAA2C;AAAA,EAC/C,qBAAqB;AACvB;AAEA,SAAS,cAAc,YAA4B;AACjD,QAAM,YAAY,KAAK,YAAY,WAAW,QAAQ;AACtD,MAAI,YAAY;AAEhB,aAAW,CAAC,MAAM,OAAO,KAAK,OAAO,QAAQ,MAAM,GAAG;AACpD,UAAM,WAAW,KAAK,WAAW,IAAI;AACrC,UAAM,YAAY,KAAK,UAAU,UAAU;AAE3C,QAAI,WAAW,SAAS,EAAG;AAE3B,cAAU,UAAU,EAAE,WAAW,KAAK,CAAC;AACvC,kBAAc,WAAW,QAAQ,UAAU,GAAG,OAAO;AACrD;AAAA,EACF;AAEA,SAAO;AACT;","names":[]}