cckb 0.1.3 → 0.1.5

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 +0,0 @@
1
- {"version":3,"sources":["../src/cli/install.ts"],"sourcesContent":["import * as fs from \"node:fs/promises\";\nimport * as fsSync from \"node:fs\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport {\n ensureDir,\n fileExists,\n readJSON,\n writeJSON,\n readTextFile,\n writeTextFile,\n} from \"../utils/file-utils.js\";\nimport { DEFAULT_CONFIG } from \"../utils/config.js\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\n// Find package root by looking for package.json\nfunction findPackageRoot(): string {\n let dir = __dirname;\n for (let i = 0; i < 5; i++) {\n const packageJson = path.join(dir, \"package.json\");\n if (fsSync.existsSync(packageJson)) {\n return dir;\n }\n dir = path.dirname(dir);\n }\n // Fallback to relative from __dirname\n return path.resolve(__dirname, \"../..\");\n}\n\nconst PACKAGE_ROOT = findPackageRoot();\nconst TEMPLATES_DIR = path.join(PACKAGE_ROOT, \"templates\");\n\nexport interface InstallOptions {\n force?: boolean;\n}\n\nexport async function install(\n targetPath: string,\n options: InstallOptions = {}\n): Promise<void> {\n const resolvedPath = path.resolve(targetPath);\n\n console.log(`Installing CCKB to: ${resolvedPath}`);\n\n // Pre-flight checks\n await validateTargetPath(resolvedPath);\n await checkExistingInstallation(resolvedPath, options.force);\n\n // Create directory structure\n await createDirectoryStructure(resolvedPath);\n\n // Copy template files\n await copyTemplateFiles(resolvedPath);\n\n // Create config file\n await createConfigFile(resolvedPath);\n\n // Install hooks configuration\n await installHooks(resolvedPath);\n\n // Update CLAUDE.md\n await updateClaudeMd(resolvedPath);\n\n // Update .gitignore\n await updateGitignore(resolvedPath);\n\n console.log(\"\\nCCKB installed successfully!\");\n console.log(\"\\nNext steps:\");\n console.log(\" 1. Review cc-knowledge-base/vault/ structure\");\n console.log(\" 2. Check .claude/settings.json for hook configuration\");\n console.log(\" 3. Start a new Claude Code session to begin capturing knowledge\");\n}\n\nasync function validateTargetPath(targetPath: string): Promise<void> {\n const exists = await fileExists(targetPath);\n if (!exists) {\n throw new Error(`Target path does not exist: ${targetPath}`);\n }\n\n const stats = await fs.stat(targetPath);\n if (!stats.isDirectory()) {\n throw new Error(`Target path is not a directory: ${targetPath}`);\n }\n}\n\nasync function checkExistingInstallation(\n targetPath: string,\n force?: boolean\n): Promise<void> {\n const kbPath = path.join(targetPath, \"cc-knowledge-base\");\n const exists = await fileExists(kbPath);\n\n if (exists && !force) {\n throw new Error(\n \"CCKB is already installed in this project. Use --force to reinstall.\"\n );\n }\n}\n\nasync function createDirectoryStructure(targetPath: string): Promise<void> {\n const directories = [\n \"cc-knowledge-base\",\n \"cc-knowledge-base/conversations\",\n \"cc-knowledge-base/vault\",\n \"cc-knowledge-base/vault/entities\",\n \"cc-knowledge-base/vault/apps\",\n \"cc-knowledge-base/vault/modules\",\n \"cc-knowledge-base/.cckb-state\",\n ];\n\n for (const dir of directories) {\n await ensureDir(path.join(targetPath, dir));\n }\n\n console.log(\" Created directory structure\");\n}\n\nasync function copyTemplateFiles(targetPath: string): Promise<void> {\n const vaultFiles = [\n { src: \"vault/INDEX.md\", dest: \"cc-knowledge-base/vault/INDEX.md\" },\n {\n src: \"vault/architecture.md\",\n dest: \"cc-knowledge-base/vault/architecture.md\",\n },\n {\n src: \"vault/general-knowledge.md\",\n dest: \"cc-knowledge-base/vault/general-knowledge.md\",\n },\n {\n src: \"vault/entities/INDEX.md\",\n dest: \"cc-knowledge-base/vault/entities/INDEX.md\",\n },\n {\n src: \"vault/apps/INDEX.md\",\n dest: \"cc-knowledge-base/vault/apps/INDEX.md\",\n },\n {\n src: \"vault/modules/INDEX.md\",\n dest: \"cc-knowledge-base/vault/modules/INDEX.md\",\n },\n ];\n\n for (const file of vaultFiles) {\n const srcPath = path.join(TEMPLATES_DIR, file.src);\n const destPath = path.join(targetPath, file.dest);\n\n const content = await readTextFile(srcPath);\n if (content) {\n await writeTextFile(destPath, content);\n }\n }\n\n // Create .gitkeep for conversations\n await writeTextFile(\n path.join(targetPath, \"cc-knowledge-base/conversations/.gitkeep\"),\n \"\"\n );\n\n console.log(\" Copied vault template files\");\n}\n\nasync function createConfigFile(targetPath: string): Promise<void> {\n const configPath = path.join(\n targetPath,\n \"cc-knowledge-base\",\n \".cckb-config.json\"\n );\n\n await writeJSON(configPath, DEFAULT_CONFIG);\n\n console.log(\" Created configuration file\");\n}\n\nasync function installHooks(targetPath: string): Promise<void> {\n const claudeDir = path.join(targetPath, \".claude\");\n const settingsPath = path.join(claudeDir, \"settings.json\");\n\n await ensureDir(claudeDir);\n\n // Load existing settings or create new\n let settings: Record<string, unknown> =\n (await readJSON<Record<string, unknown>>(settingsPath)) || {};\n\n // Load hook template\n const templatePath = path.join(TEMPLATES_DIR, \"settings.json.tmpl\");\n const hookSettings = await readJSON<{ hooks: Record<string, unknown> }>(\n templatePath\n );\n\n if (!hookSettings) {\n throw new Error(\"Failed to load hook template\");\n }\n\n // Merge hooks (CCKB hooks take precedence for its own hook types)\n const existingHooks = (settings.hooks as Record<string, unknown[]>) || {};\n const newHooks = hookSettings.hooks as Record<string, unknown[]>;\n\n settings.hooks = mergeHooks(existingHooks, newHooks);\n\n await writeJSON(settingsPath, settings);\n\n console.log(\" Installed hook configuration\");\n}\n\nfunction mergeHooks(\n existing: Record<string, unknown[]>,\n incoming: Record<string, unknown[]>\n): Record<string, unknown[]> {\n const merged = { ...existing };\n\n for (const [hookType, hooks] of Object.entries(incoming)) {\n if (!merged[hookType]) {\n merged[hookType] = [];\n }\n\n // Add incoming hooks, avoiding duplicates based on command\n for (const hook of hooks) {\n const hookObj = hook as Record<string, unknown>;\n const command = hookObj.command as string | undefined;\n\n if (command?.includes(\"cckb\")) {\n // Remove any existing CCKB hooks for this type\n merged[hookType] = merged[hookType].filter((h) => {\n const existingCmd = (h as Record<string, unknown>).command as\n | string\n | undefined;\n return !existingCmd?.includes(\"cckb\");\n });\n }\n\n merged[hookType].push(hook);\n }\n }\n\n return merged;\n}\n\nasync function updateClaudeMd(targetPath: string): Promise<void> {\n const claudeMdPath = path.join(targetPath, \"CLAUDE.md\");\n const templatePath = path.join(TEMPLATES_DIR, \"CLAUDE.md.tmpl\");\n\n const template = await readTextFile(templatePath);\n if (!template) {\n throw new Error(\"Failed to load CLAUDE.md template\");\n }\n\n const marker = \"## Project Knowledge Base (CCKB)\";\n\n let existing = await readTextFile(claudeMdPath);\n\n if (existing) {\n // Check if CCKB section already exists\n if (existing.includes(marker)) {\n // Replace existing CCKB section\n const regex = /## Project Knowledge Base \\(CCKB\\)[\\s\\S]*?(?=\\n## |$)/;\n existing = existing.replace(regex, template.trim());\n } else {\n // Append CCKB section\n existing = existing.trimEnd() + \"\\n\\n\" + template;\n }\n await writeTextFile(claudeMdPath, existing);\n } else {\n // Create new CLAUDE.md with CCKB section\n await writeTextFile(claudeMdPath, template);\n }\n\n console.log(\" Updated CLAUDE.md with vault directives\");\n}\n\nasync function updateGitignore(targetPath: string): Promise<void> {\n const gitignorePath = path.join(targetPath, \".gitignore\");\n\n const entries = [\n \"\",\n \"# CCKB state files\",\n \"cc-knowledge-base/.cckb-state/\",\n ];\n\n let existing = (await readTextFile(gitignorePath)) || \"\";\n\n // Check if already added\n if (existing.includes(\"cc-knowledge-base/.cckb-state/\")) {\n return;\n }\n\n existing = existing.trimEnd() + \"\\n\" + entries.join(\"\\n\") + \"\\n\";\n await writeTextFile(gitignorePath, existing);\n\n console.log(\" Updated .gitignore\");\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/file-utils.ts","../src/utils/config.ts"],"sourcesContent":["import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nexport async function ensureDir(dirPath: string): Promise<void> {\n await fs.mkdir(dirPath, { recursive: true });\n}\n\nexport async function fileExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function readJSON<T>(filePath: string): Promise<T | null> {\n try {\n const content = await fs.readFile(filePath, \"utf-8\");\n return JSON.parse(content) as T;\n } catch {\n return null;\n }\n}\n\nexport async function writeJSON(\n filePath: string,\n data: unknown,\n pretty = true\n): Promise<void> {\n await ensureDir(path.dirname(filePath));\n const content = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);\n await fs.writeFile(filePath, content);\n}\n\nexport async function appendToFile(\n filePath: string,\n content: string\n): Promise<void> {\n await ensureDir(path.dirname(filePath));\n await fs.appendFile(filePath, content);\n}\n\nexport async function readTextFile(filePath: string): Promise<string | null> {\n try {\n return await fs.readFile(filePath, \"utf-8\");\n } catch {\n return null;\n }\n}\n\nexport async function writeTextFile(\n filePath: string,\n content: string\n): Promise<void> {\n await ensureDir(path.dirname(filePath));\n await fs.writeFile(filePath, content);\n}\n\nexport async function copyFile(src: string, dest: string): Promise<void> {\n await ensureDir(path.dirname(dest));\n await fs.copyFile(src, dest);\n}\n\nexport async function listDir(dirPath: string): Promise<string[]> {\n try {\n return await fs.readdir(dirPath);\n } catch {\n return [];\n }\n}\n\nexport async function getFileSize(filePath: string): Promise<number> {\n try {\n const stats = await fs.stat(filePath);\n return stats.size;\n } catch {\n return 0;\n }\n}\n\nexport function generateSessionId(): string {\n const timestamp = Date.now().toString(36);\n const random = Math.random().toString(36).substring(2, 8);\n return `${timestamp}-${random}`;\n}\n\nexport function getCurrentTimestamp(): string {\n return new Date().toISOString();\n}\n","import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nexport interface CCKBConfig {\n compaction: {\n trigger: \"session_end\" | \"size\" | \"messages\" | \"manual\";\n sizeThresholdKB: number;\n messageThreshold: number;\n cleanupAfterSummary: \"keep\" | \"archive\" | \"delete\";\n };\n capture: {\n tools: string[];\n maxContentLength: number;\n };\n vault: {\n autoIntegrate: boolean;\n maxDepth: number;\n };\n feedback: {\n enabled: boolean;\n contextDepth: number;\n };\n}\n\nexport const DEFAULT_CONFIG: CCKBConfig = {\n compaction: {\n trigger: \"session_end\",\n sizeThresholdKB: 50,\n messageThreshold: 100,\n cleanupAfterSummary: \"keep\",\n },\n capture: {\n tools: [\"Write\", \"Edit\", \"MultiEdit\", \"Bash\", \"Task\"],\n maxContentLength: 500,\n },\n vault: {\n autoIntegrate: true,\n maxDepth: 5,\n },\n feedback: {\n enabled: true,\n contextDepth: 2,\n },\n};\n\nexport async function loadConfig(projectPath: string): Promise<CCKBConfig> {\n const configPath = path.join(\n projectPath,\n \"cc-knowledge-base\",\n \".cckb-config.json\"\n );\n\n try {\n const content = await fs.readFile(configPath, \"utf-8\");\n const userConfig = JSON.parse(content);\n return { ...DEFAULT_CONFIG, ...userConfig };\n } catch {\n return DEFAULT_CONFIG;\n }\n}\n\nexport async function saveConfig(\n projectPath: string,\n config: CCKBConfig\n): Promise<void> {\n const configPath = path.join(\n projectPath,\n \"cc-knowledge-base\",\n \".cckb-config.json\"\n );\n\n await fs.writeFile(configPath, JSON.stringify(config, null, 2));\n}\n\nexport function getKnowledgeBasePath(projectPath: string): string {\n return path.join(projectPath, \"cc-knowledge-base\");\n}\n\nexport function getConversationsPath(projectPath: string): string {\n return path.join(projectPath, \"cc-knowledge-base\", \"conversations\");\n}\n\nexport function getVaultPath(projectPath: string): string {\n return path.join(projectPath, \"cc-knowledge-base\", \"vault\");\n}\n\nexport function getStatePath(projectPath: string): string {\n return path.join(projectPath, \"cc-knowledge-base\", \".cckb-state\");\n}\n"],"mappings":";AAAA,YAAY,QAAQ;AACpB,YAAY,UAAU;AAEtB,eAAsB,UAAU,SAAgC;AAC9D,QAAS,SAAM,SAAS,EAAE,WAAW,KAAK,CAAC;AAC7C;AAEA,eAAsB,WAAW,UAAoC;AACnE,MAAI;AACF,UAAS,UAAO,QAAQ;AACxB,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,SAAY,UAAqC;AACrE,MAAI;AACF,UAAM,UAAU,MAAS,YAAS,UAAU,OAAO;AACnD,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,UACpB,UACA,MACA,SAAS,MACM;AACf,QAAM,UAAe,aAAQ,QAAQ,CAAC;AACtC,QAAM,UAAU,SAAS,KAAK,UAAU,MAAM,MAAM,CAAC,IAAI,KAAK,UAAU,IAAI;AAC5E,QAAS,aAAU,UAAU,OAAO;AACtC;AAEA,eAAsB,aACpB,UACA,SACe;AACf,QAAM,UAAe,aAAQ,QAAQ,CAAC;AACtC,QAAS,cAAW,UAAU,OAAO;AACvC;AAEA,eAAsB,aAAa,UAA0C;AAC3E,MAAI;AACF,WAAO,MAAS,YAAS,UAAU,OAAO;AAAA,EAC5C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,cACpB,UACA,SACe;AACf,QAAM,UAAe,aAAQ,QAAQ,CAAC;AACtC,QAAS,aAAU,UAAU,OAAO;AACtC;AAOA,eAAsB,QAAQ,SAAoC;AAChE,MAAI;AACF,WAAO,MAAS,WAAQ,OAAO;AAAA,EACjC,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,eAAsB,YAAY,UAAmC;AACnE,MAAI;AACF,UAAM,QAAQ,MAAS,QAAK,QAAQ;AACpC,WAAO,MAAM;AAAA,EACf,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,oBAA4B;AAC1C,QAAM,YAAY,KAAK,IAAI,EAAE,SAAS,EAAE;AACxC,QAAM,SAAS,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,GAAG,CAAC;AACxD,SAAO,GAAG,SAAS,IAAI,MAAM;AAC/B;AAEO,SAAS,sBAA8B;AAC5C,UAAO,oBAAI,KAAK,GAAE,YAAY;AAChC;;;ACzFA,YAAYA,SAAQ;AACpB,YAAYC,WAAU;AAuBf,IAAM,iBAA6B;AAAA,EACxC,YAAY;AAAA,IACV,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,kBAAkB;AAAA,IAClB,qBAAqB;AAAA,EACvB;AAAA,EACA,SAAS;AAAA,IACP,OAAO,CAAC,SAAS,QAAQ,aAAa,QAAQ,MAAM;AAAA,IACpD,kBAAkB;AAAA,EACpB;AAAA,EACA,OAAO;AAAA,IACL,eAAe;AAAA,IACf,UAAU;AAAA,EACZ;AAAA,EACA,UAAU;AAAA,IACR,SAAS;AAAA,IACT,cAAc;AAAA,EAChB;AACF;AAEA,eAAsB,WAAW,aAA0C;AACzE,QAAM,aAAkB;AAAA,IACtB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,MAAI;AACF,UAAM,UAAU,MAAS,aAAS,YAAY,OAAO;AACrD,UAAM,aAAa,KAAK,MAAM,OAAO;AACrC,WAAO,EAAE,GAAG,gBAAgB,GAAG,WAAW;AAAA,EAC5C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAmBO,SAAS,qBAAqB,aAA6B;AAChE,SAAY,WAAK,aAAa,qBAAqB,eAAe;AACpE;AAEO,SAAS,aAAa,aAA6B;AACxD,SAAY,WAAK,aAAa,qBAAqB,OAAO;AAC5D;AAEO,SAAS,aAAa,aAA6B;AACxD,SAAY,WAAK,aAAa,qBAAqB,aAAa;AAClE;","names":["fs","path"]}
@@ -1,602 +0,0 @@
1
- import {
2
- IndexManager
3
- } from "./chunk-NOCXYKIE.js";
4
- import {
5
- appendToFile,
6
- ensureDir,
7
- fileExists,
8
- getConversationsPath,
9
- loadConfig,
10
- readTextFile,
11
- writeTextFile
12
- } from "./chunk-E2IJSPVB.js";
13
-
14
- // src/core/compaction-engine.ts
15
- import * as path from "path";
16
- import * as fs from "fs/promises";
17
- import * as zlib from "zlib";
18
- import { promisify } from "util";
19
-
20
- // src/utils/claude-sdk.ts
21
- import { spawn } from "child_process";
22
- async function spawnClaudeAgent(prompt) {
23
- return new Promise((resolve, reject) => {
24
- const child = spawn("claude", ["--print", "-p", prompt], {
25
- stdio: ["pipe", "pipe", "pipe"],
26
- timeout: 12e4
27
- // 2 minute timeout
28
- });
29
- let stdout = "";
30
- let stderr = "";
31
- child.stdout?.on("data", (data) => {
32
- stdout += data.toString();
33
- });
34
- child.stderr?.on("data", (data) => {
35
- stderr += data.toString();
36
- });
37
- child.on("close", (code) => {
38
- if (code === 0) {
39
- resolve(stdout.trim());
40
- } else {
41
- reject(new Error(`Claude agent failed with code ${code}: ${stderr}`));
42
- }
43
- });
44
- child.on("error", (error) => {
45
- reject(error);
46
- });
47
- });
48
- }
49
-
50
- // src/core/compaction-engine.ts
51
- var gzip2 = promisify(zlib.gzip);
52
- var SUMMARIZATION_PROMPT = `You are a technical knowledge extractor. Analyze this conversation log and extract key information for a project knowledge base.
53
-
54
- Extract and format the following:
55
-
56
- ## Entities
57
- For each domain entity (data model, type, class) created or modified:
58
- - **Name**: Entity name
59
- - **Location**: File path
60
- - **Attributes**: Key fields/properties
61
- - **Relations**: Related entities
62
-
63
- ## Architecture
64
- For each architectural pattern or design decision:
65
- - **Pattern**: Name of pattern
66
- - **Description**: Brief explanation
67
- - **Affected Files**: Relevant file paths
68
-
69
- ## Services
70
- For each service or component created:
71
- - **Name**: Service name
72
- - **Location**: File path
73
- - **Purpose**: Brief description
74
- - **Methods**: Key methods/functions
75
-
76
- ## Knowledge
77
- For each convention, rule, or important context:
78
- - **Topic**: What it's about
79
- - **Details**: The actual information
80
-
81
- Only include sections that have content. Be concise but complete.
82
- Use file paths exactly as shown in the conversation.
83
-
84
- CONVERSATION LOG:
85
- `;
86
- var CompactionEngine = class {
87
- projectPath;
88
- constructor(projectPath) {
89
- this.projectPath = projectPath;
90
- }
91
- async compact(sessionId, conversation) {
92
- try {
93
- const summaryContent = await this.generateSummary(conversation);
94
- if (!summaryContent) {
95
- return null;
96
- }
97
- const summary = this.parseSummary(sessionId, summaryContent);
98
- await this.writeSummary(sessionId, summaryContent);
99
- await this.cleanupConversationFiles(sessionId);
100
- return summary;
101
- } catch (error) {
102
- console.error("Compaction failed:", error);
103
- return null;
104
- }
105
- }
106
- async generateSummary(conversation) {
107
- const prompt = SUMMARIZATION_PROMPT + conversation;
108
- try {
109
- const result = await spawnClaudeAgent(prompt);
110
- return result;
111
- } catch (error) {
112
- return this.fallbackExtraction(conversation);
113
- }
114
- }
115
- fallbackExtraction(conversation) {
116
- const lines = conversation.split("\n");
117
- const files = [];
118
- const actions = [];
119
- for (const line of lines) {
120
- const fileMatch = line.match(/(?:Created|Modified|Edited).*?:\s*(.+\.(?:ts|js|tsx|jsx|md))/i);
121
- if (fileMatch) {
122
- files.push(fileMatch[1]);
123
- }
124
- if (line.includes("[TOOL:")) {
125
- actions.push(line);
126
- }
127
- }
128
- let summary = "# Session Summary\n\n";
129
- if (files.length > 0) {
130
- summary += "## Files Modified\n";
131
- for (const file of [...new Set(files)]) {
132
- summary += `- ${file}
133
- `;
134
- }
135
- summary += "\n";
136
- }
137
- if (actions.length > 0) {
138
- summary += "## Actions\n";
139
- for (const action of actions.slice(0, 20)) {
140
- summary += `- ${action}
141
- `;
142
- }
143
- }
144
- return summary;
145
- }
146
- parseSummary(sessionId, content) {
147
- const summary = {
148
- sessionId,
149
- content,
150
- entities: [],
151
- architecture: [],
152
- services: [],
153
- knowledge: []
154
- };
155
- const entitiesMatch = content.match(/## Entities\n([\s\S]*?)(?=\n## |$)/);
156
- if (entitiesMatch) {
157
- summary.entities = this.parseEntities(entitiesMatch[1]);
158
- }
159
- const archMatch = content.match(/## Architecture\n([\s\S]*?)(?=\n## |$)/);
160
- if (archMatch) {
161
- summary.architecture = this.parseArchitecture(archMatch[1]);
162
- }
163
- const servicesMatch = content.match(/## Services\n([\s\S]*?)(?=\n## |$)/);
164
- if (servicesMatch) {
165
- summary.services = this.parseServices(servicesMatch[1]);
166
- }
167
- const knowledgeMatch = content.match(/## Knowledge\n([\s\S]*?)(?=\n## |$)/);
168
- if (knowledgeMatch) {
169
- summary.knowledge = this.parseKnowledge(knowledgeMatch[1]);
170
- }
171
- return summary;
172
- }
173
- parseEntities(section) {
174
- const entities = [];
175
- const blocks = section.split(/\n(?=- \*\*Name\*\*:)/);
176
- for (const block of blocks) {
177
- if (!block.trim()) continue;
178
- const nameMatch = block.match(/\*\*Name\*\*:\s*(.+)/);
179
- const locationMatch = block.match(/\*\*Location\*\*:\s*(.+)/);
180
- const attributesMatch = block.match(/\*\*Attributes\*\*:\s*(.+)/);
181
- const relationsMatch = block.match(/\*\*Relations\*\*:\s*(.+)/);
182
- if (nameMatch) {
183
- entities.push({
184
- name: nameMatch[1].trim(),
185
- location: locationMatch?.[1].trim(),
186
- attributes: attributesMatch?.[1].split(",").map((a) => a.trim()) || [],
187
- relations: relationsMatch?.[1].split(",").map((r) => r.trim()) || []
188
- });
189
- }
190
- }
191
- return entities;
192
- }
193
- parseArchitecture(section) {
194
- const items = [];
195
- const blocks = section.split(/\n(?=- \*\*Pattern\*\*:)/);
196
- for (const block of blocks) {
197
- if (!block.trim()) continue;
198
- const patternMatch = block.match(/\*\*Pattern\*\*:\s*(.+)/);
199
- const descMatch = block.match(/\*\*Description\*\*:\s*(.+)/);
200
- const filesMatch = block.match(/\*\*Affected Files\*\*:\s*(.+)/);
201
- if (patternMatch) {
202
- items.push({
203
- pattern: patternMatch[1].trim(),
204
- description: descMatch?.[1].trim() || "",
205
- affectedFiles: filesMatch?.[1].split(",").map((f) => f.trim()) || []
206
- });
207
- }
208
- }
209
- return items;
210
- }
211
- parseServices(section) {
212
- const items = [];
213
- const blocks = section.split(/\n(?=- \*\*Name\*\*:)/);
214
- for (const block of blocks) {
215
- if (!block.trim()) continue;
216
- const nameMatch = block.match(/\*\*Name\*\*:\s*(.+)/);
217
- const locationMatch = block.match(/\*\*Location\*\*:\s*(.+)/);
218
- const purposeMatch = block.match(/\*\*Purpose\*\*:\s*(.+)/);
219
- const methodsMatch = block.match(/\*\*Methods\*\*:\s*(.+)/);
220
- if (nameMatch) {
221
- items.push({
222
- name: nameMatch[1].trim(),
223
- location: locationMatch?.[1].trim(),
224
- purpose: purposeMatch?.[1].trim() || "",
225
- methods: methodsMatch?.[1].split(",").map((m) => m.trim()) || []
226
- });
227
- }
228
- }
229
- return items;
230
- }
231
- parseKnowledge(section) {
232
- const items = [];
233
- const blocks = section.split(/\n(?=- \*\*Topic\*\*:)/);
234
- for (const block of blocks) {
235
- if (!block.trim()) continue;
236
- const topicMatch = block.match(/\*\*Topic\*\*:\s*(.+)/);
237
- const detailsMatch = block.match(/\*\*Details\*\*:\s*(.+)/);
238
- if (topicMatch) {
239
- items.push({
240
- topic: topicMatch[1].trim(),
241
- details: detailsMatch?.[1].trim() || ""
242
- });
243
- }
244
- }
245
- return items;
246
- }
247
- async writeSummary(sessionId, content) {
248
- const conversationsPath = getConversationsPath(this.projectPath);
249
- const summaryPath = path.join(conversationsPath, sessionId, "summary.md");
250
- const fullContent = `# Session Summary: ${sessionId}
251
- Generated: ${(/* @__PURE__ */ new Date()).toISOString()}
252
-
253
- ${content}
254
- `;
255
- await writeTextFile(summaryPath, fullContent);
256
- }
257
- async cleanupConversationFiles(sessionId) {
258
- const config = await loadConfig(this.projectPath);
259
- const cleanupMode = config.compaction.cleanupAfterSummary;
260
- if (cleanupMode === "keep") {
261
- return;
262
- }
263
- const conversationsPath = getConversationsPath(this.projectPath);
264
- const sessionPath = path.join(conversationsPath, sessionId);
265
- const files = await fs.readdir(sessionPath);
266
- const conversationFiles = files.filter(
267
- (f) => /^\d+\.txt$/.test(f)
268
- );
269
- if (conversationFiles.length === 0) {
270
- return;
271
- }
272
- if (cleanupMode === "archive") {
273
- await this.archiveConversationFiles(sessionPath, conversationFiles);
274
- } else if (cleanupMode === "delete") {
275
- await this.deleteConversationFiles(sessionPath, conversationFiles);
276
- }
277
- }
278
- async archiveConversationFiles(sessionPath, files) {
279
- try {
280
- const contents = [];
281
- for (const file of files.sort()) {
282
- const filePath = path.join(sessionPath, file);
283
- const content = await readTextFile(filePath);
284
- if (content) {
285
- contents.push(`=== ${file} ===
286
- ${content}`);
287
- }
288
- }
289
- const combined = contents.join("\n\n");
290
- const compressed = await gzip2(Buffer.from(combined, "utf-8"));
291
- const archivePath = path.join(sessionPath, "raw.txt.gz");
292
- await fs.writeFile(archivePath, compressed);
293
- await this.deleteConversationFiles(sessionPath, files);
294
- } catch (error) {
295
- console.error("Failed to archive conversation files:", error);
296
- }
297
- }
298
- async deleteConversationFiles(sessionPath, files) {
299
- for (const file of files) {
300
- try {
301
- await fs.unlink(path.join(sessionPath, file));
302
- } catch {
303
- }
304
- }
305
- }
306
- };
307
-
308
- // src/core/entity-detector.ts
309
- var EntityDetector = class {
310
- /**
311
- * Analyzes a summary and determines what should be added to the vault.
312
- */
313
- detect(summary) {
314
- const items = [];
315
- for (const entity of summary.entities) {
316
- items.push(this.createEntityItem(entity));
317
- const relatedServices = summary.services.filter(
318
- (s) => s.name.toLowerCase().includes(entity.name.toLowerCase()) || s.location?.toLowerCase().includes(entity.name.toLowerCase())
319
- );
320
- for (const service of relatedServices) {
321
- items.push(this.createServiceItem(entity.name, service));
322
- }
323
- }
324
- const processedServiceNames = /* @__PURE__ */ new Set();
325
- for (const entity of summary.entities) {
326
- for (const service of summary.services) {
327
- if (service.name.toLowerCase().includes(entity.name.toLowerCase()) || service.location?.toLowerCase().includes(entity.name.toLowerCase())) {
328
- processedServiceNames.add(service.name);
329
- }
330
- }
331
- }
332
- for (const service of summary.services) {
333
- if (!processedServiceNames.has(service.name)) {
334
- items.push(this.createStandaloneServiceItem(service));
335
- }
336
- }
337
- for (const arch of summary.architecture) {
338
- items.push(this.createArchitectureItem(arch));
339
- }
340
- for (const knowledge of summary.knowledge) {
341
- items.push(this.createKnowledgeItem(knowledge));
342
- }
343
- return items;
344
- }
345
- createEntityItem(entity) {
346
- const content = this.formatEntityContent(entity);
347
- return {
348
- type: "entity",
349
- name: entity.name,
350
- data: entity,
351
- vaultPath: `entities/${entity.name.toLowerCase()}`,
352
- content
353
- };
354
- }
355
- createServiceItem(entityName, service) {
356
- const content = this.formatServiceContent(service);
357
- const serviceName = service.name.toLowerCase().replace(entityName.toLowerCase(), "").trim();
358
- const fileName = serviceName || "service";
359
- return {
360
- type: "service",
361
- name: service.name,
362
- data: service,
363
- vaultPath: `entities/${entityName.toLowerCase()}/services/${fileName}`,
364
- content
365
- };
366
- }
367
- createStandaloneServiceItem(service) {
368
- const content = this.formatServiceContent(service);
369
- return {
370
- type: "service",
371
- name: service.name,
372
- data: service,
373
- vaultPath: `services/${service.name.toLowerCase()}`,
374
- content
375
- };
376
- }
377
- createArchitectureItem(arch) {
378
- const content = `## ${arch.pattern}
379
-
380
- ${arch.description}
381
-
382
- ### Affected Files
383
- ${arch.affectedFiles.map((f) => `- ${f}`).join("\n")}
384
- `;
385
- return {
386
- type: "pattern",
387
- name: arch.pattern,
388
- data: arch,
389
- vaultPath: "architecture",
390
- content
391
- };
392
- }
393
- createKnowledgeItem(knowledge) {
394
- const content = `## ${knowledge.topic}
395
-
396
- ${knowledge.details}
397
- `;
398
- return {
399
- type: "knowledge",
400
- name: knowledge.topic,
401
- data: knowledge,
402
- vaultPath: "general-knowledge",
403
- content
404
- };
405
- }
406
- formatEntityContent(entity) {
407
- let content = `# ${entity.name}
408
-
409
- `;
410
- if (entity.location) {
411
- content += `**Location**: ${entity.location}
412
-
413
- `;
414
- }
415
- if (entity.attributes.length > 0) {
416
- content += `## Attributes
417
-
418
- `;
419
- for (const attr of entity.attributes) {
420
- content += `- ${attr}
421
- `;
422
- }
423
- content += "\n";
424
- }
425
- if (entity.relations.length > 0) {
426
- content += `## Relations
427
-
428
- `;
429
- for (const rel of entity.relations) {
430
- content += `- ${rel}
431
- `;
432
- }
433
- content += "\n";
434
- }
435
- return content;
436
- }
437
- formatServiceContent(service) {
438
- let content = `# ${service.name}
439
-
440
- `;
441
- if (service.location) {
442
- content += `**Location**: ${service.location}
443
-
444
- `;
445
- }
446
- if (service.purpose) {
447
- content += `## Purpose
448
-
449
- ${service.purpose}
450
-
451
- `;
452
- }
453
- if (service.methods.length > 0) {
454
- content += `## Methods
455
-
456
- `;
457
- for (const method of service.methods) {
458
- content += `- ${method}
459
- `;
460
- }
461
- content += "\n";
462
- }
463
- return content;
464
- }
465
- };
466
-
467
- // src/core/vault-integrator.ts
468
- import * as path2 from "path";
469
- var VaultIntegrator = class {
470
- vaultPath;
471
- indexManager;
472
- entityDetector;
473
- constructor(vaultPath) {
474
- this.vaultPath = vaultPath;
475
- this.indexManager = new IndexManager(vaultPath);
476
- this.entityDetector = new EntityDetector();
477
- }
478
- async integrate(summary) {
479
- const items = this.entityDetector.detect(summary);
480
- if (items.length === 0) {
481
- return;
482
- }
483
- for (const item of items) {
484
- await this.processItem(item);
485
- }
486
- await this.updateRootIndex();
487
- }
488
- async processItem(item) {
489
- switch (item.type) {
490
- case "entity":
491
- await this.processEntity(item);
492
- break;
493
- case "service":
494
- await this.processService(item);
495
- break;
496
- case "pattern":
497
- await this.processPattern(item);
498
- break;
499
- case "knowledge":
500
- await this.processKnowledge(item);
501
- break;
502
- }
503
- }
504
- async processEntity(item) {
505
- const entityPath = await this.indexManager.ensureEntityFolder(item.name);
506
- const fullPath = path2.join(this.vaultPath, entityPath);
507
- const attributesPath = path2.join(fullPath, "attributes.md");
508
- await writeTextFile(attributesPath, item.content);
509
- await this.indexManager.addEntry(entityPath, {
510
- name: "attributes",
511
- path: "./attributes.md",
512
- description: `Attributes of ${item.name}`,
513
- type: "file"
514
- });
515
- }
516
- async processService(item) {
517
- const pathParts = item.vaultPath.split("/");
518
- const fileName = pathParts.pop() + ".md";
519
- const folderPath = pathParts.join("/");
520
- const fullFolderPath = path2.join(this.vaultPath, folderPath);
521
- await ensureDir(fullFolderPath);
522
- const filePath = path2.join(fullFolderPath, fileName);
523
- await writeTextFile(filePath, item.content);
524
- const parentIndexPath = path2.join(fullFolderPath, "INDEX.md");
525
- if (!await fileExists(parentIndexPath)) {
526
- await this.indexManager.createIndex(folderPath, []);
527
- }
528
- await this.indexManager.addEntry(folderPath, {
529
- name: item.name,
530
- path: `./${fileName}`,
531
- description: item.data.purpose || `${item.name} service`,
532
- type: "file"
533
- });
534
- if (folderPath.startsWith("entities/") && folderPath.includes("/services")) {
535
- const entityFolder = folderPath.split("/").slice(0, 2).join("/");
536
- await this.indexManager.addEntry(entityFolder, {
537
- name: "services",
538
- path: "./services/INDEX.md",
539
- description: "Service layer documentation",
540
- type: "folder"
541
- });
542
- }
543
- }
544
- async processPattern(item) {
545
- const archPath = path2.join(this.vaultPath, "architecture.md");
546
- const existing = await readTextFile(archPath);
547
- if (!existing) {
548
- await writeTextFile(archPath, `# Architecture
549
-
550
- ${item.content}`);
551
- return;
552
- }
553
- if (existing.includes(`## ${item.name}`)) {
554
- const regex = new RegExp(`## ${item.name}[\\s\\S]*?(?=\\n## |$)`);
555
- const updated = existing.replace(regex, item.content);
556
- await writeTextFile(archPath, updated);
557
- } else {
558
- await appendToFile(archPath, `
559
- ${item.content}`);
560
- }
561
- }
562
- async processKnowledge(item) {
563
- const knowledgePath = path2.join(this.vaultPath, "general-knowledge.md");
564
- const existing = await readTextFile(knowledgePath);
565
- if (!existing) {
566
- await writeTextFile(knowledgePath, `# General Knowledge
567
-
568
- ${item.content}`);
569
- return;
570
- }
571
- if (existing.includes(`## ${item.name}`)) {
572
- const regex = new RegExp(`## ${item.name}[\\s\\S]*?(?=\\n## |$)`);
573
- const updated = existing.replace(regex, item.content);
574
- await writeTextFile(knowledgePath, updated);
575
- } else {
576
- await appendToFile(knowledgePath, `
577
- ${item.content}`);
578
- }
579
- }
580
- async updateRootIndex() {
581
- const indexPath = path2.join(this.vaultPath, "INDEX.md");
582
- const content = await readTextFile(indexPath);
583
- if (!content) {
584
- return;
585
- }
586
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
587
- const updated = content.replace(
588
- /## Last Updated[\s\S]*$/,
589
- `## Last Updated
590
-
591
- ${timestamp}`
592
- );
593
- await writeTextFile(indexPath, updated);
594
- }
595
- };
596
-
597
- export {
598
- CompactionEngine,
599
- EntityDetector,
600
- VaultIntegrator
601
- };
602
- //# sourceMappingURL=chunk-GLYS4OA4.js.map