cckb 0.1.7 → 0.1.9

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.
@@ -71,9 +71,12 @@ var CompactionEngine = class {
71
71
  async generateSummary(conversation) {
72
72
  const prompt = SUMMARIZATION_PROMPT + conversation;
73
73
  try {
74
+ console.error("[CCKB] Calling Claude for summarization...");
74
75
  const result = await spawnClaudeAgent(prompt);
76
+ console.error("[CCKB] Claude summarization complete");
75
77
  return result;
76
78
  } catch (error) {
79
+ console.error("[CCKB] Claude failed, using fallback:", error);
77
80
  return this.fallbackExtraction(conversation);
78
81
  }
79
82
  }
@@ -273,4 +276,4 @@ ${content}`);
273
276
  export {
274
277
  CompactionEngine
275
278
  };
276
- //# sourceMappingURL=chunk-KZ2I74VT.js.map
279
+ //# sourceMappingURL=chunk-OZQVYSKC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/compaction-engine.ts"],"sourcesContent":["import * as path from \"node:path\";\nimport * as fs from \"node:fs/promises\";\nimport * as zlib from \"node:zlib\";\nimport { promisify } from \"node:util\";\nimport { writeTextFile, readTextFile } from \"../utils/file-utils.js\";\nimport { getConversationsPath, loadConfig } from \"../utils/config.js\";\nimport { spawnClaudeAgent } from \"../utils/claude-sdk.js\";\n\nconst gzip = promisify(zlib.gzip);\n\nconst SUMMARIZATION_PROMPT = `You are a technical knowledge extractor. Analyze this conversation log and extract key information for a project knowledge base.\n\nExtract and format the following:\n\n## Entities\nFor each domain entity (data model, type, class) created or modified:\n- **Name**: Entity name\n- **Location**: File path\n- **Attributes**: Key fields/properties\n- **Relations**: Related entities\n\n## Architecture\nFor each architectural pattern or design decision:\n- **Pattern**: Name of pattern\n- **Description**: Brief explanation\n- **Affected Files**: Relevant file paths\n\n## Services\nFor each service or component created:\n- **Name**: Service name\n- **Location**: File path\n- **Purpose**: Brief description\n- **Methods**: Key methods/functions\n\n## Knowledge\nFor each convention, rule, or important context:\n- **Topic**: What it's about\n- **Details**: The actual information\n\nOnly include sections that have content. Be concise but complete.\nUse file paths exactly as shown in the conversation.\n\nCONVERSATION LOG:\n`;\n\nexport interface Summary {\n sessionId: string;\n content: string;\n entities: ExtractedEntity[];\n architecture: ArchitectureItem[];\n services: ServiceItem[];\n knowledge: KnowledgeItem[];\n}\n\nexport interface ExtractedEntity {\n name: string;\n location?: string;\n attributes: string[];\n relations: string[];\n}\n\nexport interface ArchitectureItem {\n pattern: string;\n description: string;\n affectedFiles: string[];\n}\n\nexport interface ServiceItem {\n name: string;\n location?: string;\n purpose: string;\n methods: string[];\n}\n\nexport interface KnowledgeItem {\n topic: string;\n details: string;\n}\n\nexport class CompactionEngine {\n private projectPath: string;\n\n constructor(projectPath: string) {\n this.projectPath = projectPath;\n }\n\n async compact(sessionId: string, conversation: string): Promise<Summary | null> {\n try {\n // Use Claude SDK to generate summary\n const summaryContent = await this.generateSummary(conversation);\n\n if (!summaryContent) {\n return null;\n }\n\n // Parse the summary to extract structured data\n const summary = this.parseSummary(sessionId, summaryContent);\n\n // Write summary to conversation folder\n await this.writeSummary(sessionId, summaryContent);\n\n // Cleanup original conversation files based on config\n await this.cleanupConversationFiles(sessionId);\n\n return summary;\n } catch (error) {\n console.error(\"Compaction failed:\", error);\n return null;\n }\n }\n\n private async generateSummary(conversation: string): Promise<string | null> {\n const prompt = SUMMARIZATION_PROMPT + conversation;\n\n try {\n console.error(\"[CCKB] Calling Claude for summarization...\");\n const result = await spawnClaudeAgent(prompt);\n console.error(\"[CCKB] Claude summarization complete\");\n return result;\n } catch (error) {\n console.error(\"[CCKB] Claude failed, using fallback:\", error);\n return this.fallbackExtraction(conversation);\n }\n }\n\n private fallbackExtraction(conversation: string): string {\n // Basic extraction without AI - just capture file paths and actions\n const lines = conversation.split(\"\\n\");\n const files: string[] = [];\n const actions: string[] = [];\n\n for (const line of lines) {\n // Extract file paths\n const fileMatch = line.match(/(?:Created|Modified|Edited).*?:\\s*(.+\\.(?:ts|js|tsx|jsx|md))/i);\n if (fileMatch) {\n files.push(fileMatch[1]);\n }\n\n // Extract tool actions\n if (line.includes(\"[TOOL:\")) {\n actions.push(line);\n }\n }\n\n let summary = \"# Session Summary\\n\\n\";\n\n if (files.length > 0) {\n summary += \"## Files Modified\\n\";\n for (const file of [...new Set(files)]) {\n summary += `- ${file}\\n`;\n }\n summary += \"\\n\";\n }\n\n if (actions.length > 0) {\n summary += \"## Actions\\n\";\n for (const action of actions.slice(0, 20)) {\n summary += `- ${action}\\n`;\n }\n }\n\n return summary;\n }\n\n private parseSummary(sessionId: string, content: string): Summary {\n const summary: Summary = {\n sessionId,\n content,\n entities: [],\n architecture: [],\n services: [],\n knowledge: [],\n };\n\n // Parse entities section\n const entitiesMatch = content.match(/## Entities\\n([\\s\\S]*?)(?=\\n## |$)/);\n if (entitiesMatch) {\n summary.entities = this.parseEntities(entitiesMatch[1]);\n }\n\n // Parse architecture section\n const archMatch = content.match(/## Architecture\\n([\\s\\S]*?)(?=\\n## |$)/);\n if (archMatch) {\n summary.architecture = this.parseArchitecture(archMatch[1]);\n }\n\n // Parse services section\n const servicesMatch = content.match(/## Services\\n([\\s\\S]*?)(?=\\n## |$)/);\n if (servicesMatch) {\n summary.services = this.parseServices(servicesMatch[1]);\n }\n\n // Parse knowledge section\n const knowledgeMatch = content.match(/## Knowledge\\n([\\s\\S]*?)(?=\\n## |$)/);\n if (knowledgeMatch) {\n summary.knowledge = this.parseKnowledge(knowledgeMatch[1]);\n }\n\n return summary;\n }\n\n private parseEntities(section: string): ExtractedEntity[] {\n const entities: ExtractedEntity[] = [];\n const blocks = section.split(/\\n(?=- \\*\\*Name\\*\\*:)/);\n\n for (const block of blocks) {\n if (!block.trim()) continue;\n\n const nameMatch = block.match(/\\*\\*Name\\*\\*:\\s*(.+)/);\n const locationMatch = block.match(/\\*\\*Location\\*\\*:\\s*(.+)/);\n const attributesMatch = block.match(/\\*\\*Attributes\\*\\*:\\s*(.+)/);\n const relationsMatch = block.match(/\\*\\*Relations\\*\\*:\\s*(.+)/);\n\n if (nameMatch) {\n entities.push({\n name: nameMatch[1].trim(),\n location: locationMatch?.[1].trim(),\n attributes: attributesMatch?.[1].split(\",\").map((a) => a.trim()) || [],\n relations: relationsMatch?.[1].split(\",\").map((r) => r.trim()) || [],\n });\n }\n }\n\n return entities;\n }\n\n private parseArchitecture(section: string): ArchitectureItem[] {\n const items: ArchitectureItem[] = [];\n const blocks = section.split(/\\n(?=- \\*\\*Pattern\\*\\*:)/);\n\n for (const block of blocks) {\n if (!block.trim()) continue;\n\n const patternMatch = block.match(/\\*\\*Pattern\\*\\*:\\s*(.+)/);\n const descMatch = block.match(/\\*\\*Description\\*\\*:\\s*(.+)/);\n const filesMatch = block.match(/\\*\\*Affected Files\\*\\*:\\s*(.+)/);\n\n if (patternMatch) {\n items.push({\n pattern: patternMatch[1].trim(),\n description: descMatch?.[1].trim() || \"\",\n affectedFiles: filesMatch?.[1].split(\",\").map((f) => f.trim()) || [],\n });\n }\n }\n\n return items;\n }\n\n private parseServices(section: string): ServiceItem[] {\n const items: ServiceItem[] = [];\n const blocks = section.split(/\\n(?=- \\*\\*Name\\*\\*:)/);\n\n for (const block of blocks) {\n if (!block.trim()) continue;\n\n const nameMatch = block.match(/\\*\\*Name\\*\\*:\\s*(.+)/);\n const locationMatch = block.match(/\\*\\*Location\\*\\*:\\s*(.+)/);\n const purposeMatch = block.match(/\\*\\*Purpose\\*\\*:\\s*(.+)/);\n const methodsMatch = block.match(/\\*\\*Methods\\*\\*:\\s*(.+)/);\n\n if (nameMatch) {\n items.push({\n name: nameMatch[1].trim(),\n location: locationMatch?.[1].trim(),\n purpose: purposeMatch?.[1].trim() || \"\",\n methods: methodsMatch?.[1].split(\",\").map((m) => m.trim()) || [],\n });\n }\n }\n\n return items;\n }\n\n private parseKnowledge(section: string): KnowledgeItem[] {\n const items: KnowledgeItem[] = [];\n const blocks = section.split(/\\n(?=- \\*\\*Topic\\*\\*:)/);\n\n for (const block of blocks) {\n if (!block.trim()) continue;\n\n const topicMatch = block.match(/\\*\\*Topic\\*\\*:\\s*(.+)/);\n const detailsMatch = block.match(/\\*\\*Details\\*\\*:\\s*(.+)/);\n\n if (topicMatch) {\n items.push({\n topic: topicMatch[1].trim(),\n details: detailsMatch?.[1].trim() || \"\",\n });\n }\n }\n\n return items;\n }\n\n private async writeSummary(sessionId: string, content: string): Promise<void> {\n const conversationsPath = getConversationsPath(this.projectPath);\n const summaryPath = path.join(conversationsPath, sessionId, \"summary.md\");\n\n const fullContent = `# Session Summary: ${sessionId}\nGenerated: ${new Date().toISOString()}\n\n${content}\n`;\n\n await writeTextFile(summaryPath, fullContent);\n }\n\n private async cleanupConversationFiles(sessionId: string): Promise<void> {\n const config = await loadConfig(this.projectPath);\n const cleanupMode = config.compaction.cleanupAfterSummary;\n\n if (cleanupMode === \"keep\") {\n return; // Nothing to do\n }\n\n const conversationsPath = getConversationsPath(this.projectPath);\n const sessionPath = path.join(conversationsPath, sessionId);\n\n // Get all conversation files (numbered .txt files)\n const files = await fs.readdir(sessionPath);\n const conversationFiles = files.filter(\n (f) => /^\\d+\\.txt$/.test(f)\n );\n\n if (conversationFiles.length === 0) {\n return;\n }\n\n if (cleanupMode === \"archive\") {\n await this.archiveConversationFiles(sessionPath, conversationFiles);\n } else if (cleanupMode === \"delete\") {\n await this.deleteConversationFiles(sessionPath, conversationFiles);\n }\n }\n\n private async archiveConversationFiles(\n sessionPath: string,\n files: string[]\n ): Promise<void> {\n try {\n // Combine all conversation files into one archive\n const contents: string[] = [];\n\n for (const file of files.sort()) {\n const filePath = path.join(sessionPath, file);\n const content = await readTextFile(filePath);\n if (content) {\n contents.push(`=== ${file} ===\\n${content}`);\n }\n }\n\n const combined = contents.join(\"\\n\\n\");\n const compressed = await gzip(Buffer.from(combined, \"utf-8\"));\n\n // Write archive\n const archivePath = path.join(sessionPath, \"raw.txt.gz\");\n await fs.writeFile(archivePath, compressed);\n\n // Delete original files\n await this.deleteConversationFiles(sessionPath, files);\n } catch (error) {\n console.error(\"Failed to archive conversation files:\", error);\n // Don't delete originals if archive fails\n }\n }\n\n private async deleteConversationFiles(\n sessionPath: string,\n files: string[]\n ): Promise<void> {\n for (const file of files) {\n try {\n await fs.unlink(path.join(sessionPath, file));\n } catch {\n // Ignore deletion errors\n }\n }\n }\n}\n"],"mappings":"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","names":["gzip"]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  CompactionEngine
3
- } from "../chunk-KZ2I74VT.js";
3
+ } from "../chunk-OZQVYSKC.js";
4
4
  import {
5
5
  VaultIntegrator
6
6
  } from "../chunk-AJ2AVV5V.js";
@@ -19,42 +19,61 @@ async function handleStop() {
19
19
  const input = await readStdin();
20
20
  const data = input ? JSON.parse(input) : {};
21
21
  const projectPath = data.cwd || process.cwd();
22
+ console.error(`[CCKB] Stop hook triggered for: ${projectPath}`);
22
23
  const manager = new ConversationManager(projectPath);
23
24
  const sessionId = await manager.getActiveSessionId();
24
25
  if (!sessionId) {
26
+ console.error("[CCKB] No active session found, skipping compaction");
25
27
  console.log(JSON.stringify({ continue: true, suppressOutput: true }));
26
28
  return;
27
29
  }
28
- runCompactionAsync(projectPath, sessionId).catch(() => {
29
- });
30
+ console.error(`[CCKB] Starting compaction for session: ${sessionId}`);
31
+ try {
32
+ await runCompactionAsync(projectPath, sessionId);
33
+ console.error("[CCKB] Compaction complete");
34
+ } catch (error) {
35
+ console.error("[CCKB] Compaction failed:", error);
36
+ }
30
37
  const output = {
31
38
  continue: true,
32
39
  suppressOutput: true
33
40
  };
34
41
  console.log(JSON.stringify(output));
35
42
  } catch (error) {
43
+ console.error("[CCKB] Stop hook error:", error);
36
44
  console.log(JSON.stringify({ continue: true, suppressOutput: true }));
37
45
  }
38
46
  }
39
47
  async function runCompactionAsync(projectPath, sessionId) {
40
- const config = await loadConfig(projectPath);
41
- if (config.compaction.trigger !== "session_end") {
42
- return;
43
- }
44
- const manager = new ConversationManager(projectPath);
45
- const conversation = await manager.getFullConversation(sessionId);
46
- if (!conversation || conversation.length < 100) {
47
- return;
48
- }
49
- const compactionEngine = new CompactionEngine(projectPath);
50
- const summary = await compactionEngine.compact(sessionId, conversation);
51
- if (!summary) {
52
- return;
53
- }
54
- if (config.vault.autoIntegrate) {
55
- const vaultPath = getVaultPath(projectPath);
56
- const integrator = new VaultIntegrator(vaultPath);
57
- await integrator.integrate(summary);
48
+ try {
49
+ const config = await loadConfig(projectPath);
50
+ if (config.compaction.trigger !== "session_end") {
51
+ console.error(`[CCKB] Skipping - trigger is "${config.compaction.trigger}"`);
52
+ return;
53
+ }
54
+ const manager = new ConversationManager(projectPath);
55
+ const conversation = await manager.getFullConversation(sessionId);
56
+ if (!conversation || conversation.length < 100) {
57
+ console.error(`[CCKB] Skipping - conversation too short (${conversation?.length || 0} chars)`);
58
+ return;
59
+ }
60
+ console.error(`[CCKB] Compacting ${conversation.length} chars...`);
61
+ const compactionEngine = new CompactionEngine(projectPath);
62
+ const summary = await compactionEngine.compact(sessionId, conversation);
63
+ if (!summary) {
64
+ console.error("[CCKB] Compaction returned null");
65
+ return;
66
+ }
67
+ console.error(`[CCKB] Summary created: ${summary.entities.length} entities`);
68
+ if (config.vault.autoIntegrate) {
69
+ const vaultPath = getVaultPath(projectPath);
70
+ const integrator = new VaultIntegrator(vaultPath);
71
+ await integrator.integrate(summary);
72
+ console.error("[CCKB] Vault updated");
73
+ }
74
+ } catch (error) {
75
+ console.error("[CCKB] runCompactionAsync error:", error);
76
+ throw error;
58
77
  }
59
78
  }
60
79
  async function readStdin() {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/hooks/stop.ts"],"sourcesContent":["import { ConversationManager } from \"../core/conversation-manager.js\";\nimport { CompactionEngine } from \"../core/compaction-engine.js\";\nimport { VaultIntegrator } from \"../core/vault-integrator.js\";\nimport { loadConfig, getVaultPath } from \"../utils/config.js\";\n\ninterface StopInput {\n cwd?: string;\n}\n\ninterface HookOutput {\n continue: boolean;\n suppressOutput?: boolean;\n}\n\nexport async function handleStop(): Promise<void> {\n try {\n // Read input from stdin\n const input = await readStdin();\n const data: StopInput = input ? JSON.parse(input) : {};\n\n const projectPath = data.cwd || process.cwd();\n\n // Get active session\n const manager = new ConversationManager(projectPath);\n const sessionId = await manager.getActiveSessionId();\n\n if (!sessionId) {\n console.log(JSON.stringify({ continue: true, suppressOutput: true }));\n return;\n }\n\n // Run compaction asynchronously (don't block session exit)\n runCompactionAsync(projectPath, sessionId).catch(() => {\n // Silent failure\n });\n\n const output: HookOutput = {\n continue: true,\n suppressOutput: true,\n };\n\n console.log(JSON.stringify(output));\n } catch (error) {\n console.log(JSON.stringify({ continue: true, suppressOutput: true }));\n }\n}\n\nasync function runCompactionAsync(\n projectPath: string,\n sessionId: string\n): Promise<void> {\n const config = await loadConfig(projectPath);\n\n if (config.compaction.trigger !== \"session_end\") {\n return;\n }\n\n // Get conversation content\n const manager = new ConversationManager(projectPath);\n const conversation = await manager.getFullConversation(sessionId);\n\n if (!conversation || conversation.length < 100) {\n // Skip empty or very short conversations\n return;\n }\n\n // Run compaction\n const compactionEngine = new CompactionEngine(projectPath);\n const summary = await compactionEngine.compact(sessionId, conversation);\n\n if (!summary) {\n return;\n }\n\n // Integrate into vault if enabled\n if (config.vault.autoIntegrate) {\n const vaultPath = getVaultPath(projectPath);\n const integrator = new VaultIntegrator(vaultPath);\n await integrator.integrate(summary);\n }\n}\n\nasync function readStdin(): Promise<string> {\n return new Promise((resolve) => {\n let data = \"\";\n\n if (process.stdin.isTTY) {\n resolve(\"\");\n return;\n }\n\n process.stdin.setEncoding(\"utf8\");\n process.stdin.on(\"data\", (chunk) => {\n data += chunk;\n });\n process.stdin.on(\"end\", () => {\n resolve(data.trim());\n });\n\n setTimeout(() => {\n resolve(data.trim());\n }, 1000);\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAcA,eAAsB,aAA4B;AAChD,MAAI;AAEF,UAAM,QAAQ,MAAM,UAAU;AAC9B,UAAM,OAAkB,QAAQ,KAAK,MAAM,KAAK,IAAI,CAAC;AAErD,UAAM,cAAc,KAAK,OAAO,QAAQ,IAAI;AAG5C,UAAM,UAAU,IAAI,oBAAoB,WAAW;AACnD,UAAM,YAAY,MAAM,QAAQ,mBAAmB;AAEnD,QAAI,CAAC,WAAW;AACd,cAAQ,IAAI,KAAK,UAAU,EAAE,UAAU,MAAM,gBAAgB,KAAK,CAAC,CAAC;AACpE;AAAA,IACF;AAGA,uBAAmB,aAAa,SAAS,EAAE,MAAM,MAAM;AAAA,IAEvD,CAAC;AAED,UAAM,SAAqB;AAAA,MACzB,UAAU;AAAA,MACV,gBAAgB;AAAA,IAClB;AAEA,YAAQ,IAAI,KAAK,UAAU,MAAM,CAAC;AAAA,EACpC,SAAS,OAAO;AACd,YAAQ,IAAI,KAAK,UAAU,EAAE,UAAU,MAAM,gBAAgB,KAAK,CAAC,CAAC;AAAA,EACtE;AACF;AAEA,eAAe,mBACb,aACA,WACe;AACf,QAAM,SAAS,MAAM,WAAW,WAAW;AAE3C,MAAI,OAAO,WAAW,YAAY,eAAe;AAC/C;AAAA,EACF;AAGA,QAAM,UAAU,IAAI,oBAAoB,WAAW;AACnD,QAAM,eAAe,MAAM,QAAQ,oBAAoB,SAAS;AAEhE,MAAI,CAAC,gBAAgB,aAAa,SAAS,KAAK;AAE9C;AAAA,EACF;AAGA,QAAM,mBAAmB,IAAI,iBAAiB,WAAW;AACzD,QAAM,UAAU,MAAM,iBAAiB,QAAQ,WAAW,YAAY;AAEtE,MAAI,CAAC,SAAS;AACZ;AAAA,EACF;AAGA,MAAI,OAAO,MAAM,eAAe;AAC9B,UAAM,YAAY,aAAa,WAAW;AAC1C,UAAM,aAAa,IAAI,gBAAgB,SAAS;AAChD,UAAM,WAAW,UAAU,OAAO;AAAA,EACpC;AACF;AAEA,eAAe,YAA6B;AAC1C,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,QAAI,OAAO;AAEX,QAAI,QAAQ,MAAM,OAAO;AACvB,cAAQ,EAAE;AACV;AAAA,IACF;AAEA,YAAQ,MAAM,YAAY,MAAM;AAChC,YAAQ,MAAM,GAAG,QAAQ,CAAC,UAAU;AAClC,cAAQ;AAAA,IACV,CAAC;AACD,YAAQ,MAAM,GAAG,OAAO,MAAM;AAC5B,cAAQ,KAAK,KAAK,CAAC;AAAA,IACrB,CAAC;AAED,eAAW,MAAM;AACf,cAAQ,KAAK,KAAK,CAAC;AAAA,IACrB,GAAG,GAAI;AAAA,EACT,CAAC;AACH;","names":[]}
1
+ {"version":3,"sources":["../../src/hooks/stop.ts"],"sourcesContent":["import { ConversationManager } from \"../core/conversation-manager.js\";\nimport { CompactionEngine } from \"../core/compaction-engine.js\";\nimport { VaultIntegrator } from \"../core/vault-integrator.js\";\nimport { loadConfig, getVaultPath } from \"../utils/config.js\";\n\ninterface StopInput {\n cwd?: string;\n}\n\ninterface HookOutput {\n continue: boolean;\n suppressOutput?: boolean;\n}\n\nexport async function handleStop(): Promise<void> {\n try {\n // Read input from stdin\n const input = await readStdin();\n const data: StopInput = input ? JSON.parse(input) : {};\n\n const projectPath = data.cwd || process.cwd();\n\n // Log to stderr for debugging (stdout is for hook protocol)\n console.error(`[CCKB] Stop hook triggered for: ${projectPath}`);\n\n // Get active session\n const manager = new ConversationManager(projectPath);\n const sessionId = await manager.getActiveSessionId();\n\n if (!sessionId) {\n console.error(\"[CCKB] No active session found, skipping compaction\");\n console.log(JSON.stringify({ continue: true, suppressOutput: true }));\n return;\n }\n\n console.error(`[CCKB] Starting compaction for session: ${sessionId}`);\n\n // Run compaction and wait for it to complete before exiting\n try {\n await runCompactionAsync(projectPath, sessionId);\n console.error(\"[CCKB] Compaction complete\");\n } catch (error) {\n console.error(\"[CCKB] Compaction failed:\", error);\n }\n\n const output: HookOutput = {\n continue: true,\n suppressOutput: true,\n };\n\n console.log(JSON.stringify(output));\n } catch (error) {\n console.error(\"[CCKB] Stop hook error:\", error);\n console.log(JSON.stringify({ continue: true, suppressOutput: true }));\n }\n}\n\nasync function runCompactionAsync(\n projectPath: string,\n sessionId: string\n): Promise<void> {\n try {\n const config = await loadConfig(projectPath);\n\n if (config.compaction.trigger !== \"session_end\") {\n console.error(`[CCKB] Skipping - trigger is \"${config.compaction.trigger}\"`);\n return;\n }\n\n const manager = new ConversationManager(projectPath);\n const conversation = await manager.getFullConversation(sessionId);\n\n if (!conversation || conversation.length < 100) {\n console.error(`[CCKB] Skipping - conversation too short (${conversation?.length || 0} chars)`);\n return;\n }\n\n console.error(`[CCKB] Compacting ${conversation.length} chars...`);\n\n const compactionEngine = new CompactionEngine(projectPath);\n const summary = await compactionEngine.compact(sessionId, conversation);\n\n if (!summary) {\n console.error(\"[CCKB] Compaction returned null\");\n return;\n }\n\n console.error(`[CCKB] Summary created: ${summary.entities.length} entities`);\n\n if (config.vault.autoIntegrate) {\n const vaultPath = getVaultPath(projectPath);\n const integrator = new VaultIntegrator(vaultPath);\n await integrator.integrate(summary);\n console.error(\"[CCKB] Vault updated\");\n }\n } catch (error) {\n console.error(\"[CCKB] runCompactionAsync error:\", error);\n throw error;\n }\n}\n\nasync function readStdin(): Promise<string> {\n return new Promise((resolve) => {\n let data = \"\";\n\n if (process.stdin.isTTY) {\n resolve(\"\");\n return;\n }\n\n process.stdin.setEncoding(\"utf8\");\n process.stdin.on(\"data\", (chunk) => {\n data += chunk;\n });\n process.stdin.on(\"end\", () => {\n resolve(data.trim());\n });\n\n setTimeout(() => {\n resolve(data.trim());\n }, 1000);\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAcA,eAAsB,aAA4B;AAChD,MAAI;AAEF,UAAM,QAAQ,MAAM,UAAU;AAC9B,UAAM,OAAkB,QAAQ,KAAK,MAAM,KAAK,IAAI,CAAC;AAErD,UAAM,cAAc,KAAK,OAAO,QAAQ,IAAI;AAG5C,YAAQ,MAAM,mCAAmC,WAAW,EAAE;AAG9D,UAAM,UAAU,IAAI,oBAAoB,WAAW;AACnD,UAAM,YAAY,MAAM,QAAQ,mBAAmB;AAEnD,QAAI,CAAC,WAAW;AACd,cAAQ,MAAM,qDAAqD;AACnE,cAAQ,IAAI,KAAK,UAAU,EAAE,UAAU,MAAM,gBAAgB,KAAK,CAAC,CAAC;AACpE;AAAA,IACF;AAEA,YAAQ,MAAM,2CAA2C,SAAS,EAAE;AAGpE,QAAI;AACF,YAAM,mBAAmB,aAAa,SAAS;AAC/C,cAAQ,MAAM,4BAA4B;AAAA,IAC5C,SAAS,OAAO;AACd,cAAQ,MAAM,6BAA6B,KAAK;AAAA,IAClD;AAEA,UAAM,SAAqB;AAAA,MACzB,UAAU;AAAA,MACV,gBAAgB;AAAA,IAClB;AAEA,YAAQ,IAAI,KAAK,UAAU,MAAM,CAAC;AAAA,EACpC,SAAS,OAAO;AACd,YAAQ,MAAM,2BAA2B,KAAK;AAC9C,YAAQ,IAAI,KAAK,UAAU,EAAE,UAAU,MAAM,gBAAgB,KAAK,CAAC,CAAC;AAAA,EACtE;AACF;AAEA,eAAe,mBACb,aACA,WACe;AACf,MAAI;AACF,UAAM,SAAS,MAAM,WAAW,WAAW;AAE3C,QAAI,OAAO,WAAW,YAAY,eAAe;AAC/C,cAAQ,MAAM,iCAAiC,OAAO,WAAW,OAAO,GAAG;AAC3E;AAAA,IACF;AAEA,UAAM,UAAU,IAAI,oBAAoB,WAAW;AACnD,UAAM,eAAe,MAAM,QAAQ,oBAAoB,SAAS;AAEhE,QAAI,CAAC,gBAAgB,aAAa,SAAS,KAAK;AAC9C,cAAQ,MAAM,6CAA6C,cAAc,UAAU,CAAC,SAAS;AAC7F;AAAA,IACF;AAEA,YAAQ,MAAM,qBAAqB,aAAa,MAAM,WAAW;AAEjE,UAAM,mBAAmB,IAAI,iBAAiB,WAAW;AACzD,UAAM,UAAU,MAAM,iBAAiB,QAAQ,WAAW,YAAY;AAEtE,QAAI,CAAC,SAAS;AACZ,cAAQ,MAAM,iCAAiC;AAC/C;AAAA,IACF;AAEA,YAAQ,MAAM,2BAA2B,QAAQ,SAAS,MAAM,WAAW;AAE3E,QAAI,OAAO,MAAM,eAAe;AAC9B,YAAM,YAAY,aAAa,WAAW;AAC1C,YAAM,aAAa,IAAI,gBAAgB,SAAS;AAChD,YAAM,WAAW,UAAU,OAAO;AAClC,cAAQ,MAAM,sBAAsB;AAAA,IACtC;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,oCAAoC,KAAK;AACvD,UAAM;AAAA,EACR;AACF;AAEA,eAAe,YAA6B;AAC1C,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,QAAI,OAAO;AAEX,QAAI,QAAQ,MAAM,OAAO;AACvB,cAAQ,EAAE;AACV;AAAA,IACF;AAEA,YAAQ,MAAM,YAAY,MAAM;AAChC,YAAQ,MAAM,GAAG,QAAQ,CAAC,UAAU;AAClC,cAAQ;AAAA,IACV,CAAC;AACD,YAAQ,MAAM,GAAG,OAAO,MAAM;AAC5B,cAAQ,KAAK,KAAK,CAAC;AAAA,IACrB,CAAC;AAED,eAAW,MAAM;AACf,cAAQ,KAAK,KAAK,CAAC;AAAA,IACrB,GAAG,GAAI;AAAA,EACT,CAAC;AACH;","names":[]}
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-JTHQRYKF.js";
8
8
  import {
9
9
  CompactionEngine
10
- } from "./chunk-KZ2I74VT.js";
10
+ } from "./chunk-OZQVYSKC.js";
11
11
  import {
12
12
  EntityDetector,
13
13
  VaultIntegrator
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cckb",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "Claude Code Knowledge Base - Automatic project knowledge capture for Claude Code",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/core/compaction-engine.ts"],"sourcesContent":["import * as path from \"node:path\";\nimport * as fs from \"node:fs/promises\";\nimport * as zlib from \"node:zlib\";\nimport { promisify } from \"node:util\";\nimport { writeTextFile, readTextFile } from \"../utils/file-utils.js\";\nimport { getConversationsPath, loadConfig } from \"../utils/config.js\";\nimport { spawnClaudeAgent } from \"../utils/claude-sdk.js\";\n\nconst gzip = promisify(zlib.gzip);\n\nconst SUMMARIZATION_PROMPT = `You are a technical knowledge extractor. Analyze this conversation log and extract key information for a project knowledge base.\n\nExtract and format the following:\n\n## Entities\nFor each domain entity (data model, type, class) created or modified:\n- **Name**: Entity name\n- **Location**: File path\n- **Attributes**: Key fields/properties\n- **Relations**: Related entities\n\n## Architecture\nFor each architectural pattern or design decision:\n- **Pattern**: Name of pattern\n- **Description**: Brief explanation\n- **Affected Files**: Relevant file paths\n\n## Services\nFor each service or component created:\n- **Name**: Service name\n- **Location**: File path\n- **Purpose**: Brief description\n- **Methods**: Key methods/functions\n\n## Knowledge\nFor each convention, rule, or important context:\n- **Topic**: What it's about\n- **Details**: The actual information\n\nOnly include sections that have content. Be concise but complete.\nUse file paths exactly as shown in the conversation.\n\nCONVERSATION LOG:\n`;\n\nexport interface Summary {\n sessionId: string;\n content: string;\n entities: ExtractedEntity[];\n architecture: ArchitectureItem[];\n services: ServiceItem[];\n knowledge: KnowledgeItem[];\n}\n\nexport interface ExtractedEntity {\n name: string;\n location?: string;\n attributes: string[];\n relations: string[];\n}\n\nexport interface ArchitectureItem {\n pattern: string;\n description: string;\n affectedFiles: string[];\n}\n\nexport interface ServiceItem {\n name: string;\n location?: string;\n purpose: string;\n methods: string[];\n}\n\nexport interface KnowledgeItem {\n topic: string;\n details: string;\n}\n\nexport class CompactionEngine {\n private projectPath: string;\n\n constructor(projectPath: string) {\n this.projectPath = projectPath;\n }\n\n async compact(sessionId: string, conversation: string): Promise<Summary | null> {\n try {\n // Use Claude SDK to generate summary\n const summaryContent = await this.generateSummary(conversation);\n\n if (!summaryContent) {\n return null;\n }\n\n // Parse the summary to extract structured data\n const summary = this.parseSummary(sessionId, summaryContent);\n\n // Write summary to conversation folder\n await this.writeSummary(sessionId, summaryContent);\n\n // Cleanup original conversation files based on config\n await this.cleanupConversationFiles(sessionId);\n\n return summary;\n } catch (error) {\n console.error(\"Compaction failed:\", error);\n return null;\n }\n }\n\n private async generateSummary(conversation: string): Promise<string | null> {\n const prompt = SUMMARIZATION_PROMPT + conversation;\n\n try {\n const result = await spawnClaudeAgent(prompt);\n return result;\n } catch (error) {\n // Fallback to basic extraction if Claude SDK fails\n return this.fallbackExtraction(conversation);\n }\n }\n\n private fallbackExtraction(conversation: string): string {\n // Basic extraction without AI - just capture file paths and actions\n const lines = conversation.split(\"\\n\");\n const files: string[] = [];\n const actions: string[] = [];\n\n for (const line of lines) {\n // Extract file paths\n const fileMatch = line.match(/(?:Created|Modified|Edited).*?:\\s*(.+\\.(?:ts|js|tsx|jsx|md))/i);\n if (fileMatch) {\n files.push(fileMatch[1]);\n }\n\n // Extract tool actions\n if (line.includes(\"[TOOL:\")) {\n actions.push(line);\n }\n }\n\n let summary = \"# Session Summary\\n\\n\";\n\n if (files.length > 0) {\n summary += \"## Files Modified\\n\";\n for (const file of [...new Set(files)]) {\n summary += `- ${file}\\n`;\n }\n summary += \"\\n\";\n }\n\n if (actions.length > 0) {\n summary += \"## Actions\\n\";\n for (const action of actions.slice(0, 20)) {\n summary += `- ${action}\\n`;\n }\n }\n\n return summary;\n }\n\n private parseSummary(sessionId: string, content: string): Summary {\n const summary: Summary = {\n sessionId,\n content,\n entities: [],\n architecture: [],\n services: [],\n knowledge: [],\n };\n\n // Parse entities section\n const entitiesMatch = content.match(/## Entities\\n([\\s\\S]*?)(?=\\n## |$)/);\n if (entitiesMatch) {\n summary.entities = this.parseEntities(entitiesMatch[1]);\n }\n\n // Parse architecture section\n const archMatch = content.match(/## Architecture\\n([\\s\\S]*?)(?=\\n## |$)/);\n if (archMatch) {\n summary.architecture = this.parseArchitecture(archMatch[1]);\n }\n\n // Parse services section\n const servicesMatch = content.match(/## Services\\n([\\s\\S]*?)(?=\\n## |$)/);\n if (servicesMatch) {\n summary.services = this.parseServices(servicesMatch[1]);\n }\n\n // Parse knowledge section\n const knowledgeMatch = content.match(/## Knowledge\\n([\\s\\S]*?)(?=\\n## |$)/);\n if (knowledgeMatch) {\n summary.knowledge = this.parseKnowledge(knowledgeMatch[1]);\n }\n\n return summary;\n }\n\n private parseEntities(section: string): ExtractedEntity[] {\n const entities: ExtractedEntity[] = [];\n const blocks = section.split(/\\n(?=- \\*\\*Name\\*\\*:)/);\n\n for (const block of blocks) {\n if (!block.trim()) continue;\n\n const nameMatch = block.match(/\\*\\*Name\\*\\*:\\s*(.+)/);\n const locationMatch = block.match(/\\*\\*Location\\*\\*:\\s*(.+)/);\n const attributesMatch = block.match(/\\*\\*Attributes\\*\\*:\\s*(.+)/);\n const relationsMatch = block.match(/\\*\\*Relations\\*\\*:\\s*(.+)/);\n\n if (nameMatch) {\n entities.push({\n name: nameMatch[1].trim(),\n location: locationMatch?.[1].trim(),\n attributes: attributesMatch?.[1].split(\",\").map((a) => a.trim()) || [],\n relations: relationsMatch?.[1].split(\",\").map((r) => r.trim()) || [],\n });\n }\n }\n\n return entities;\n }\n\n private parseArchitecture(section: string): ArchitectureItem[] {\n const items: ArchitectureItem[] = [];\n const blocks = section.split(/\\n(?=- \\*\\*Pattern\\*\\*:)/);\n\n for (const block of blocks) {\n if (!block.trim()) continue;\n\n const patternMatch = block.match(/\\*\\*Pattern\\*\\*:\\s*(.+)/);\n const descMatch = block.match(/\\*\\*Description\\*\\*:\\s*(.+)/);\n const filesMatch = block.match(/\\*\\*Affected Files\\*\\*:\\s*(.+)/);\n\n if (patternMatch) {\n items.push({\n pattern: patternMatch[1].trim(),\n description: descMatch?.[1].trim() || \"\",\n affectedFiles: filesMatch?.[1].split(\",\").map((f) => f.trim()) || [],\n });\n }\n }\n\n return items;\n }\n\n private parseServices(section: string): ServiceItem[] {\n const items: ServiceItem[] = [];\n const blocks = section.split(/\\n(?=- \\*\\*Name\\*\\*:)/);\n\n for (const block of blocks) {\n if (!block.trim()) continue;\n\n const nameMatch = block.match(/\\*\\*Name\\*\\*:\\s*(.+)/);\n const locationMatch = block.match(/\\*\\*Location\\*\\*:\\s*(.+)/);\n const purposeMatch = block.match(/\\*\\*Purpose\\*\\*:\\s*(.+)/);\n const methodsMatch = block.match(/\\*\\*Methods\\*\\*:\\s*(.+)/);\n\n if (nameMatch) {\n items.push({\n name: nameMatch[1].trim(),\n location: locationMatch?.[1].trim(),\n purpose: purposeMatch?.[1].trim() || \"\",\n methods: methodsMatch?.[1].split(\",\").map((m) => m.trim()) || [],\n });\n }\n }\n\n return items;\n }\n\n private parseKnowledge(section: string): KnowledgeItem[] {\n const items: KnowledgeItem[] = [];\n const blocks = section.split(/\\n(?=- \\*\\*Topic\\*\\*:)/);\n\n for (const block of blocks) {\n if (!block.trim()) continue;\n\n const topicMatch = block.match(/\\*\\*Topic\\*\\*:\\s*(.+)/);\n const detailsMatch = block.match(/\\*\\*Details\\*\\*:\\s*(.+)/);\n\n if (topicMatch) {\n items.push({\n topic: topicMatch[1].trim(),\n details: detailsMatch?.[1].trim() || \"\",\n });\n }\n }\n\n return items;\n }\n\n private async writeSummary(sessionId: string, content: string): Promise<void> {\n const conversationsPath = getConversationsPath(this.projectPath);\n const summaryPath = path.join(conversationsPath, sessionId, \"summary.md\");\n\n const fullContent = `# Session Summary: ${sessionId}\nGenerated: ${new Date().toISOString()}\n\n${content}\n`;\n\n await writeTextFile(summaryPath, fullContent);\n }\n\n private async cleanupConversationFiles(sessionId: string): Promise<void> {\n const config = await loadConfig(this.projectPath);\n const cleanupMode = config.compaction.cleanupAfterSummary;\n\n if (cleanupMode === \"keep\") {\n return; // Nothing to do\n }\n\n const conversationsPath = getConversationsPath(this.projectPath);\n const sessionPath = path.join(conversationsPath, sessionId);\n\n // Get all conversation files (numbered .txt files)\n const files = await fs.readdir(sessionPath);\n const conversationFiles = files.filter(\n (f) => /^\\d+\\.txt$/.test(f)\n );\n\n if (conversationFiles.length === 0) {\n return;\n }\n\n if (cleanupMode === \"archive\") {\n await this.archiveConversationFiles(sessionPath, conversationFiles);\n } else if (cleanupMode === \"delete\") {\n await this.deleteConversationFiles(sessionPath, conversationFiles);\n }\n }\n\n private async archiveConversationFiles(\n sessionPath: string,\n files: string[]\n ): Promise<void> {\n try {\n // Combine all conversation files into one archive\n const contents: string[] = [];\n\n for (const file of files.sort()) {\n const filePath = path.join(sessionPath, file);\n const content = await readTextFile(filePath);\n if (content) {\n contents.push(`=== ${file} ===\\n${content}`);\n }\n }\n\n const combined = contents.join(\"\\n\\n\");\n const compressed = await gzip(Buffer.from(combined, \"utf-8\"));\n\n // Write archive\n const archivePath = path.join(sessionPath, \"raw.txt.gz\");\n await fs.writeFile(archivePath, compressed);\n\n // Delete original files\n await this.deleteConversationFiles(sessionPath, files);\n } catch (error) {\n console.error(\"Failed to archive conversation files:\", error);\n // Don't delete originals if archive fails\n }\n }\n\n private async deleteConversationFiles(\n sessionPath: string,\n files: string[]\n ): Promise<void> {\n for (const file of files) {\n try {\n await fs.unlink(path.join(sessionPath, file));\n } catch {\n // Ignore deletion errors\n }\n }\n }\n}\n"],"mappings":"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","names":["gzip"]}