@weavelogic/knowledge-graph-agent 0.8.8 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,792 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, appendFileSync } from "fs";
2
+ import { join, dirname } from "path";
3
+ import { createSessionId, createConversationId, createMessageId, createToolCallId, createSubAgentId, createSwarmId } from "./types.js";
4
+ const DEFAULT_CAPTURE_CONFIG = {
5
+ storageDir: ".kg/claude",
6
+ useKnowledgeGraph: true,
7
+ createMarkdown: true,
8
+ separateToolOutputs: true,
9
+ maxContentLength: 5e4,
10
+ captureSubAgents: true,
11
+ captureSwarms: true,
12
+ captureWorkflows: true,
13
+ defaultTags: ["claude", "interaction"]
14
+ };
15
+ class HookCaptureSystem {
16
+ config;
17
+ state;
18
+ projectRoot;
19
+ constructor(projectRoot, config = {}) {
20
+ this.projectRoot = projectRoot;
21
+ this.config = { ...DEFAULT_CAPTURE_CONFIG, ...config };
22
+ this.state = {
23
+ currentSessionId: null,
24
+ currentConversationId: null,
25
+ pendingToolCalls: /* @__PURE__ */ new Map(),
26
+ subAgentStack: [],
27
+ activeSwarmId: null,
28
+ storage: null
29
+ };
30
+ this.ensureStorageDir();
31
+ }
32
+ /**
33
+ * Ensure storage directory exists
34
+ */
35
+ ensureStorageDir() {
36
+ const fullPath = join(this.projectRoot, this.config.storageDir);
37
+ if (!existsSync(fullPath)) {
38
+ mkdirSync(fullPath, { recursive: true });
39
+ }
40
+ }
41
+ /**
42
+ * Create empty token usage
43
+ */
44
+ createEmptyTokenUsage() {
45
+ return {
46
+ inputTokens: 0,
47
+ outputTokens: 0,
48
+ totalTokens: 0
49
+ };
50
+ }
51
+ /**
52
+ * Create empty aggregated token usage
53
+ */
54
+ createEmptyAggregatedTokenUsage() {
55
+ return {
56
+ inputTokens: 0,
57
+ outputTokens: 0,
58
+ totalTokens: 0,
59
+ operationCount: 0
60
+ };
61
+ }
62
+ /**
63
+ * Create base metadata
64
+ */
65
+ createBaseMetadata(tags = []) {
66
+ const now = /* @__PURE__ */ new Date();
67
+ return {
68
+ createdAt: now,
69
+ updatedAt: now,
70
+ tags: [...this.config.defaultTags, ...tags]
71
+ };
72
+ }
73
+ /**
74
+ * Get current git branch
75
+ */
76
+ getGitBranch() {
77
+ try {
78
+ const headPath = join(this.projectRoot, ".git", "HEAD");
79
+ if (existsSync(headPath)) {
80
+ const content = readFileSync(headPath, "utf-8").trim();
81
+ if (content.startsWith("ref: refs/heads/")) {
82
+ return content.replace("ref: refs/heads/", "");
83
+ }
84
+ }
85
+ } catch {
86
+ }
87
+ return void 0;
88
+ }
89
+ /**
90
+ * Start a new session
91
+ */
92
+ startSession(name, purpose) {
93
+ const sessionId = createSessionId();
94
+ const now = /* @__PURE__ */ new Date();
95
+ const environment = {
96
+ workingDirectory: this.projectRoot,
97
+ gitBranch: this.getGitBranch(),
98
+ platform: process.platform
99
+ };
100
+ const session = {
101
+ id: sessionId,
102
+ name: name || `Session ${now.toISOString().slice(0, 16)}`,
103
+ purpose: purpose || "General interaction",
104
+ startedAt: now,
105
+ status: "running",
106
+ conversationIds: [],
107
+ swarmIds: [],
108
+ workflowIds: [],
109
+ environment,
110
+ tokenUsage: this.createEmptyAggregatedTokenUsage(),
111
+ metadata: this.createBaseMetadata(["session"])
112
+ };
113
+ this.state.storage = {
114
+ session,
115
+ conversations: /* @__PURE__ */ new Map(),
116
+ messages: /* @__PURE__ */ new Map(),
117
+ toolCalls: /* @__PURE__ */ new Map(),
118
+ subAgents: /* @__PURE__ */ new Map(),
119
+ swarms: /* @__PURE__ */ new Map()
120
+ };
121
+ this.state.currentSessionId = sessionId;
122
+ this.saveSession(session);
123
+ return session;
124
+ }
125
+ /**
126
+ * End the current session
127
+ */
128
+ endSession() {
129
+ if (!this.state.storage) return null;
130
+ const session = this.state.storage.session;
131
+ session.endedAt = /* @__PURE__ */ new Date();
132
+ session.status = "completed";
133
+ session.metadata.updatedAt = /* @__PURE__ */ new Date();
134
+ this.aggregateSessionTokens();
135
+ this.saveSession(session);
136
+ const result = { ...session };
137
+ this.state.currentSessionId = null;
138
+ this.state.currentConversationId = null;
139
+ this.state.storage = null;
140
+ return result;
141
+ }
142
+ /**
143
+ * Start a new conversation
144
+ */
145
+ startConversation(model, systemPrompt) {
146
+ if (!this.state.storage) {
147
+ this.startSession();
148
+ }
149
+ const conversationId = createConversationId();
150
+ const now = /* @__PURE__ */ new Date();
151
+ const conversation = {
152
+ id: conversationId,
153
+ sessionId: this.state.storage.session.id,
154
+ model: model || "claude-opus-4-5-20251101",
155
+ systemPrompt,
156
+ messageIds: [],
157
+ subAgentIds: [],
158
+ status: "running",
159
+ startedAt: now,
160
+ tokenUsage: this.createEmptyTokenUsage(),
161
+ metadata: this.createBaseMetadata(["conversation"])
162
+ };
163
+ this.state.storage.conversations.set(conversationId, conversation);
164
+ this.state.storage.session.conversationIds.push(conversationId);
165
+ this.state.currentConversationId = conversationId;
166
+ return conversation;
167
+ }
168
+ /**
169
+ * Handle hook events
170
+ */
171
+ handleHookEvent(event) {
172
+ switch (event.event) {
173
+ case "UserPromptSubmit":
174
+ this.handleUserPrompt(event);
175
+ break;
176
+ case "PreToolUse":
177
+ this.handlePreToolUse(event);
178
+ break;
179
+ case "PostToolUse":
180
+ this.handlePostToolUse(event);
181
+ break;
182
+ case "Stop":
183
+ this.handleSessionStop(event);
184
+ break;
185
+ case "PreCompact":
186
+ this.handlePreCompact(event);
187
+ break;
188
+ }
189
+ }
190
+ /**
191
+ * Handle user prompt submission
192
+ */
193
+ handleUserPrompt(event) {
194
+ if (!this.state.currentConversationId || !this.state.storage) {
195
+ this.startConversation();
196
+ }
197
+ const messageId = createMessageId();
198
+ const now = /* @__PURE__ */ new Date();
199
+ const conversationId = this.state.currentConversationId;
200
+ const textBlock = {
201
+ type: "text",
202
+ text: this.truncateContent(event.userPrompt || "")
203
+ };
204
+ const message = {
205
+ id: messageId,
206
+ conversationId,
207
+ role: "user",
208
+ content: textBlock.text,
209
+ contentBlocks: [textBlock],
210
+ toolCallIds: [],
211
+ timestamp: now,
212
+ tokenUsage: this.createEmptyTokenUsage(),
213
+ metadata: this.createBaseMetadata(["prompt", "user"])
214
+ };
215
+ this.state.storage.messages.set(messageId, message);
216
+ const conversation = this.state.storage.conversations.get(conversationId);
217
+ if (conversation) {
218
+ conversation.messageIds.push(messageId);
219
+ }
220
+ this.saveMessage(message);
221
+ }
222
+ /**
223
+ * Handle pre-tool use event
224
+ */
225
+ handlePreToolUse(event) {
226
+ if (!this.state.currentConversationId || !this.state.storage) {
227
+ this.startConversation();
228
+ }
229
+ const toolCallId = createToolCallId();
230
+ const now = /* @__PURE__ */ new Date();
231
+ const conversationId = this.state.currentConversationId;
232
+ let lastMessageId;
233
+ const conversation = this.state.storage.conversations.get(conversationId);
234
+ if (conversation && conversation.messageIds.length > 0) {
235
+ lastMessageId = conversation.messageIds[conversation.messageIds.length - 1];
236
+ const lastMessage = this.state.storage.messages.get(lastMessageId);
237
+ if (!lastMessage || lastMessage.role === "user") {
238
+ const assistantMsgId = createMessageId();
239
+ const assistantMessage = {
240
+ id: assistantMsgId,
241
+ conversationId,
242
+ role: "assistant",
243
+ content: "",
244
+ contentBlocks: [],
245
+ toolCallIds: [],
246
+ timestamp: now,
247
+ tokenUsage: this.createEmptyTokenUsage(),
248
+ metadata: this.createBaseMetadata(["response", "assistant"])
249
+ };
250
+ this.state.storage.messages.set(assistantMsgId, assistantMessage);
251
+ conversation.messageIds.push(assistantMsgId);
252
+ lastMessageId = assistantMsgId;
253
+ }
254
+ } else {
255
+ const assistantMsgId = createMessageId();
256
+ const assistantMessage = {
257
+ id: assistantMsgId,
258
+ conversationId,
259
+ role: "assistant",
260
+ content: "",
261
+ contentBlocks: [],
262
+ toolCallIds: [],
263
+ timestamp: now,
264
+ tokenUsage: this.createEmptyTokenUsage(),
265
+ metadata: this.createBaseMetadata(["response", "assistant"])
266
+ };
267
+ this.state.storage.messages.set(assistantMsgId, assistantMessage);
268
+ if (conversation) {
269
+ conversation.messageIds.push(assistantMsgId);
270
+ }
271
+ lastMessageId = assistantMsgId;
272
+ }
273
+ const toolCall = {
274
+ id: toolCallId,
275
+ messageId: lastMessageId,
276
+ toolName: event.toolName || "unknown",
277
+ toolCategory: this.categorizeToolCall(event.toolName || ""),
278
+ input: event.toolInput || {},
279
+ status: "running",
280
+ startedAt: now,
281
+ metadata: this.createBaseMetadata(["tool-call", event.toolName || "unknown"])
282
+ };
283
+ this.state.storage.toolCalls.set(toolCallId, toolCall);
284
+ const message = this.state.storage.messages.get(lastMessageId);
285
+ if (message) {
286
+ message.toolCallIds = message.toolCallIds || [];
287
+ message.toolCallIds.push(toolCallId);
288
+ }
289
+ this.state.pendingToolCalls.set(event.toolName || "unknown", toolCallId);
290
+ if (event.toolName === "Task" && this.config.captureSubAgents) {
291
+ this.handleSubAgentSpawn(event, toolCall);
292
+ }
293
+ if (event.toolName?.includes("swarm") && this.config.captureSwarms) {
294
+ this.handleSwarmOperation(event, toolCall);
295
+ }
296
+ }
297
+ /**
298
+ * Handle post-tool use event
299
+ */
300
+ handlePostToolUse(event) {
301
+ if (!this.state.storage) return;
302
+ const now = /* @__PURE__ */ new Date();
303
+ const toolCallId = this.state.pendingToolCalls.get(event.toolName || "unknown");
304
+ if (!toolCallId) return;
305
+ this.state.pendingToolCalls.delete(event.toolName || "unknown");
306
+ const toolCall = this.state.storage.toolCalls.get(toolCallId);
307
+ if (!toolCall) return;
308
+ toolCall.output = this.truncateContent(event.toolOutput || "");
309
+ toolCall.completedAt = now;
310
+ toolCall.status = event.error ? "failed" : "completed";
311
+ toolCall.executionTimeMs = event.duration;
312
+ if (event.error) {
313
+ toolCall.error = {
314
+ type: "execution_error",
315
+ message: event.error
316
+ };
317
+ }
318
+ toolCall.metadata.updatedAt = now;
319
+ if (toolCall.toolCategory === "file") {
320
+ const input = toolCall.input;
321
+ const filePath = input.file_path || input.path;
322
+ if (filePath) {
323
+ toolCall.affectedFiles = [String(filePath)];
324
+ }
325
+ }
326
+ if (event.toolName === "Task" && this.state.subAgentStack.length > 0) {
327
+ this.handleSubAgentComplete(event);
328
+ }
329
+ this.saveToolCall(toolCall);
330
+ }
331
+ /**
332
+ * Handle session stop event
333
+ */
334
+ handleSessionStop(_event) {
335
+ this.endSession();
336
+ }
337
+ /**
338
+ * Handle pre-compact event
339
+ */
340
+ handlePreCompact(_event) {
341
+ if (this.state.storage) {
342
+ this.state.storage.session.metadata.custom = {
343
+ ...this.state.storage.session.metadata.custom,
344
+ lastCompaction: (/* @__PURE__ */ new Date()).toISOString()
345
+ };
346
+ this.saveSession(this.state.storage.session);
347
+ }
348
+ }
349
+ /**
350
+ * Handle sub-agent spawn
351
+ */
352
+ handleSubAgentSpawn(event, toolCall) {
353
+ if (!this.state.storage || !this.state.currentConversationId) return;
354
+ const subAgentId = createSubAgentId();
355
+ const now = /* @__PURE__ */ new Date();
356
+ const input = event.toolInput;
357
+ const parentConversationId = this.state.currentConversationId;
358
+ const parentMessageId = toolCall.messageId;
359
+ const subAgent = {
360
+ id: subAgentId,
361
+ parentConversationId,
362
+ parentMessageId,
363
+ toolCallId: toolCall.id,
364
+ agentType: input.subagent_type || "custom",
365
+ name: String(input.description || "Sub-agent"),
366
+ task: this.truncateContent(String(input.prompt || input.description || "Unknown task")),
367
+ model: input.model || "claude-sonnet-4-20250514",
368
+ status: "running",
369
+ startedAt: now,
370
+ tokenUsage: this.createEmptyTokenUsage(),
371
+ metadata: this.createBaseMetadata(["sub-agent", String(input.subagent_type || "custom")])
372
+ };
373
+ this.state.storage.subAgents.set(subAgentId, subAgent);
374
+ this.state.subAgentStack.push(subAgentId);
375
+ const conversation = this.state.storage.conversations.get(parentConversationId);
376
+ if (conversation) {
377
+ conversation.subAgentIds = conversation.subAgentIds || [];
378
+ conversation.subAgentIds.push(subAgentId);
379
+ }
380
+ }
381
+ /**
382
+ * Handle sub-agent completion
383
+ */
384
+ handleSubAgentComplete(event) {
385
+ if (!this.state.storage) return;
386
+ const subAgentId = this.state.subAgentStack.pop();
387
+ if (!subAgentId) return;
388
+ const subAgent = this.state.storage.subAgents.get(subAgentId);
389
+ if (!subAgent) return;
390
+ const now = /* @__PURE__ */ new Date();
391
+ subAgent.completedAt = now;
392
+ subAgent.status = event.error ? "failed" : "completed";
393
+ subAgent.result = {
394
+ success: !event.error,
395
+ summary: this.truncateContent(event.toolOutput || "").slice(0, 500),
396
+ error: event.error
397
+ };
398
+ subAgent.metadata.updatedAt = now;
399
+ this.saveSubAgent(subAgent);
400
+ }
401
+ /**
402
+ * Handle swarm operation
403
+ */
404
+ handleSwarmOperation(event, _toolCall) {
405
+ if (!this.state.storage) return;
406
+ const input = event.toolInput;
407
+ if (event.toolName?.includes("swarm_init")) {
408
+ const swarmId = createSwarmId();
409
+ const now = /* @__PURE__ */ new Date();
410
+ const swarm = {
411
+ id: swarmId,
412
+ sessionId: this.state.storage.session.id,
413
+ name: String(input.name || "Swarm"),
414
+ topology: input.topology || "mesh",
415
+ strategy: input.strategy || "adaptive",
416
+ maxAgents: input.maxAgents || 8,
417
+ agentIds: [],
418
+ task: String(input.task || "Swarm task"),
419
+ status: "running",
420
+ startedAt: now,
421
+ tokenUsage: this.createEmptyAggregatedTokenUsage(),
422
+ metadata: this.createBaseMetadata(["swarm"])
423
+ };
424
+ this.state.storage.swarms.set(swarmId, swarm);
425
+ this.state.storage.session.swarmIds = this.state.storage.session.swarmIds || [];
426
+ this.state.storage.session.swarmIds.push(swarmId);
427
+ this.state.activeSwarmId = swarmId;
428
+ }
429
+ }
430
+ /**
431
+ * Categorize tool calls
432
+ */
433
+ categorizeToolCall(toolName) {
434
+ const categories = {
435
+ Read: "file",
436
+ Write: "file",
437
+ Edit: "file",
438
+ MultiEdit: "file",
439
+ Glob: "file",
440
+ Grep: "file",
441
+ Bash: "bash",
442
+ Task: "task",
443
+ WebFetch: "search",
444
+ WebSearch: "search",
445
+ TodoWrite: "todo",
446
+ NotebookEdit: "notebook",
447
+ Skill: "skill"
448
+ };
449
+ if (toolName.startsWith("mcp__")) {
450
+ return "mcp";
451
+ }
452
+ return categories[toolName] || "other";
453
+ }
454
+ /**
455
+ * Aggregate session tokens
456
+ */
457
+ aggregateSessionTokens() {
458
+ if (!this.state.storage) return;
459
+ let totalInput = 0;
460
+ let totalOutput = 0;
461
+ let operationCount = 0;
462
+ for (const conversation of this.state.storage.conversations.values()) {
463
+ totalInput += conversation.tokenUsage.inputTokens;
464
+ totalOutput += conversation.tokenUsage.outputTokens;
465
+ operationCount++;
466
+ }
467
+ this.state.storage.session.tokenUsage = {
468
+ inputTokens: totalInput,
469
+ outputTokens: totalOutput,
470
+ totalTokens: totalInput + totalOutput,
471
+ operationCount
472
+ };
473
+ }
474
+ /**
475
+ * Truncate content to max length
476
+ */
477
+ truncateContent(content) {
478
+ if (content.length <= this.config.maxContentLength) {
479
+ return content;
480
+ }
481
+ return content.slice(0, this.config.maxContentLength) + "\n\n[... truncated ...]";
482
+ }
483
+ /**
484
+ * Get storage path for an entity
485
+ */
486
+ getStoragePath(type, id) {
487
+ const dir = join(this.projectRoot, this.config.storageDir, type);
488
+ if (!existsSync(dir)) {
489
+ mkdirSync(dir, { recursive: true });
490
+ }
491
+ return join(dir, `${id}.json`);
492
+ }
493
+ /**
494
+ * Get markdown path for an entity
495
+ */
496
+ getMarkdownPath(type, id) {
497
+ const dir = join(this.projectRoot, this.config.storageDir, "docs", type);
498
+ if (!existsSync(dir)) {
499
+ mkdirSync(dir, { recursive: true });
500
+ }
501
+ return join(dir, `${id}.md`);
502
+ }
503
+ /**
504
+ * Save session to storage
505
+ */
506
+ saveSession(session) {
507
+ const path = this.getStoragePath("sessions", session.id);
508
+ writeFileSync(path, JSON.stringify(session, null, 2));
509
+ if (this.config.createMarkdown) {
510
+ this.saveSessionMarkdown(session);
511
+ }
512
+ }
513
+ /**
514
+ * Save session as markdown
515
+ */
516
+ saveSessionMarkdown(session) {
517
+ const path = this.getMarkdownPath("sessions", session.id);
518
+ const content = `---
519
+ title: "${session.name}"
520
+ type: claude-session
521
+ status: ${session.status}
522
+ created: ${session.startedAt.toISOString()}
523
+ updated: ${session.metadata.updatedAt.toISOString()}
524
+ tags: [${session.metadata.tags.join(", ")}]
525
+ session_id: "${session.id}"
526
+ ---
527
+
528
+ # ${session.name}
529
+
530
+ **Purpose:** ${session.purpose || "General interaction"}
531
+
532
+ **Started:** ${session.startedAt.toISOString()}
533
+ ${session.endedAt ? `**Ended:** ${session.endedAt.toISOString()}` : "**Status:** In Progress"}
534
+
535
+ ## Environment
536
+
537
+ - **Working Directory:** ${session.environment?.workingDirectory || "Unknown"}
538
+ ${session.environment?.gitBranch ? `- **Git Branch:** ${session.environment.gitBranch}` : ""}
539
+
540
+ ## Conversations
541
+
542
+ ${session.conversationIds.map((id) => `- [[${id}]]`).join("\n") || "No conversations yet"}
543
+
544
+ ## Token Usage
545
+
546
+ | Metric | Count |
547
+ |--------|-------|
548
+ | Input Tokens | ${session.tokenUsage.inputTokens} |
549
+ | Output Tokens | ${session.tokenUsage.outputTokens} |
550
+ | Total Tokens | ${session.tokenUsage.totalTokens} |
551
+ | Operations | ${session.tokenUsage.operationCount} |
552
+
553
+ ---
554
+ > Captured by kg-agent hook system
555
+ `;
556
+ writeFileSync(path, content);
557
+ }
558
+ /**
559
+ * Save message to storage
560
+ */
561
+ saveMessage(message) {
562
+ if (this.config.createMarkdown) {
563
+ this.appendToConversationLog(message);
564
+ }
565
+ }
566
+ /**
567
+ * Append message to conversation log
568
+ */
569
+ appendToConversationLog(message) {
570
+ const path = this.getMarkdownPath("conversations", message.conversationId);
571
+ const dir = dirname(path);
572
+ if (!existsSync(dir)) {
573
+ mkdirSync(dir, { recursive: true });
574
+ }
575
+ if (!existsSync(path)) {
576
+ const header = `---
577
+ title: "Conversation ${message.conversationId}"
578
+ type: claude-conversation
579
+ created: ${message.timestamp.toISOString()}
580
+ tags: [${this.config.defaultTags.join(", ")}]
581
+ ---
582
+
583
+ # Conversation Log
584
+
585
+ `;
586
+ writeFileSync(path, header);
587
+ }
588
+ const roleIcon = message.role === "user" ? "👤" : "🤖";
589
+ const entry = `
590
+ ## ${roleIcon} ${message.role.charAt(0).toUpperCase() + message.role.slice(1)} - ${message.timestamp.toISOString()}
591
+
592
+ ${message.content}
593
+
594
+ ${message.toolCallIds && message.toolCallIds.length > 0 ? `**Tool Calls:** ${message.toolCallIds.join(", ")}` : ""}
595
+
596
+ ---
597
+ `;
598
+ appendFileSync(path, entry);
599
+ }
600
+ /**
601
+ * Save tool call to storage
602
+ */
603
+ saveToolCall(toolCall) {
604
+ if (this.config.separateToolOutputs && toolCall.output) {
605
+ const path = this.getStoragePath("tool-outputs", toolCall.id);
606
+ writeFileSync(
607
+ path,
608
+ JSON.stringify(
609
+ {
610
+ id: toolCall.id,
611
+ name: toolCall.toolName,
612
+ category: toolCall.toolCategory,
613
+ input: toolCall.input,
614
+ output: toolCall.output,
615
+ status: toolCall.status,
616
+ executionTimeMs: toolCall.executionTimeMs,
617
+ affectedFiles: toolCall.affectedFiles,
618
+ timestamp: toolCall.completedAt?.toISOString()
619
+ },
620
+ null,
621
+ 2
622
+ )
623
+ );
624
+ }
625
+ }
626
+ /**
627
+ * Save sub-agent to storage
628
+ */
629
+ saveSubAgent(subAgent) {
630
+ const path = this.getStoragePath("agents", subAgent.id);
631
+ writeFileSync(path, JSON.stringify(subAgent, null, 2));
632
+ if (this.config.createMarkdown) {
633
+ this.saveSubAgentMarkdown(subAgent);
634
+ }
635
+ }
636
+ /**
637
+ * Save sub-agent as markdown
638
+ */
639
+ saveSubAgentMarkdown(subAgent) {
640
+ const path = this.getMarkdownPath("agents", subAgent.id);
641
+ const content = `---
642
+ title: "Sub-Agent: ${subAgent.name.slice(0, 50)}"
643
+ type: claude-agent
644
+ status: ${subAgent.status}
645
+ agent_type: ${subAgent.agentType}
646
+ created: ${subAgent.startedAt.toISOString()}
647
+ tags: [${subAgent.metadata.tags.join(", ")}]
648
+ ---
649
+
650
+ # Sub-Agent: ${subAgent.agentType}
651
+
652
+ **Name:** ${subAgent.name}
653
+ **Task:** ${subAgent.task.slice(0, 200)}${subAgent.task.length > 200 ? "..." : ""}
654
+
655
+ **Status:** ${subAgent.status}
656
+ **Model:** ${subAgent.model}
657
+ **Spawned:** ${subAgent.startedAt.toISOString()}
658
+ ${subAgent.completedAt ? `**Completed:** ${subAgent.completedAt.toISOString()}` : ""}
659
+
660
+ ## Task Description
661
+
662
+ \`\`\`
663
+ ${subAgent.task}
664
+ \`\`\`
665
+
666
+ ${subAgent.result ? `
667
+ ## Result
668
+
669
+ **Success:** ${subAgent.result.success}
670
+
671
+ ${subAgent.result.summary ? `### Summary
672
+ ${subAgent.result.summary}` : ""}
673
+
674
+ ${subAgent.result.error ? `### Error
675
+ \`\`\`
676
+ ${subAgent.result.error}
677
+ \`\`\`` : ""}
678
+ ` : ""}
679
+
680
+ ---
681
+ > Captured by kg-agent hook system
682
+ `;
683
+ writeFileSync(path, content);
684
+ }
685
+ /**
686
+ * Get current session
687
+ */
688
+ getCurrentSession() {
689
+ return this.state.storage?.session || null;
690
+ }
691
+ /**
692
+ * Get current conversation
693
+ */
694
+ getCurrentConversation() {
695
+ if (!this.state.storage || !this.state.currentConversationId) return null;
696
+ return this.state.storage.conversations.get(this.state.currentConversationId) || null;
697
+ }
698
+ /**
699
+ * Load session from storage
700
+ */
701
+ loadSession(sessionId) {
702
+ const path = this.getStoragePath("sessions", sessionId);
703
+ if (!existsSync(path)) return null;
704
+ try {
705
+ const data = readFileSync(path, "utf-8");
706
+ return JSON.parse(data);
707
+ } catch {
708
+ return null;
709
+ }
710
+ }
711
+ /**
712
+ * List all stored sessions
713
+ */
714
+ listSessions() {
715
+ const sessionsDir = join(this.projectRoot, this.config.storageDir, "sessions");
716
+ if (!existsSync(sessionsDir)) return [];
717
+ try {
718
+ const { readdirSync } = require("fs");
719
+ const files = readdirSync(sessionsDir);
720
+ return files.filter((f) => f.endsWith(".json")).map((f) => f.replace(".json", ""));
721
+ } catch {
722
+ return [];
723
+ }
724
+ }
725
+ }
726
+ async function processHookEvent(projectRoot, eventType, config) {
727
+ const capture = new HookCaptureSystem(projectRoot, config);
728
+ const chunks = [];
729
+ for await (const chunk of process.stdin) {
730
+ chunks.push(chunk);
731
+ }
732
+ const inputData = Buffer.concat(chunks).toString("utf-8");
733
+ let eventData;
734
+ try {
735
+ const parsed = JSON.parse(inputData);
736
+ eventData = {
737
+ event: eventType,
738
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
739
+ toolName: parsed.tool_name || process.env.TOOL_NAME,
740
+ toolInput: parsed.tool_input,
741
+ toolOutput: parsed.tool_output,
742
+ userPrompt: parsed.user_prompt || inputData,
743
+ metadata: parsed
744
+ };
745
+ } catch {
746
+ eventData = {
747
+ event: eventType,
748
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
749
+ userPrompt: inputData
750
+ };
751
+ }
752
+ capture.handleHookEvent(eventData);
753
+ }
754
+ function generateHookConfig(projectRoot) {
755
+ const kgBinPath = "npx @weavelogic/knowledge-graph-agent";
756
+ return {
757
+ hooks: {
758
+ UserPromptSubmit: [
759
+ {
760
+ type: "command",
761
+ command: `${kgBinPath} hooks capture --event UserPromptSubmit --path "${projectRoot}"`
762
+ }
763
+ ],
764
+ PreToolUse: [
765
+ {
766
+ type: "command",
767
+ command: `${kgBinPath} hooks capture --event PreToolUse --path "${projectRoot}"`
768
+ }
769
+ ],
770
+ PostToolUse: [
771
+ {
772
+ type: "command",
773
+ command: `${kgBinPath} hooks capture --event PostToolUse --path "${projectRoot}"`
774
+ }
775
+ ],
776
+ Stop: [
777
+ {
778
+ type: "command",
779
+ command: `${kgBinPath} hooks capture --event Stop --path "${projectRoot}"`
780
+ }
781
+ ]
782
+ }
783
+ };
784
+ }
785
+ export {
786
+ DEFAULT_CAPTURE_CONFIG,
787
+ HookCaptureSystem,
788
+ HookCaptureSystem as default,
789
+ generateHookConfig,
790
+ processHookEvent
791
+ };
792
+ //# sourceMappingURL=hook-capture.js.map