@aitytech/agentkits-memory 1.0.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.
Files changed (116) hide show
  1. package/README.md +250 -0
  2. package/dist/cache-manager.d.ts +134 -0
  3. package/dist/cache-manager.d.ts.map +1 -0
  4. package/dist/cache-manager.js +407 -0
  5. package/dist/cache-manager.js.map +1 -0
  6. package/dist/cli/save.d.ts +20 -0
  7. package/dist/cli/save.d.ts.map +1 -0
  8. package/dist/cli/save.js +94 -0
  9. package/dist/cli/save.js.map +1 -0
  10. package/dist/cli/setup.d.ts +18 -0
  11. package/dist/cli/setup.d.ts.map +1 -0
  12. package/dist/cli/setup.js +163 -0
  13. package/dist/cli/setup.js.map +1 -0
  14. package/dist/cli/viewer.d.ts +21 -0
  15. package/dist/cli/viewer.d.ts.map +1 -0
  16. package/dist/cli/viewer.js +182 -0
  17. package/dist/cli/viewer.js.map +1 -0
  18. package/dist/hnsw-index.d.ts +111 -0
  19. package/dist/hnsw-index.d.ts.map +1 -0
  20. package/dist/hnsw-index.js +781 -0
  21. package/dist/hnsw-index.js.map +1 -0
  22. package/dist/hooks/cli.d.ts +20 -0
  23. package/dist/hooks/cli.d.ts.map +1 -0
  24. package/dist/hooks/cli.js +102 -0
  25. package/dist/hooks/cli.js.map +1 -0
  26. package/dist/hooks/context.d.ts +31 -0
  27. package/dist/hooks/context.d.ts.map +1 -0
  28. package/dist/hooks/context.js +64 -0
  29. package/dist/hooks/context.js.map +1 -0
  30. package/dist/hooks/index.d.ts +16 -0
  31. package/dist/hooks/index.d.ts.map +1 -0
  32. package/dist/hooks/index.js +20 -0
  33. package/dist/hooks/index.js.map +1 -0
  34. package/dist/hooks/observation.d.ts +30 -0
  35. package/dist/hooks/observation.d.ts.map +1 -0
  36. package/dist/hooks/observation.js +79 -0
  37. package/dist/hooks/observation.js.map +1 -0
  38. package/dist/hooks/service.d.ts +102 -0
  39. package/dist/hooks/service.d.ts.map +1 -0
  40. package/dist/hooks/service.js +454 -0
  41. package/dist/hooks/service.js.map +1 -0
  42. package/dist/hooks/session-init.d.ts +30 -0
  43. package/dist/hooks/session-init.d.ts.map +1 -0
  44. package/dist/hooks/session-init.js +54 -0
  45. package/dist/hooks/session-init.js.map +1 -0
  46. package/dist/hooks/summarize.d.ts +30 -0
  47. package/dist/hooks/summarize.d.ts.map +1 -0
  48. package/dist/hooks/summarize.js +74 -0
  49. package/dist/hooks/summarize.js.map +1 -0
  50. package/dist/hooks/types.d.ts +193 -0
  51. package/dist/hooks/types.d.ts.map +1 -0
  52. package/dist/hooks/types.js +137 -0
  53. package/dist/hooks/types.js.map +1 -0
  54. package/dist/index.d.ts +173 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +564 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/mcp/index.d.ts +9 -0
  59. package/dist/mcp/index.d.ts.map +1 -0
  60. package/dist/mcp/index.js +9 -0
  61. package/dist/mcp/index.js.map +1 -0
  62. package/dist/mcp/server.d.ts +22 -0
  63. package/dist/mcp/server.d.ts.map +1 -0
  64. package/dist/mcp/server.js +368 -0
  65. package/dist/mcp/server.js.map +1 -0
  66. package/dist/mcp/tools.d.ts +14 -0
  67. package/dist/mcp/tools.d.ts.map +1 -0
  68. package/dist/mcp/tools.js +110 -0
  69. package/dist/mcp/tools.js.map +1 -0
  70. package/dist/mcp/types.d.ts +100 -0
  71. package/dist/mcp/types.d.ts.map +1 -0
  72. package/dist/mcp/types.js +9 -0
  73. package/dist/mcp/types.js.map +1 -0
  74. package/dist/migration.d.ts +77 -0
  75. package/dist/migration.d.ts.map +1 -0
  76. package/dist/migration.js +457 -0
  77. package/dist/migration.js.map +1 -0
  78. package/dist/sqljs-backend.d.ts +128 -0
  79. package/dist/sqljs-backend.d.ts.map +1 -0
  80. package/dist/sqljs-backend.js +623 -0
  81. package/dist/sqljs-backend.js.map +1 -0
  82. package/dist/types.d.ts +481 -0
  83. package/dist/types.d.ts.map +1 -0
  84. package/dist/types.js +73 -0
  85. package/dist/types.js.map +1 -0
  86. package/hooks.json +46 -0
  87. package/package.json +67 -0
  88. package/src/__tests__/index.test.ts +407 -0
  89. package/src/__tests__/sqljs-backend.test.ts +410 -0
  90. package/src/cache-manager.ts +515 -0
  91. package/src/cli/save.ts +109 -0
  92. package/src/cli/setup.ts +203 -0
  93. package/src/cli/viewer.ts +218 -0
  94. package/src/hnsw-index.ts +1013 -0
  95. package/src/hooks/__tests__/handlers.test.ts +298 -0
  96. package/src/hooks/__tests__/integration.test.ts +431 -0
  97. package/src/hooks/__tests__/service.test.ts +487 -0
  98. package/src/hooks/__tests__/types.test.ts +341 -0
  99. package/src/hooks/cli.ts +121 -0
  100. package/src/hooks/context.ts +77 -0
  101. package/src/hooks/index.ts +23 -0
  102. package/src/hooks/observation.ts +102 -0
  103. package/src/hooks/service.ts +582 -0
  104. package/src/hooks/session-init.ts +70 -0
  105. package/src/hooks/summarize.ts +89 -0
  106. package/src/hooks/types.ts +365 -0
  107. package/src/index.ts +755 -0
  108. package/src/mcp/__tests__/server.test.ts +181 -0
  109. package/src/mcp/index.ts +9 -0
  110. package/src/mcp/server.ts +441 -0
  111. package/src/mcp/tools.ts +113 -0
  112. package/src/mcp/types.ts +109 -0
  113. package/src/migration.ts +574 -0
  114. package/src/sql.js.d.ts +70 -0
  115. package/src/sqljs-backend.ts +789 -0
  116. package/src/types.ts +715 -0
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Summarize Hook Handler (Stop)
3
+ *
4
+ * Generates a session summary when Claude Code session ends.
5
+ * Uses template-based summarization (no LLM required).
6
+ *
7
+ * @module @agentkits/memory/hooks/summarize
8
+ */
9
+
10
+ import {
11
+ NormalizedHookInput,
12
+ HookResult,
13
+ EventHandler,
14
+ } from './types.js';
15
+ import { MemoryHookService } from './service.js';
16
+
17
+ /**
18
+ * Summarize Hook - Stop Event
19
+ *
20
+ * Called when a Claude Code session ends.
21
+ * Generates a summary and marks the session as completed.
22
+ */
23
+ export class SummarizeHook implements EventHandler {
24
+ private service: MemoryHookService;
25
+
26
+ constructor(service: MemoryHookService) {
27
+ this.service = service;
28
+ }
29
+
30
+ /**
31
+ * Execute the summarize hook
32
+ */
33
+ async execute(input: NormalizedHookInput): Promise<HookResult> {
34
+ try {
35
+ // Initialize service
36
+ await this.service.initialize();
37
+
38
+ // Check if session exists
39
+ const session = this.service.getSession(input.sessionId);
40
+ if (!session) {
41
+ // No session to summarize
42
+ return {
43
+ continue: true,
44
+ suppressOutput: true,
45
+ };
46
+ }
47
+
48
+ // Generate summary from observations
49
+ const summary = await this.service.generateSummary(input.sessionId);
50
+
51
+ // Complete the session with summary
52
+ await this.service.completeSession(input.sessionId, summary);
53
+
54
+ // Shutdown service
55
+ await this.service.shutdown();
56
+
57
+ return {
58
+ continue: true,
59
+ suppressOutput: true,
60
+ };
61
+ } catch (error) {
62
+ // Log error but don't block session end
63
+ console.error('[AgentKits Memory] Summarize hook error:', error);
64
+
65
+ // Try to shutdown anyway
66
+ try {
67
+ await this.service.shutdown();
68
+ } catch {
69
+ // Ignore shutdown errors
70
+ }
71
+
72
+ return {
73
+ continue: true,
74
+ suppressOutput: true,
75
+ error: error instanceof Error ? error.message : 'Unknown error',
76
+ };
77
+ }
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Create summarize hook handler
83
+ */
84
+ export function createSummarizeHook(cwd: string): SummarizeHook {
85
+ const service = new MemoryHookService(cwd);
86
+ return new SummarizeHook(service);
87
+ }
88
+
89
+ export default SummarizeHook;
@@ -0,0 +1,365 @@
1
+ /**
2
+ * Hook Types for AgentKits Memory
3
+ *
4
+ * Lightweight hook system for auto-capturing Claude Code sessions.
5
+ * Based on claude-mem patterns but simplified for project-scoped storage.
6
+ *
7
+ * @module @agentkits/memory/hooks/types
8
+ */
9
+
10
+ // ===== Claude Code Hook Input Types =====
11
+
12
+ /**
13
+ * Raw input from Claude Code hooks (via stdin JSON)
14
+ */
15
+ export interface ClaudeCodeHookInput {
16
+ /** Claude's session ID */
17
+ session_id?: string;
18
+
19
+ /** Current working directory */
20
+ cwd?: string;
21
+
22
+ /** User's prompt (UserPromptSubmit) */
23
+ prompt?: string;
24
+
25
+ /** Tool name (PostToolUse) */
26
+ tool_name?: string;
27
+
28
+ /** Tool input parameters (PostToolUse) */
29
+ tool_input?: unknown;
30
+
31
+ /** Tool response/output (PostToolUse) */
32
+ tool_result?: unknown;
33
+
34
+ /** Path to conversation transcript (Stop) */
35
+ transcript_path?: string;
36
+
37
+ /** Stop reason (Stop) */
38
+ stop_reason?: string;
39
+ }
40
+
41
+ /**
42
+ * Normalized hook input for handlers
43
+ */
44
+ export interface NormalizedHookInput {
45
+ /** Session ID */
46
+ sessionId: string;
47
+
48
+ /** Project directory */
49
+ cwd: string;
50
+
51
+ /** Project name (derived from cwd) */
52
+ project: string;
53
+
54
+ /** User's prompt */
55
+ prompt?: string;
56
+
57
+ /** Tool name */
58
+ toolName?: string;
59
+
60
+ /** Tool input */
61
+ toolInput?: unknown;
62
+
63
+ /** Tool response */
64
+ toolResponse?: unknown;
65
+
66
+ /** Transcript path */
67
+ transcriptPath?: string;
68
+
69
+ /** Stop reason */
70
+ stopReason?: string;
71
+
72
+ /** Timestamp */
73
+ timestamp: number;
74
+ }
75
+
76
+ // ===== Hook Result Types =====
77
+
78
+ /**
79
+ * Hook execution result
80
+ */
81
+ export interface HookResult {
82
+ /** Continue processing (always true for us) */
83
+ continue: boolean;
84
+
85
+ /** Suppress output to Claude */
86
+ suppressOutput: boolean;
87
+
88
+ /** Additional context to inject (SessionStart only) */
89
+ additionalContext?: string;
90
+
91
+ /** Error message if failed */
92
+ error?: string;
93
+ }
94
+
95
+ /**
96
+ * Hook-specific output for Claude Code
97
+ */
98
+ export interface HookSpecificOutput {
99
+ hookEventName: string;
100
+ additionalContext?: string;
101
+ }
102
+
103
+ /**
104
+ * Full hook response for Claude Code
105
+ */
106
+ export interface ClaudeCodeHookResponse {
107
+ continue?: boolean;
108
+ suppressOutput?: boolean;
109
+ hookSpecificOutput?: HookSpecificOutput;
110
+ }
111
+
112
+ // ===== Event Handler Types =====
113
+
114
+ /**
115
+ * Hook event types
116
+ */
117
+ export type HookEventType =
118
+ | 'context' // SessionStart - inject context
119
+ | 'session-init' // UserPromptSubmit - initialize session
120
+ | 'observation' // PostToolUse - capture tool usage
121
+ | 'summarize'; // Stop - generate summary
122
+
123
+ /**
124
+ * Event handler interface
125
+ */
126
+ export interface EventHandler {
127
+ /** Execute the hook handler */
128
+ execute(input: NormalizedHookInput): Promise<HookResult>;
129
+ }
130
+
131
+ // ===== Observation Types =====
132
+
133
+ /**
134
+ * Captured observation from tool usage
135
+ */
136
+ export interface Observation {
137
+ /** Unique ID */
138
+ id: string;
139
+
140
+ /** Session ID */
141
+ sessionId: string;
142
+
143
+ /** Project name */
144
+ project: string;
145
+
146
+ /** Tool name */
147
+ toolName: string;
148
+
149
+ /** Tool input (JSON) */
150
+ toolInput: string;
151
+
152
+ /** Tool response (JSON, truncated) */
153
+ toolResponse: string;
154
+
155
+ /** Working directory */
156
+ cwd: string;
157
+
158
+ /** Timestamp */
159
+ timestamp: number;
160
+
161
+ /** Observation type */
162
+ type: ObservationType;
163
+
164
+ /** Brief title (auto-generated) */
165
+ title?: string;
166
+ }
167
+
168
+ /**
169
+ * Observation types based on tool usage
170
+ */
171
+ export type ObservationType =
172
+ | 'read' // Read, Glob, Grep
173
+ | 'write' // Write, Edit
174
+ | 'execute' // Bash, Task
175
+ | 'search' // WebSearch, WebFetch
176
+ | 'other'; // Unknown tools
177
+
178
+ /**
179
+ * Session record for tracking
180
+ */
181
+ export interface SessionRecord {
182
+ /** Database ID */
183
+ id: number;
184
+
185
+ /** Claude's session ID */
186
+ sessionId: string;
187
+
188
+ /** Project name */
189
+ project: string;
190
+
191
+ /** First user prompt */
192
+ prompt: string;
193
+
194
+ /** Session start time */
195
+ startedAt: number;
196
+
197
+ /** Session end time */
198
+ endedAt?: number;
199
+
200
+ /** Number of observations */
201
+ observationCount: number;
202
+
203
+ /** Auto-generated summary */
204
+ summary?: string;
205
+
206
+ /** Status */
207
+ status: 'active' | 'completed' | 'abandoned';
208
+ }
209
+
210
+ // ===== Context Types =====
211
+
212
+ /**
213
+ * Context to inject on session start
214
+ */
215
+ export interface MemoryContext {
216
+ /** Recent observations */
217
+ recentObservations: Observation[];
218
+
219
+ /** Previous sessions */
220
+ previousSessions: SessionRecord[];
221
+
222
+ /** Project-specific patterns */
223
+ patterns?: string[];
224
+
225
+ /** Recent decisions */
226
+ decisions?: string[];
227
+
228
+ /** Formatted markdown */
229
+ markdown: string;
230
+ }
231
+
232
+ // ===== Utility Functions =====
233
+
234
+ /**
235
+ * Generate observation ID
236
+ */
237
+ export function generateObservationId(): string {
238
+ const timestamp = Date.now().toString(36);
239
+ const random = Math.random().toString(36).substring(2, 6);
240
+ return `obs_${timestamp}_${random}`;
241
+ }
242
+
243
+ /**
244
+ * Get project name from cwd
245
+ */
246
+ export function getProjectName(cwd: string): string {
247
+ const parts = cwd.split(/[/\\]/);
248
+ return parts[parts.length - 1] || 'unknown';
249
+ }
250
+
251
+ /**
252
+ * Determine observation type from tool name
253
+ */
254
+ export function getObservationType(toolName: string): ObservationType {
255
+ const readTools = ['Read', 'Glob', 'Grep', 'LS'];
256
+ const writeTools = ['Write', 'Edit', 'NotebookEdit'];
257
+ const executeTools = ['Bash', 'Task', 'Skill'];
258
+ const searchTools = ['WebSearch', 'WebFetch'];
259
+
260
+ if (readTools.includes(toolName)) return 'read';
261
+ if (writeTools.includes(toolName)) return 'write';
262
+ if (executeTools.includes(toolName)) return 'execute';
263
+ if (searchTools.includes(toolName)) return 'search';
264
+ return 'other';
265
+ }
266
+
267
+ /**
268
+ * Generate observation title from tool usage
269
+ */
270
+ export function generateObservationTitle(toolName: string, toolInput: unknown): string {
271
+ try {
272
+ const input = typeof toolInput === 'string' ? JSON.parse(toolInput) : toolInput;
273
+
274
+ switch (toolName) {
275
+ case 'Read':
276
+ return `Read ${input?.file_path || input?.path || 'file'}`;
277
+ case 'Write':
278
+ return `Write ${input?.file_path || input?.path || 'file'}`;
279
+ case 'Edit':
280
+ return `Edit ${input?.file_path || input?.path || 'file'}`;
281
+ case 'Bash':
282
+ const cmd = input?.command || '';
283
+ return `Run: ${cmd.substring(0, 50)}${cmd.length > 50 ? '...' : ''}`;
284
+ case 'Glob':
285
+ return `Find ${input?.pattern || 'files'}`;
286
+ case 'Grep':
287
+ return `Search "${input?.pattern || ''}"`;
288
+ case 'Task':
289
+ return `Task: ${input?.description || 'agent'}`;
290
+ case 'WebSearch':
291
+ return `Search: ${input?.query || ''}`;
292
+ case 'WebFetch':
293
+ return `Fetch: ${input?.url || ''}`;
294
+ default:
295
+ return `${toolName}`;
296
+ }
297
+ } catch {
298
+ return toolName;
299
+ }
300
+ }
301
+
302
+ /**
303
+ * Truncate string to max length
304
+ */
305
+ export function truncate(str: string, maxLength: number = 1000): string {
306
+ if (str.length <= maxLength) return str;
307
+ return str.substring(0, maxLength) + '...[truncated]';
308
+ }
309
+
310
+ /**
311
+ * Standard hook response (continue, no output)
312
+ */
313
+ export const STANDARD_RESPONSE: ClaudeCodeHookResponse = {
314
+ continue: true,
315
+ suppressOutput: true,
316
+ };
317
+
318
+ /**
319
+ * Format hook response for stdout
320
+ */
321
+ export function formatResponse(result: HookResult): string {
322
+ if (result.additionalContext) {
323
+ return JSON.stringify({
324
+ hookSpecificOutput: {
325
+ hookEventName: 'SessionStart',
326
+ additionalContext: result.additionalContext,
327
+ },
328
+ });
329
+ }
330
+
331
+ return JSON.stringify(STANDARD_RESPONSE);
332
+ }
333
+
334
+ /**
335
+ * Parse stdin input from Claude Code
336
+ */
337
+ export function parseHookInput(stdin: string): NormalizedHookInput {
338
+ try {
339
+ const raw: ClaudeCodeHookInput = JSON.parse(stdin);
340
+
341
+ const cwd = raw.cwd || process.cwd();
342
+
343
+ return {
344
+ sessionId: raw.session_id || `session_${Date.now()}`,
345
+ cwd,
346
+ project: getProjectName(cwd),
347
+ prompt: raw.prompt,
348
+ toolName: raw.tool_name,
349
+ toolInput: raw.tool_input,
350
+ toolResponse: raw.tool_result,
351
+ transcriptPath: raw.transcript_path,
352
+ stopReason: raw.stop_reason,
353
+ timestamp: Date.now(),
354
+ };
355
+ } catch {
356
+ // Fallback for empty or invalid input
357
+ const cwd = process.cwd();
358
+ return {
359
+ sessionId: `session_${Date.now()}`,
360
+ cwd,
361
+ project: getProjectName(cwd),
362
+ timestamp: Date.now(),
363
+ };
364
+ }
365
+ }