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.
- package/README.md +40 -6
- package/dist/bin/cckb.js +34 -12
- package/dist/bin/cckb.js.map +1 -1
- package/dist/{chunk-NOCXYKIE.js → chunk-4IQV2TQE.js} +2 -2
- package/dist/chunk-C6NR36YP.js +276 -0
- package/dist/chunk-C6NR36YP.js.map +1 -0
- package/dist/{chunk-E2IJSPVB.js → chunk-FRETJBP5.js} +23 -1
- package/dist/chunk-FRETJBP5.js.map +1 -0
- package/dist/{chunk-XQXIDTUW.js → chunk-HP5YVMZ6.js} +2 -2
- package/dist/chunk-TB2GPCQP.js +358 -0
- package/dist/chunk-TB2GPCQP.js.map +1 -0
- package/dist/chunk-XOV27B2L.js +969 -0
- package/dist/chunk-XOV27B2L.js.map +1 -0
- package/dist/hooks/notification.js +2 -2
- package/dist/hooks/post-tool-use.js +2 -2
- package/dist/hooks/session-start.js +3 -3
- package/dist/hooks/stop.js +7 -5
- package/dist/hooks/stop.js.map +1 -1
- package/dist/hooks/user-prompt.js +2 -2
- package/dist/index.d.ts +106 -1
- package/dist/index.js +16 -6
- package/package.json +1 -1
- package/dist/chunk-3KGJEBKK.js +0 -207
- package/dist/chunk-3KGJEBKK.js.map +0 -1
- package/dist/chunk-E2IJSPVB.js.map +0 -1
- package/dist/chunk-GLYS4OA4.js +0 -602
- package/dist/chunk-GLYS4OA4.js.map +0 -1
- /package/dist/{chunk-NOCXYKIE.js.map → chunk-4IQV2TQE.js.map} +0 -0
- /package/dist/{chunk-XQXIDTUW.js.map → chunk-HP5YVMZ6.js.map} +0 -0
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
import {
|
|
2
|
+
IndexManager
|
|
3
|
+
} from "./chunk-4IQV2TQE.js";
|
|
4
|
+
import {
|
|
5
|
+
appendToFile,
|
|
6
|
+
ensureDir,
|
|
7
|
+
fileExists,
|
|
8
|
+
readTextFile,
|
|
9
|
+
writeTextFile
|
|
10
|
+
} from "./chunk-FRETJBP5.js";
|
|
11
|
+
|
|
12
|
+
// src/core/entity-detector.ts
|
|
13
|
+
var EntityDetector = class {
|
|
14
|
+
/**
|
|
15
|
+
* Analyzes a summary and determines what should be added to the vault.
|
|
16
|
+
*/
|
|
17
|
+
detect(summary) {
|
|
18
|
+
const items = [];
|
|
19
|
+
for (const entity of summary.entities) {
|
|
20
|
+
items.push(this.createEntityItem(entity));
|
|
21
|
+
const relatedServices = summary.services.filter(
|
|
22
|
+
(s) => s.name.toLowerCase().includes(entity.name.toLowerCase()) || s.location?.toLowerCase().includes(entity.name.toLowerCase())
|
|
23
|
+
);
|
|
24
|
+
for (const service of relatedServices) {
|
|
25
|
+
items.push(this.createServiceItem(entity.name, service));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const processedServiceNames = /* @__PURE__ */ new Set();
|
|
29
|
+
for (const entity of summary.entities) {
|
|
30
|
+
for (const service of summary.services) {
|
|
31
|
+
if (service.name.toLowerCase().includes(entity.name.toLowerCase()) || service.location?.toLowerCase().includes(entity.name.toLowerCase())) {
|
|
32
|
+
processedServiceNames.add(service.name);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
for (const service of summary.services) {
|
|
37
|
+
if (!processedServiceNames.has(service.name)) {
|
|
38
|
+
items.push(this.createStandaloneServiceItem(service));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
for (const arch of summary.architecture) {
|
|
42
|
+
items.push(this.createArchitectureItem(arch));
|
|
43
|
+
}
|
|
44
|
+
for (const knowledge of summary.knowledge) {
|
|
45
|
+
items.push(this.createKnowledgeItem(knowledge));
|
|
46
|
+
}
|
|
47
|
+
return items;
|
|
48
|
+
}
|
|
49
|
+
createEntityItem(entity) {
|
|
50
|
+
const content = this.formatEntityContent(entity);
|
|
51
|
+
return {
|
|
52
|
+
type: "entity",
|
|
53
|
+
name: entity.name,
|
|
54
|
+
data: entity,
|
|
55
|
+
vaultPath: `entities/${entity.name.toLowerCase()}`,
|
|
56
|
+
content
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
createServiceItem(entityName, service) {
|
|
60
|
+
const content = this.formatServiceContent(service);
|
|
61
|
+
const serviceName = service.name.toLowerCase().replace(entityName.toLowerCase(), "").trim();
|
|
62
|
+
const fileName = serviceName || "service";
|
|
63
|
+
return {
|
|
64
|
+
type: "service",
|
|
65
|
+
name: service.name,
|
|
66
|
+
data: service,
|
|
67
|
+
vaultPath: `entities/${entityName.toLowerCase()}/services/${fileName}`,
|
|
68
|
+
content
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
createStandaloneServiceItem(service) {
|
|
72
|
+
const content = this.formatServiceContent(service);
|
|
73
|
+
return {
|
|
74
|
+
type: "service",
|
|
75
|
+
name: service.name,
|
|
76
|
+
data: service,
|
|
77
|
+
vaultPath: `services/${service.name.toLowerCase()}`,
|
|
78
|
+
content
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
createArchitectureItem(arch) {
|
|
82
|
+
const content = `## ${arch.pattern}
|
|
83
|
+
|
|
84
|
+
${arch.description}
|
|
85
|
+
|
|
86
|
+
### Affected Files
|
|
87
|
+
${arch.affectedFiles.map((f) => `- ${f}`).join("\n")}
|
|
88
|
+
`;
|
|
89
|
+
return {
|
|
90
|
+
type: "pattern",
|
|
91
|
+
name: arch.pattern,
|
|
92
|
+
data: arch,
|
|
93
|
+
vaultPath: "architecture",
|
|
94
|
+
content
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
createKnowledgeItem(knowledge) {
|
|
98
|
+
const content = `## ${knowledge.topic}
|
|
99
|
+
|
|
100
|
+
${knowledge.details}
|
|
101
|
+
`;
|
|
102
|
+
return {
|
|
103
|
+
type: "knowledge",
|
|
104
|
+
name: knowledge.topic,
|
|
105
|
+
data: knowledge,
|
|
106
|
+
vaultPath: "general-knowledge",
|
|
107
|
+
content
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
formatEntityContent(entity) {
|
|
111
|
+
let content = `# ${entity.name}
|
|
112
|
+
|
|
113
|
+
`;
|
|
114
|
+
if (entity.location) {
|
|
115
|
+
content += `**Location**: ${entity.location}
|
|
116
|
+
|
|
117
|
+
`;
|
|
118
|
+
}
|
|
119
|
+
if (entity.attributes.length > 0) {
|
|
120
|
+
content += `## Attributes
|
|
121
|
+
|
|
122
|
+
`;
|
|
123
|
+
for (const attr of entity.attributes) {
|
|
124
|
+
content += `- ${attr}
|
|
125
|
+
`;
|
|
126
|
+
}
|
|
127
|
+
content += "\n";
|
|
128
|
+
}
|
|
129
|
+
if (entity.relations.length > 0) {
|
|
130
|
+
content += `## Relations
|
|
131
|
+
|
|
132
|
+
`;
|
|
133
|
+
for (const rel of entity.relations) {
|
|
134
|
+
content += `- ${rel}
|
|
135
|
+
`;
|
|
136
|
+
}
|
|
137
|
+
content += "\n";
|
|
138
|
+
}
|
|
139
|
+
return content;
|
|
140
|
+
}
|
|
141
|
+
formatServiceContent(service) {
|
|
142
|
+
let content = `# ${service.name}
|
|
143
|
+
|
|
144
|
+
`;
|
|
145
|
+
if (service.location) {
|
|
146
|
+
content += `**Location**: ${service.location}
|
|
147
|
+
|
|
148
|
+
`;
|
|
149
|
+
}
|
|
150
|
+
if (service.purpose) {
|
|
151
|
+
content += `## Purpose
|
|
152
|
+
|
|
153
|
+
${service.purpose}
|
|
154
|
+
|
|
155
|
+
`;
|
|
156
|
+
}
|
|
157
|
+
if (service.methods.length > 0) {
|
|
158
|
+
content += `## Methods
|
|
159
|
+
|
|
160
|
+
`;
|
|
161
|
+
for (const method of service.methods) {
|
|
162
|
+
content += `- ${method}
|
|
163
|
+
`;
|
|
164
|
+
}
|
|
165
|
+
content += "\n";
|
|
166
|
+
}
|
|
167
|
+
return content;
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// src/core/vault-integrator.ts
|
|
172
|
+
import * as path from "path";
|
|
173
|
+
var VaultIntegrator = class {
|
|
174
|
+
vaultPath;
|
|
175
|
+
indexManager;
|
|
176
|
+
entityDetector;
|
|
177
|
+
constructor(vaultPath) {
|
|
178
|
+
this.vaultPath = vaultPath;
|
|
179
|
+
this.indexManager = new IndexManager(vaultPath);
|
|
180
|
+
this.entityDetector = new EntityDetector();
|
|
181
|
+
}
|
|
182
|
+
async integrate(summary) {
|
|
183
|
+
const items = this.entityDetector.detect(summary);
|
|
184
|
+
if (items.length === 0) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
for (const item of items) {
|
|
188
|
+
await this.processItem(item);
|
|
189
|
+
}
|
|
190
|
+
await this.updateRootIndex();
|
|
191
|
+
}
|
|
192
|
+
async processItem(item) {
|
|
193
|
+
switch (item.type) {
|
|
194
|
+
case "entity":
|
|
195
|
+
await this.processEntity(item);
|
|
196
|
+
break;
|
|
197
|
+
case "service":
|
|
198
|
+
await this.processService(item);
|
|
199
|
+
break;
|
|
200
|
+
case "pattern":
|
|
201
|
+
await this.processPattern(item);
|
|
202
|
+
break;
|
|
203
|
+
case "knowledge":
|
|
204
|
+
await this.processKnowledge(item);
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
async processEntity(item) {
|
|
209
|
+
const entityPath = await this.indexManager.ensureEntityFolder(item.name);
|
|
210
|
+
const fullPath = path.join(this.vaultPath, entityPath);
|
|
211
|
+
const attributesPath = path.join(fullPath, "attributes.md");
|
|
212
|
+
await writeTextFile(attributesPath, item.content);
|
|
213
|
+
await this.indexManager.addEntry(entityPath, {
|
|
214
|
+
name: "attributes",
|
|
215
|
+
path: "./attributes.md",
|
|
216
|
+
description: `Attributes of ${item.name}`,
|
|
217
|
+
type: "file"
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
async processService(item) {
|
|
221
|
+
const pathParts = item.vaultPath.split("/");
|
|
222
|
+
const fileName = pathParts.pop() + ".md";
|
|
223
|
+
const folderPath = pathParts.join("/");
|
|
224
|
+
const fullFolderPath = path.join(this.vaultPath, folderPath);
|
|
225
|
+
await ensureDir(fullFolderPath);
|
|
226
|
+
const filePath = path.join(fullFolderPath, fileName);
|
|
227
|
+
await writeTextFile(filePath, item.content);
|
|
228
|
+
const parentIndexPath = path.join(fullFolderPath, "INDEX.md");
|
|
229
|
+
if (!await fileExists(parentIndexPath)) {
|
|
230
|
+
await this.indexManager.createIndex(folderPath, []);
|
|
231
|
+
}
|
|
232
|
+
await this.indexManager.addEntry(folderPath, {
|
|
233
|
+
name: item.name,
|
|
234
|
+
path: `./${fileName}`,
|
|
235
|
+
description: item.data.purpose || `${item.name} service`,
|
|
236
|
+
type: "file"
|
|
237
|
+
});
|
|
238
|
+
if (folderPath.startsWith("entities/") && folderPath.includes("/services")) {
|
|
239
|
+
const entityFolder = folderPath.split("/").slice(0, 2).join("/");
|
|
240
|
+
await this.indexManager.addEntry(entityFolder, {
|
|
241
|
+
name: "services",
|
|
242
|
+
path: "./services/INDEX.md",
|
|
243
|
+
description: "Service layer documentation",
|
|
244
|
+
type: "folder"
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
async processPattern(item) {
|
|
249
|
+
const archPath = path.join(this.vaultPath, "architecture.md");
|
|
250
|
+
const existing = await readTextFile(archPath);
|
|
251
|
+
if (!existing) {
|
|
252
|
+
await writeTextFile(archPath, `# Architecture
|
|
253
|
+
|
|
254
|
+
${item.content}`);
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
if (existing.includes(`## ${item.name}`)) {
|
|
258
|
+
const regex = new RegExp(`## ${item.name}[\\s\\S]*?(?=\\n## |$)`);
|
|
259
|
+
const updated = existing.replace(regex, item.content);
|
|
260
|
+
await writeTextFile(archPath, updated);
|
|
261
|
+
} else {
|
|
262
|
+
await appendToFile(archPath, `
|
|
263
|
+
${item.content}`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
async processKnowledge(item) {
|
|
267
|
+
const knowledgePath = path.join(this.vaultPath, "general-knowledge.md");
|
|
268
|
+
const existing = await readTextFile(knowledgePath);
|
|
269
|
+
if (!existing) {
|
|
270
|
+
await writeTextFile(knowledgePath, `# General Knowledge
|
|
271
|
+
|
|
272
|
+
${item.content}`);
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
if (existing.includes(`## ${item.name}`)) {
|
|
276
|
+
const regex = new RegExp(`## ${item.name}[\\s\\S]*?(?=\\n## |$)`);
|
|
277
|
+
const updated = existing.replace(regex, item.content);
|
|
278
|
+
await writeTextFile(knowledgePath, updated);
|
|
279
|
+
} else {
|
|
280
|
+
await appendToFile(knowledgePath, `
|
|
281
|
+
${item.content}`);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
async updateRootIndex() {
|
|
285
|
+
const indexPath = path.join(this.vaultPath, "INDEX.md");
|
|
286
|
+
const content = await readTextFile(indexPath);
|
|
287
|
+
if (!content) {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
291
|
+
const updated = content.replace(
|
|
292
|
+
/## Last Updated[\s\S]*$/,
|
|
293
|
+
`## Last Updated
|
|
294
|
+
|
|
295
|
+
${timestamp}`
|
|
296
|
+
);
|
|
297
|
+
await writeTextFile(indexPath, updated);
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
// src/utils/claude-sdk.ts
|
|
302
|
+
import { spawn } from "child_process";
|
|
303
|
+
async function spawnClaudeAgent(prompt, options) {
|
|
304
|
+
const timeout = options?.timeout ?? 3e5;
|
|
305
|
+
return new Promise((resolve, reject) => {
|
|
306
|
+
const child = spawn("claude", ["--print", "-p", prompt], {
|
|
307
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
308
|
+
});
|
|
309
|
+
let stdout = "";
|
|
310
|
+
let stderr = "";
|
|
311
|
+
let timedOut = false;
|
|
312
|
+
const timeoutId = setTimeout(() => {
|
|
313
|
+
timedOut = true;
|
|
314
|
+
child.kill("SIGTERM");
|
|
315
|
+
reject(new Error(`Claude agent timed out after ${timeout / 1e3}s`));
|
|
316
|
+
}, timeout);
|
|
317
|
+
child.stdout?.on("data", (data) => {
|
|
318
|
+
stdout += data.toString();
|
|
319
|
+
});
|
|
320
|
+
child.stderr?.on("data", (data) => {
|
|
321
|
+
stderr += data.toString();
|
|
322
|
+
});
|
|
323
|
+
child.on("close", (code) => {
|
|
324
|
+
clearTimeout(timeoutId);
|
|
325
|
+
if (timedOut) return;
|
|
326
|
+
if (code === 0) {
|
|
327
|
+
resolve(stdout.trim());
|
|
328
|
+
} else {
|
|
329
|
+
reject(new Error(`Claude agent failed with code ${code}: ${stderr || "(no stderr)"}`));
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
child.on("error", (error) => {
|
|
333
|
+
clearTimeout(timeoutId);
|
|
334
|
+
reject(error);
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
async function isClaudeAvailable() {
|
|
339
|
+
return new Promise((resolve) => {
|
|
340
|
+
const child = spawn("claude", ["--version"], {
|
|
341
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
342
|
+
});
|
|
343
|
+
child.on("close", (code) => {
|
|
344
|
+
resolve(code === 0);
|
|
345
|
+
});
|
|
346
|
+
child.on("error", () => {
|
|
347
|
+
resolve(false);
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
export {
|
|
353
|
+
EntityDetector,
|
|
354
|
+
VaultIntegrator,
|
|
355
|
+
spawnClaudeAgent,
|
|
356
|
+
isClaudeAvailable
|
|
357
|
+
};
|
|
358
|
+
//# sourceMappingURL=chunk-TB2GPCQP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/entity-detector.ts","../src/core/vault-integrator.ts","../src/utils/claude-sdk.ts"],"sourcesContent":["import type { Summary, ExtractedEntity, ServiceItem } from \"./compaction-engine.js\";\n\nexport interface DetectedItem {\n type: \"entity\" | \"service\" | \"pattern\" | \"knowledge\";\n name: string;\n data: unknown;\n vaultPath: string;\n content: string;\n}\n\nexport class EntityDetector {\n /**\n * Analyzes a summary and determines what should be added to the vault.\n */\n detect(summary: Summary): DetectedItem[] {\n const items: DetectedItem[] = [];\n\n // Detect entities\n for (const entity of summary.entities) {\n items.push(this.createEntityItem(entity));\n\n // Also create service entries for entity-related services\n const relatedServices = summary.services.filter(\n (s) =>\n s.name.toLowerCase().includes(entity.name.toLowerCase()) ||\n s.location?.toLowerCase().includes(entity.name.toLowerCase())\n );\n\n for (const service of relatedServices) {\n items.push(this.createServiceItem(entity.name, service));\n }\n }\n\n // Detect standalone services\n const processedServiceNames = new Set<string>();\n for (const entity of summary.entities) {\n for (const service of summary.services) {\n if (\n service.name.toLowerCase().includes(entity.name.toLowerCase()) ||\n service.location?.toLowerCase().includes(entity.name.toLowerCase())\n ) {\n processedServiceNames.add(service.name);\n }\n }\n }\n\n for (const service of summary.services) {\n if (!processedServiceNames.has(service.name)) {\n items.push(this.createStandaloneServiceItem(service));\n }\n }\n\n // Detect architecture patterns\n for (const arch of summary.architecture) {\n items.push(this.createArchitectureItem(arch));\n }\n\n // Detect general knowledge\n for (const knowledge of summary.knowledge) {\n items.push(this.createKnowledgeItem(knowledge));\n }\n\n return items;\n }\n\n private createEntityItem(entity: ExtractedEntity): DetectedItem {\n const content = this.formatEntityContent(entity);\n\n return {\n type: \"entity\",\n name: entity.name,\n data: entity,\n vaultPath: `entities/${entity.name.toLowerCase()}`,\n content,\n };\n }\n\n private createServiceItem(\n entityName: string,\n service: ServiceItem\n ): DetectedItem {\n const content = this.formatServiceContent(service);\n const serviceName = service.name.toLowerCase().replace(entityName.toLowerCase(), \"\").trim();\n const fileName = serviceName || \"service\";\n\n return {\n type: \"service\",\n name: service.name,\n data: service,\n vaultPath: `entities/${entityName.toLowerCase()}/services/${fileName}`,\n content,\n };\n }\n\n private createStandaloneServiceItem(service: ServiceItem): DetectedItem {\n const content = this.formatServiceContent(service);\n\n return {\n type: \"service\",\n name: service.name,\n data: service,\n vaultPath: `services/${service.name.toLowerCase()}`,\n content,\n };\n }\n\n private createArchitectureItem(arch: {\n pattern: string;\n description: string;\n affectedFiles: string[];\n }): DetectedItem {\n const content = `## ${arch.pattern}\n\n${arch.description}\n\n### Affected Files\n${arch.affectedFiles.map((f) => `- ${f}`).join(\"\\n\")}\n`;\n\n return {\n type: \"pattern\",\n name: arch.pattern,\n data: arch,\n vaultPath: \"architecture\",\n content,\n };\n }\n\n private createKnowledgeItem(knowledge: {\n topic: string;\n details: string;\n }): DetectedItem {\n const content = `## ${knowledge.topic}\n\n${knowledge.details}\n`;\n\n return {\n type: \"knowledge\",\n name: knowledge.topic,\n data: knowledge,\n vaultPath: \"general-knowledge\",\n content,\n };\n }\n\n private formatEntityContent(entity: ExtractedEntity): string {\n let content = `# ${entity.name}\\n\\n`;\n\n if (entity.location) {\n content += `**Location**: ${entity.location}\\n\\n`;\n }\n\n if (entity.attributes.length > 0) {\n content += `## Attributes\\n\\n`;\n for (const attr of entity.attributes) {\n content += `- ${attr}\\n`;\n }\n content += \"\\n\";\n }\n\n if (entity.relations.length > 0) {\n content += `## Relations\\n\\n`;\n for (const rel of entity.relations) {\n content += `- ${rel}\\n`;\n }\n content += \"\\n\";\n }\n\n return content;\n }\n\n private formatServiceContent(service: ServiceItem): string {\n let content = `# ${service.name}\\n\\n`;\n\n if (service.location) {\n content += `**Location**: ${service.location}\\n\\n`;\n }\n\n if (service.purpose) {\n content += `## Purpose\\n\\n${service.purpose}\\n\\n`;\n }\n\n if (service.methods.length > 0) {\n content += `## Methods\\n\\n`;\n for (const method of service.methods) {\n content += `- ${method}\\n`;\n }\n content += \"\\n\";\n }\n\n return content;\n }\n}\n","import * as path from \"node:path\";\nimport {\n readTextFile,\n writeTextFile,\n appendToFile,\n ensureDir,\n fileExists,\n} from \"../utils/file-utils.js\";\nimport { IndexManager } from \"./index-manager.js\";\nimport { EntityDetector, type DetectedItem } from \"./entity-detector.js\";\nimport type { Summary } from \"./compaction-engine.js\";\n\nexport class VaultIntegrator {\n private vaultPath: string;\n private indexManager: IndexManager;\n private entityDetector: EntityDetector;\n\n constructor(vaultPath: string) {\n this.vaultPath = vaultPath;\n this.indexManager = new IndexManager(vaultPath);\n this.entityDetector = new EntityDetector();\n }\n\n async integrate(summary: Summary): Promise<void> {\n // Detect items from summary\n const items = this.entityDetector.detect(summary);\n\n if (items.length === 0) {\n return;\n }\n\n // Process each detected item\n for (const item of items) {\n await this.processItem(item);\n }\n\n // Update root INDEX.md with timestamp\n await this.updateRootIndex();\n }\n\n private async processItem(item: DetectedItem): Promise<void> {\n switch (item.type) {\n case \"entity\":\n await this.processEntity(item);\n break;\n case \"service\":\n await this.processService(item);\n break;\n case \"pattern\":\n await this.processPattern(item);\n break;\n case \"knowledge\":\n await this.processKnowledge(item);\n break;\n }\n }\n\n private async processEntity(item: DetectedItem): Promise<void> {\n const entityPath = await this.indexManager.ensureEntityFolder(item.name);\n const fullPath = path.join(this.vaultPath, entityPath);\n\n // Write attributes.md\n const attributesPath = path.join(fullPath, \"attributes.md\");\n await writeTextFile(attributesPath, item.content);\n\n // Update entity INDEX.md\n await this.indexManager.addEntry(entityPath, {\n name: \"attributes\",\n path: \"./attributes.md\",\n description: `Attributes of ${item.name}`,\n type: \"file\",\n });\n }\n\n private async processService(item: DetectedItem): Promise<void> {\n const pathParts = item.vaultPath.split(\"/\");\n const fileName = pathParts.pop() + \".md\";\n const folderPath = pathParts.join(\"/\");\n const fullFolderPath = path.join(this.vaultPath, folderPath);\n\n await ensureDir(fullFolderPath);\n\n // Write service file\n const filePath = path.join(fullFolderPath, fileName);\n await writeTextFile(filePath, item.content);\n\n // Ensure parent has INDEX.md\n const parentIndexPath = path.join(fullFolderPath, \"INDEX.md\");\n if (!(await fileExists(parentIndexPath))) {\n await this.indexManager.createIndex(folderPath, []);\n }\n\n // Update parent INDEX.md\n await this.indexManager.addEntry(folderPath, {\n name: item.name,\n path: `./${fileName}`,\n description: (item.data as { purpose?: string }).purpose || `${item.name} service`,\n type: \"file\",\n });\n\n // Update entity INDEX if this is an entity service\n if (folderPath.startsWith(\"entities/\") && folderPath.includes(\"/services\")) {\n const entityFolder = folderPath.split(\"/\").slice(0, 2).join(\"/\");\n await this.indexManager.addEntry(entityFolder, {\n name: \"services\",\n path: \"./services/INDEX.md\",\n description: \"Service layer documentation\",\n type: \"folder\",\n });\n }\n }\n\n private async processPattern(item: DetectedItem): Promise<void> {\n const archPath = path.join(this.vaultPath, \"architecture.md\");\n const existing = await readTextFile(archPath);\n\n if (!existing) {\n await writeTextFile(archPath, `# Architecture\\n\\n${item.content}`);\n return;\n }\n\n // Check if pattern already exists\n if (existing.includes(`## ${item.name}`)) {\n // Replace existing section\n const regex = new RegExp(`## ${item.name}[\\\\s\\\\S]*?(?=\\\\n## |$)`);\n const updated = existing.replace(regex, item.content);\n await writeTextFile(archPath, updated);\n } else {\n // Append new pattern\n await appendToFile(archPath, `\\n${item.content}`);\n }\n }\n\n private async processKnowledge(item: DetectedItem): Promise<void> {\n const knowledgePath = path.join(this.vaultPath, \"general-knowledge.md\");\n const existing = await readTextFile(knowledgePath);\n\n if (!existing) {\n await writeTextFile(knowledgePath, `# General Knowledge\\n\\n${item.content}`);\n return;\n }\n\n // Check if topic already exists\n if (existing.includes(`## ${item.name}`)) {\n // Replace existing section\n const regex = new RegExp(`## ${item.name}[\\\\s\\\\S]*?(?=\\\\n## |$)`);\n const updated = existing.replace(regex, item.content);\n await writeTextFile(knowledgePath, updated);\n } else {\n // Append new knowledge\n await appendToFile(knowledgePath, `\\n${item.content}`);\n }\n }\n\n private async updateRootIndex(): Promise<void> {\n const indexPath = path.join(this.vaultPath, \"INDEX.md\");\n const content = await readTextFile(indexPath);\n\n if (!content) {\n return;\n }\n\n // Update the \"Last Updated\" line\n const timestamp = new Date().toISOString();\n const updated = content.replace(\n /## Last Updated[\\s\\S]*$/,\n `## Last Updated\\n\\n${timestamp}`\n );\n\n await writeTextFile(indexPath, updated);\n }\n}\n","import { spawn } from \"node:child_process\";\n\nexport interface ClaudeAgentOptions {\n timeout?: number; // milliseconds, default 5 minutes\n}\n\n/**\n * Spawns a Claude Code subagent to process a prompt.\n * Uses the Claude Code CLI with the --print flag to get output.\n */\nexport async function spawnClaudeAgent(\n prompt: string,\n options?: ClaudeAgentOptions\n): Promise<string> {\n const timeout = options?.timeout ?? 300000; // 5 minutes default\n\n return new Promise((resolve, reject) => {\n // Use claude CLI with --print to get output without interactive mode\n const child = spawn(\"claude\", [\"--print\", \"-p\", prompt], {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n\n let stdout = \"\";\n let stderr = \"\";\n let timedOut = false;\n\n // Manual timeout since spawn timeout doesn't always work\n const timeoutId = setTimeout(() => {\n timedOut = true;\n child.kill(\"SIGTERM\");\n reject(new Error(`Claude agent timed out after ${timeout / 1000}s`));\n }, timeout);\n\n child.stdout?.on(\"data\", (data) => {\n stdout += data.toString();\n });\n\n child.stderr?.on(\"data\", (data) => {\n stderr += data.toString();\n });\n\n child.on(\"close\", (code) => {\n clearTimeout(timeoutId);\n if (timedOut) return; // Already rejected\n\n if (code === 0) {\n resolve(stdout.trim());\n } else {\n reject(new Error(`Claude agent failed with code ${code}: ${stderr || \"(no stderr)\"}`));\n }\n });\n\n child.on(\"error\", (error) => {\n clearTimeout(timeoutId);\n reject(error);\n });\n });\n}\n\n/**\n * Checks if Claude CLI is available.\n */\nexport async function isClaudeAvailable(): Promise<boolean> {\n return new Promise((resolve) => {\n const child = spawn(\"claude\", [\"--version\"], {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n\n child.on(\"close\", (code) => {\n resolve(code === 0);\n });\n\n child.on(\"error\", () => {\n resolve(false);\n });\n });\n}\n"],"mappings":"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","names":[]}
|