@aitytech/agentkits-memory 1.0.0 → 2.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.
- package/LICENSE +21 -0
- package/README.md +267 -149
- package/assets/agentkits-memory-add-memory.png +0 -0
- package/assets/agentkits-memory-memory-detail.png +0 -0
- package/assets/agentkits-memory-memory-list.png +0 -0
- package/assets/logo.svg +24 -0
- package/dist/better-sqlite3-backend.d.ts +192 -0
- package/dist/better-sqlite3-backend.d.ts.map +1 -0
- package/dist/better-sqlite3-backend.js +801 -0
- package/dist/better-sqlite3-backend.js.map +1 -0
- package/dist/cli/save.js +0 -0
- package/dist/cli/setup.d.ts +6 -2
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +289 -42
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/viewer.js +25 -56
- package/dist/cli/viewer.js.map +1 -1
- package/dist/cli/web-viewer.d.ts +14 -0
- package/dist/cli/web-viewer.d.ts.map +1 -0
- package/dist/cli/web-viewer.js +1769 -0
- package/dist/cli/web-viewer.js.map +1 -0
- package/dist/embeddings/embedding-cache.d.ts +131 -0
- package/dist/embeddings/embedding-cache.d.ts.map +1 -0
- package/dist/embeddings/embedding-cache.js +217 -0
- package/dist/embeddings/embedding-cache.js.map +1 -0
- package/dist/embeddings/index.d.ts +11 -0
- package/dist/embeddings/index.d.ts.map +1 -0
- package/dist/embeddings/index.js +11 -0
- package/dist/embeddings/index.js.map +1 -0
- package/dist/embeddings/local-embeddings.d.ts +140 -0
- package/dist/embeddings/local-embeddings.d.ts.map +1 -0
- package/dist/embeddings/local-embeddings.js +293 -0
- package/dist/embeddings/local-embeddings.js.map +1 -0
- package/dist/hooks/context.d.ts +6 -1
- package/dist/hooks/context.d.ts.map +1 -1
- package/dist/hooks/context.js +12 -2
- package/dist/hooks/context.js.map +1 -1
- package/dist/hooks/observation.d.ts +6 -1
- package/dist/hooks/observation.d.ts.map +1 -1
- package/dist/hooks/observation.js +12 -2
- package/dist/hooks/observation.js.map +1 -1
- package/dist/hooks/service.d.ts +1 -6
- package/dist/hooks/service.d.ts.map +1 -1
- package/dist/hooks/service.js +33 -85
- package/dist/hooks/service.js.map +1 -1
- package/dist/hooks/session-init.d.ts +6 -1
- package/dist/hooks/session-init.d.ts.map +1 -1
- package/dist/hooks/session-init.js +12 -2
- package/dist/hooks/session-init.js.map +1 -1
- package/dist/hooks/summarize.d.ts +6 -1
- package/dist/hooks/summarize.d.ts.map +1 -1
- package/dist/hooks/summarize.js +12 -2
- package/dist/hooks/summarize.js.map +1 -1
- package/dist/index.d.ts +10 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +172 -94
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +17 -3
- package/dist/mcp/server.js.map +1 -1
- package/dist/migration.js +3 -3
- package/dist/migration.js.map +1 -1
- package/dist/search/hybrid-search.d.ts +262 -0
- package/dist/search/hybrid-search.d.ts.map +1 -0
- package/dist/search/hybrid-search.js +688 -0
- package/dist/search/hybrid-search.js.map +1 -0
- package/dist/search/index.d.ts +13 -0
- package/dist/search/index.d.ts.map +1 -0
- package/dist/search/index.js +13 -0
- package/dist/search/index.js.map +1 -0
- package/dist/search/token-economics.d.ts +161 -0
- package/dist/search/token-economics.d.ts.map +1 -0
- package/dist/search/token-economics.js +239 -0
- package/dist/search/token-economics.js.map +1 -0
- package/dist/types.d.ts +0 -68
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +23 -8
- package/src/__tests__/better-sqlite3-backend.test.ts +1466 -0
- package/src/__tests__/cache-manager.test.ts +499 -0
- package/src/__tests__/embedding-integration.test.ts +481 -0
- package/src/__tests__/hnsw-index.test.ts +727 -0
- package/src/__tests__/index.test.ts +432 -0
- package/src/better-sqlite3-backend.ts +1000 -0
- package/src/cli/setup.ts +358 -47
- package/src/cli/viewer.ts +28 -63
- package/src/cli/web-viewer.ts +1956 -0
- package/src/embeddings/__tests__/embedding-cache.test.ts +269 -0
- package/src/embeddings/__tests__/local-embeddings.test.ts +495 -0
- package/src/embeddings/embedding-cache.ts +318 -0
- package/src/embeddings/index.ts +20 -0
- package/src/embeddings/local-embeddings.ts +419 -0
- package/src/hooks/__tests__/handlers.test.ts +58 -17
- package/src/hooks/__tests__/integration.test.ts +77 -26
- package/src/hooks/context.ts +13 -2
- package/src/hooks/observation.ts +13 -2
- package/src/hooks/service.ts +39 -100
- package/src/hooks/session-init.ts +13 -2
- package/src/hooks/summarize.ts +13 -2
- package/src/index.ts +210 -116
- package/src/mcp/server.ts +20 -3
- package/src/search/__tests__/hybrid-search.test.ts +669 -0
- package/src/search/__tests__/token-economics.test.ts +276 -0
- package/src/search/hybrid-search.ts +968 -0
- package/src/search/index.ts +29 -0
- package/src/search/token-economics.ts +367 -0
- package/src/types.ts +0 -96
- package/src/__tests__/sqljs-backend.test.ts +0 -410
- package/src/migration.ts +0 -574
- package/src/sql.js.d.ts +0 -70
- package/src/sqljs-backend.ts +0 -789
package/src/migration.ts
DELETED
|
@@ -1,574 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Memory Migration Utility for AgentKits
|
|
3
|
-
*
|
|
4
|
-
* Migrates existing .claude/memory/*.md files to SQLite database.
|
|
5
|
-
* Preserves all content and metadata from markdown frontmatter.
|
|
6
|
-
*
|
|
7
|
-
* @module @agentkits/memory/migration
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { EventEmitter } from 'node:events';
|
|
11
|
-
import { promises as fs } from 'node:fs';
|
|
12
|
-
import * as path from 'node:path';
|
|
13
|
-
import {
|
|
14
|
-
MigrationConfig,
|
|
15
|
-
MigrationProgress,
|
|
16
|
-
MigrationResult,
|
|
17
|
-
MigrationError,
|
|
18
|
-
MigrationSource,
|
|
19
|
-
MemoryEntry,
|
|
20
|
-
MemoryType,
|
|
21
|
-
MemoryEntryInput,
|
|
22
|
-
EmbeddingGenerator,
|
|
23
|
-
createDefaultEntry,
|
|
24
|
-
DEFAULT_NAMESPACES,
|
|
25
|
-
NAMESPACE_TYPE_MAP,
|
|
26
|
-
} from './types.js';
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Default migration configuration
|
|
30
|
-
*/
|
|
31
|
-
const DEFAULT_MIGRATION_CONFIG: Partial<MigrationConfig> = {
|
|
32
|
-
batchSize: 50,
|
|
33
|
-
generateEmbeddings: false, // Default false for speed
|
|
34
|
-
validateData: true,
|
|
35
|
-
continueOnError: true,
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Parsed markdown file structure
|
|
40
|
-
*/
|
|
41
|
-
interface ParsedMarkdown {
|
|
42
|
-
frontmatter: Record<string, unknown>;
|
|
43
|
-
content: string;
|
|
44
|
-
sections: Array<{
|
|
45
|
-
title: string;
|
|
46
|
-
level: number;
|
|
47
|
-
content: string;
|
|
48
|
-
}>;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Memory Migration Manager for AgentKits
|
|
53
|
-
*
|
|
54
|
-
* Handles migration from:
|
|
55
|
-
* - Markdown files (.claude/memory/*.md)
|
|
56
|
-
* - JSON exports
|
|
57
|
-
* - Other SQLite databases
|
|
58
|
-
*/
|
|
59
|
-
export class MemoryMigrator extends EventEmitter {
|
|
60
|
-
private config: MigrationConfig;
|
|
61
|
-
private embeddingGenerator?: EmbeddingGenerator;
|
|
62
|
-
private progress: MigrationProgress;
|
|
63
|
-
private storeFunction: (entry: MemoryEntry) => Promise<void>;
|
|
64
|
-
|
|
65
|
-
constructor(
|
|
66
|
-
storeFunction: (entry: MemoryEntry) => Promise<void>,
|
|
67
|
-
config: Partial<MigrationConfig>,
|
|
68
|
-
embeddingGenerator?: EmbeddingGenerator
|
|
69
|
-
) {
|
|
70
|
-
super();
|
|
71
|
-
this.storeFunction = storeFunction;
|
|
72
|
-
this.config = { ...DEFAULT_MIGRATION_CONFIG, ...config } as MigrationConfig;
|
|
73
|
-
this.embeddingGenerator = embeddingGenerator;
|
|
74
|
-
this.progress = this.initializeProgress();
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Run the migration
|
|
79
|
-
*/
|
|
80
|
-
async migrate(): Promise<MigrationResult> {
|
|
81
|
-
const startTime = Date.now();
|
|
82
|
-
this.progress = this.initializeProgress();
|
|
83
|
-
|
|
84
|
-
this.emit('migration:started', { source: this.config.source });
|
|
85
|
-
|
|
86
|
-
try {
|
|
87
|
-
// Load entries from source
|
|
88
|
-
const entries = await this.loadFromSource();
|
|
89
|
-
this.progress.total = entries.length;
|
|
90
|
-
|
|
91
|
-
this.emit('migration:progress', { ...this.progress });
|
|
92
|
-
|
|
93
|
-
// Process entries
|
|
94
|
-
for (const entry of entries) {
|
|
95
|
-
try {
|
|
96
|
-
// Validate if enabled
|
|
97
|
-
if (this.config.validateData) {
|
|
98
|
-
const validation = this.validateEntry(entry);
|
|
99
|
-
if (!validation.valid) {
|
|
100
|
-
if (this.config.continueOnError) {
|
|
101
|
-
this.addError(
|
|
102
|
-
entry.key,
|
|
103
|
-
validation.reason || 'Validation failed',
|
|
104
|
-
'VALIDATION_ERROR',
|
|
105
|
-
false
|
|
106
|
-
);
|
|
107
|
-
this.progress.skipped++;
|
|
108
|
-
continue;
|
|
109
|
-
} else {
|
|
110
|
-
throw new Error(validation.reason);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Store entry
|
|
116
|
-
await this.storeFunction(entry);
|
|
117
|
-
this.progress.migrated++;
|
|
118
|
-
|
|
119
|
-
this.progress.percentage = Math.round(
|
|
120
|
-
(this.progress.migrated / this.progress.total) * 100
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
this.emit('migration:progress', { ...this.progress });
|
|
124
|
-
} catch (error) {
|
|
125
|
-
if (this.config.continueOnError) {
|
|
126
|
-
this.addError(
|
|
127
|
-
entry.key,
|
|
128
|
-
(error as Error).message,
|
|
129
|
-
'STORE_ERROR',
|
|
130
|
-
true
|
|
131
|
-
);
|
|
132
|
-
this.progress.failed++;
|
|
133
|
-
} else {
|
|
134
|
-
throw error;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const duration = Date.now() - startTime;
|
|
140
|
-
|
|
141
|
-
const result: MigrationResult = {
|
|
142
|
-
success: this.progress.failed === 0 || this.config.continueOnError,
|
|
143
|
-
progress: { ...this.progress },
|
|
144
|
-
duration,
|
|
145
|
-
summary: this.generateSummary(),
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
this.emit('migration:completed', result);
|
|
149
|
-
return result;
|
|
150
|
-
} catch (error) {
|
|
151
|
-
const duration = Date.now() - startTime;
|
|
152
|
-
|
|
153
|
-
const result: MigrationResult = {
|
|
154
|
-
success: false,
|
|
155
|
-
progress: { ...this.progress },
|
|
156
|
-
duration,
|
|
157
|
-
summary: `Migration failed: ${(error as Error).message}`,
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
this.emit('migration:failed', { error, result });
|
|
161
|
-
return result;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Get current migration progress
|
|
167
|
-
*/
|
|
168
|
-
getProgress(): MigrationProgress {
|
|
169
|
-
return { ...this.progress };
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// ===== Source Loaders =====
|
|
173
|
-
|
|
174
|
-
private async loadFromSource(): Promise<MemoryEntry[]> {
|
|
175
|
-
switch (this.config.source) {
|
|
176
|
-
case 'markdown':
|
|
177
|
-
return this.loadFromMarkdown();
|
|
178
|
-
case 'json':
|
|
179
|
-
return this.loadFromJSON();
|
|
180
|
-
case 'sqlite':
|
|
181
|
-
return this.loadFromSQLite();
|
|
182
|
-
default:
|
|
183
|
-
throw new Error(`Unknown migration source: ${this.config.source}`);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Load entries from .claude/memory/*.md files
|
|
189
|
-
*/
|
|
190
|
-
private async loadFromMarkdown(): Promise<MemoryEntry[]> {
|
|
191
|
-
const entries: MemoryEntry[] = [];
|
|
192
|
-
const basePath = this.config.sourcePath;
|
|
193
|
-
|
|
194
|
-
try {
|
|
195
|
-
const files = await this.getMarkdownFiles(basePath);
|
|
196
|
-
|
|
197
|
-
for (const filePath of files) {
|
|
198
|
-
try {
|
|
199
|
-
const content = await fs.readFile(filePath, 'utf-8');
|
|
200
|
-
const parsed = this.parseMarkdownFile(content);
|
|
201
|
-
const fileEntries = await this.markdownToEntries(filePath, parsed, basePath);
|
|
202
|
-
entries.push(...fileEntries);
|
|
203
|
-
} catch (error) {
|
|
204
|
-
this.addError(filePath, (error as Error).message, 'PARSE_ERROR', true);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
return entries;
|
|
209
|
-
} catch (error) {
|
|
210
|
-
throw new Error(`Failed to load Markdown: ${(error as Error).message}`);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Get all markdown files in directory
|
|
216
|
-
*/
|
|
217
|
-
private async getMarkdownFiles(dir: string): Promise<string[]> {
|
|
218
|
-
const files: string[] = [];
|
|
219
|
-
|
|
220
|
-
try {
|
|
221
|
-
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
222
|
-
|
|
223
|
-
for (const entry of entries) {
|
|
224
|
-
const fullPath = path.join(dir, entry.name);
|
|
225
|
-
|
|
226
|
-
if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
227
|
-
files.push(fullPath);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
} catch {
|
|
231
|
-
// Directory doesn't exist
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
return files;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Parse a markdown file with frontmatter and sections
|
|
239
|
-
*/
|
|
240
|
-
private parseMarkdownFile(content: string): ParsedMarkdown {
|
|
241
|
-
const result: ParsedMarkdown = {
|
|
242
|
-
frontmatter: {},
|
|
243
|
-
content: content,
|
|
244
|
-
sections: [],
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
// Extract frontmatter if present
|
|
248
|
-
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
249
|
-
|
|
250
|
-
if (frontmatterMatch) {
|
|
251
|
-
const frontmatterText = frontmatterMatch[1];
|
|
252
|
-
result.content = frontmatterMatch[2];
|
|
253
|
-
|
|
254
|
-
// Parse YAML-like frontmatter
|
|
255
|
-
for (const line of frontmatterText.split('\n')) {
|
|
256
|
-
const colonIndex = line.indexOf(':');
|
|
257
|
-
if (colonIndex > 0) {
|
|
258
|
-
const key = line.substring(0, colonIndex).trim();
|
|
259
|
-
let value: unknown = line.substring(colonIndex + 1).trim();
|
|
260
|
-
|
|
261
|
-
// Parse common types
|
|
262
|
-
if (value === 'true') value = true;
|
|
263
|
-
else if (value === 'false') value = false;
|
|
264
|
-
else if (typeof value === 'string' && /^\d+$/.test(value)) value = parseInt(value, 10);
|
|
265
|
-
else if (typeof value === 'string' && value.startsWith('[') && value.endsWith(']')) {
|
|
266
|
-
try {
|
|
267
|
-
value = JSON.parse(value.replace(/'/g, '"'));
|
|
268
|
-
} catch {
|
|
269
|
-
// Keep as string
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
result.frontmatter[key] = value;
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// Parse sections (## headers)
|
|
279
|
-
const sectionRegex = /^(#{1,6})\s+(.+)$/gm;
|
|
280
|
-
let lastIndex = 0;
|
|
281
|
-
let match;
|
|
282
|
-
const sections: Array<{ title: string; level: number; startIndex: number }> = [];
|
|
283
|
-
|
|
284
|
-
while ((match = sectionRegex.exec(result.content)) !== null) {
|
|
285
|
-
sections.push({
|
|
286
|
-
title: match[2],
|
|
287
|
-
level: match[1].length,
|
|
288
|
-
startIndex: match.index,
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// Extract section content
|
|
293
|
-
for (let i = 0; i < sections.length; i++) {
|
|
294
|
-
const section = sections[i];
|
|
295
|
-
const nextSection = sections[i + 1];
|
|
296
|
-
const endIndex = nextSection ? nextSection.startIndex : result.content.length;
|
|
297
|
-
const sectionContent = result.content
|
|
298
|
-
.substring(section.startIndex, endIndex)
|
|
299
|
-
.replace(/^#{1,6}\s+.+\n/, '') // Remove header line
|
|
300
|
-
.trim();
|
|
301
|
-
|
|
302
|
-
result.sections.push({
|
|
303
|
-
title: section.title,
|
|
304
|
-
level: section.level,
|
|
305
|
-
content: sectionContent,
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
return result;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
/**
|
|
313
|
-
* Convert parsed markdown to memory entries
|
|
314
|
-
*/
|
|
315
|
-
private async markdownToEntries(
|
|
316
|
-
filePath: string,
|
|
317
|
-
parsed: ParsedMarkdown,
|
|
318
|
-
basePath: string
|
|
319
|
-
): Promise<MemoryEntry[]> {
|
|
320
|
-
const entries: MemoryEntry[] = [];
|
|
321
|
-
|
|
322
|
-
// Derive namespace from filename (e.g., 'active-context.md' -> 'active-context')
|
|
323
|
-
const filename = path.basename(filePath, '.md');
|
|
324
|
-
const namespace = this.mapFilenameToNamespace(filename);
|
|
325
|
-
const type = NAMESPACE_TYPE_MAP[namespace] || 'semantic';
|
|
326
|
-
|
|
327
|
-
// Create main entry for the file
|
|
328
|
-
const mainEntry = createDefaultEntry({
|
|
329
|
-
key: filename,
|
|
330
|
-
content: parsed.content.trim(),
|
|
331
|
-
type,
|
|
332
|
-
namespace,
|
|
333
|
-
tags: Array.isArray(parsed.frontmatter.tags)
|
|
334
|
-
? parsed.frontmatter.tags as string[]
|
|
335
|
-
: [],
|
|
336
|
-
metadata: {
|
|
337
|
-
...parsed.frontmatter,
|
|
338
|
-
sourceFile: filePath,
|
|
339
|
-
migratedAt: Date.now(),
|
|
340
|
-
},
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
// Generate embedding if enabled
|
|
344
|
-
if (this.config.generateEmbeddings && this.embeddingGenerator && parsed.content.trim()) {
|
|
345
|
-
try {
|
|
346
|
-
mainEntry.embedding = await this.embeddingGenerator(parsed.content.trim());
|
|
347
|
-
} catch (error) {
|
|
348
|
-
this.emit('migration:warning', {
|
|
349
|
-
message: `Failed to generate embedding for ${filename}: ${(error as Error).message}`,
|
|
350
|
-
});
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
entries.push(mainEntry);
|
|
355
|
-
|
|
356
|
-
// Optionally create entries for each section
|
|
357
|
-
for (const section of parsed.sections) {
|
|
358
|
-
if (section.content.length > 100) { // Only create entries for substantial sections
|
|
359
|
-
const sectionKey = `${filename}:${this.slugify(section.title)}`;
|
|
360
|
-
const sectionEntry = createDefaultEntry({
|
|
361
|
-
key: sectionKey,
|
|
362
|
-
content: section.content,
|
|
363
|
-
type,
|
|
364
|
-
namespace,
|
|
365
|
-
tags: ['section', `level-${section.level}`],
|
|
366
|
-
metadata: {
|
|
367
|
-
parentKey: filename,
|
|
368
|
-
sectionTitle: section.title,
|
|
369
|
-
sectionLevel: section.level,
|
|
370
|
-
sourceFile: filePath,
|
|
371
|
-
},
|
|
372
|
-
references: [mainEntry.id],
|
|
373
|
-
});
|
|
374
|
-
|
|
375
|
-
// Generate embedding for section if enabled
|
|
376
|
-
if (this.config.generateEmbeddings && this.embeddingGenerator) {
|
|
377
|
-
try {
|
|
378
|
-
sectionEntry.embedding = await this.embeddingGenerator(section.content);
|
|
379
|
-
} catch {
|
|
380
|
-
// Skip embedding for this section
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
entries.push(sectionEntry);
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
return entries;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
/**
|
|
392
|
-
* Map filename to standard namespace
|
|
393
|
-
*/
|
|
394
|
-
private mapFilenameToNamespace(filename: string): string {
|
|
395
|
-
const mapping: Record<string, string> = {
|
|
396
|
-
'project-context': DEFAULT_NAMESPACES.CONTEXT,
|
|
397
|
-
'active-context': DEFAULT_NAMESPACES.ACTIVE,
|
|
398
|
-
'session-state': DEFAULT_NAMESPACES.SESSION,
|
|
399
|
-
'progress': DEFAULT_NAMESPACES.PROGRESS,
|
|
400
|
-
'patterns': DEFAULT_NAMESPACES.PATTERNS,
|
|
401
|
-
'decisions': DEFAULT_NAMESPACES.DECISIONS,
|
|
402
|
-
'errors': DEFAULT_NAMESPACES.ERRORS,
|
|
403
|
-
};
|
|
404
|
-
|
|
405
|
-
return mapping[filename] || filename;
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
/**
|
|
409
|
-
* Load entries from JSON file
|
|
410
|
-
*/
|
|
411
|
-
private async loadFromJSON(): Promise<MemoryEntry[]> {
|
|
412
|
-
const filePath = this.config.sourcePath;
|
|
413
|
-
|
|
414
|
-
try {
|
|
415
|
-
const content = await fs.readFile(filePath, 'utf-8');
|
|
416
|
-
const data = JSON.parse(content);
|
|
417
|
-
|
|
418
|
-
// Handle different JSON formats
|
|
419
|
-
if (Array.isArray(data)) {
|
|
420
|
-
return data.map((item) => this.jsonItemToEntry(item));
|
|
421
|
-
} else if (data.entries && Array.isArray(data.entries)) {
|
|
422
|
-
return data.entries.map((item: unknown) => this.jsonItemToEntry(item));
|
|
423
|
-
} else if (typeof data === 'object') {
|
|
424
|
-
// Assume it's a namespace -> entries map
|
|
425
|
-
const entries: MemoryEntry[] = [];
|
|
426
|
-
for (const [namespace, namespaceEntries] of Object.entries(data)) {
|
|
427
|
-
if (Array.isArray(namespaceEntries)) {
|
|
428
|
-
for (const item of namespaceEntries) {
|
|
429
|
-
entries.push(this.jsonItemToEntry({ ...item, namespace }));
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
return entries;
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
return [];
|
|
437
|
-
} catch (error) {
|
|
438
|
-
throw new Error(`Failed to load JSON: ${(error as Error).message}`);
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
/**
|
|
443
|
-
* Convert JSON item to MemoryEntry
|
|
444
|
-
*/
|
|
445
|
-
private jsonItemToEntry(item: any): MemoryEntry {
|
|
446
|
-
return createDefaultEntry({
|
|
447
|
-
key: item.key || item.id || `entry_${Date.now()}`,
|
|
448
|
-
content: typeof item.content === 'string'
|
|
449
|
-
? item.content
|
|
450
|
-
: typeof item.value === 'string'
|
|
451
|
-
? item.value
|
|
452
|
-
: JSON.stringify(item.value || item.content || item),
|
|
453
|
-
type: item.type || 'semantic',
|
|
454
|
-
namespace: item.namespace || 'default',
|
|
455
|
-
tags: item.tags || [],
|
|
456
|
-
metadata: item.metadata || {},
|
|
457
|
-
});
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
/**
|
|
461
|
-
* Load entries from existing SQLite database
|
|
462
|
-
*/
|
|
463
|
-
private async loadFromSQLite(): Promise<MemoryEntry[]> {
|
|
464
|
-
// Would need sql.js to read existing database
|
|
465
|
-
// For now, return empty and log warning
|
|
466
|
-
this.emit('migration:warning', {
|
|
467
|
-
message: 'SQLite migration requires sql.js to be loaded. Use JSON export instead.',
|
|
468
|
-
});
|
|
469
|
-
return [];
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
// ===== Helper Methods =====
|
|
473
|
-
|
|
474
|
-
private initializeProgress(): MigrationProgress {
|
|
475
|
-
return {
|
|
476
|
-
total: 0,
|
|
477
|
-
migrated: 0,
|
|
478
|
-
failed: 0,
|
|
479
|
-
skipped: 0,
|
|
480
|
-
percentage: 0,
|
|
481
|
-
errors: [],
|
|
482
|
-
};
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
private validateEntry(entry: MemoryEntry): { valid: boolean; reason?: string } {
|
|
486
|
-
if (!entry.key || typeof entry.key !== 'string') {
|
|
487
|
-
return { valid: false, reason: 'Missing or invalid key' };
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
if (!entry.content || typeof entry.content !== 'string') {
|
|
491
|
-
return { valid: false, reason: 'Missing or invalid content' };
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
if (entry.key.length > 500) {
|
|
495
|
-
return { valid: false, reason: 'Key too long (max 500 chars)' };
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
return { valid: true };
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
private addError(
|
|
502
|
-
entryId: string,
|
|
503
|
-
message: string,
|
|
504
|
-
code: string,
|
|
505
|
-
recoverable: boolean
|
|
506
|
-
): void {
|
|
507
|
-
const error: MigrationError = {
|
|
508
|
-
entryId,
|
|
509
|
-
message,
|
|
510
|
-
code,
|
|
511
|
-
recoverable,
|
|
512
|
-
};
|
|
513
|
-
this.progress.errors.push(error);
|
|
514
|
-
this.emit('migration:error', error);
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
private generateSummary(): string {
|
|
518
|
-
const { migrated, failed, skipped, total, errors } = this.progress;
|
|
519
|
-
|
|
520
|
-
let summary = `Migrated ${migrated}/${total} entries`;
|
|
521
|
-
|
|
522
|
-
if (failed > 0) {
|
|
523
|
-
summary += `, ${failed} failed`;
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
if (skipped > 0) {
|
|
527
|
-
summary += `, ${skipped} skipped`;
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
if (errors.length > 0) {
|
|
531
|
-
const errorTypes = new Map<string, number>();
|
|
532
|
-
for (const error of errors) {
|
|
533
|
-
errorTypes.set(error.code, (errorTypes.get(error.code) || 0) + 1);
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
const errorSummary = Array.from(errorTypes.entries())
|
|
537
|
-
.map(([code, count]) => `${code}: ${count}`)
|
|
538
|
-
.join(', ');
|
|
539
|
-
|
|
540
|
-
summary += `. Errors: ${errorSummary}`;
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
return summary;
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
private slugify(text: string): string {
|
|
547
|
-
return text
|
|
548
|
-
.toLowerCase()
|
|
549
|
-
.replace(/[^a-z0-9]+/g, '-')
|
|
550
|
-
.replace(/^-|-$/g, '');
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
/**
|
|
555
|
-
* Convenience function to migrate markdown files
|
|
556
|
-
*/
|
|
557
|
-
export async function migrateMarkdownMemory(
|
|
558
|
-
memoryDir: string,
|
|
559
|
-
storeFunction: (entry: MemoryEntry) => Promise<void>,
|
|
560
|
-
options: Partial<MigrationConfig> = {}
|
|
561
|
-
): Promise<MigrationResult> {
|
|
562
|
-
const migrator = new MemoryMigrator(
|
|
563
|
-
storeFunction,
|
|
564
|
-
{
|
|
565
|
-
source: 'markdown',
|
|
566
|
-
sourcePath: memoryDir,
|
|
567
|
-
...options,
|
|
568
|
-
}
|
|
569
|
-
);
|
|
570
|
-
|
|
571
|
-
return migrator.migrate();
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
export default MemoryMigrator;
|
package/src/sql.js.d.ts
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Type declarations for sql.js
|
|
3
|
-
* @see https://sql.js.org/
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
declare module 'sql.js' {
|
|
7
|
-
export interface SqlJsStatic {
|
|
8
|
-
Database: typeof Database;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface SqlJsConfig {
|
|
12
|
-
locateFile?: (file: string) => string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface QueryExecResult {
|
|
16
|
-
columns: string[];
|
|
17
|
-
values: (string | number | Uint8Array | null)[][];
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface ParamsObject {
|
|
21
|
-
[key: string]: string | number | Uint8Array | null | undefined;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export type ParamsCallback = (obj: ParamsObject) => void;
|
|
25
|
-
export type BindParams =
|
|
26
|
-
| unknown[]
|
|
27
|
-
| ParamsObject
|
|
28
|
-
| null;
|
|
29
|
-
|
|
30
|
-
export class Statement {
|
|
31
|
-
bind(params?: BindParams): boolean;
|
|
32
|
-
step(): boolean;
|
|
33
|
-
getAsObject(params?: BindParams): ParamsObject;
|
|
34
|
-
get(params?: BindParams): (string | number | Uint8Array | null)[];
|
|
35
|
-
getColumnNames(): string[];
|
|
36
|
-
free(): boolean;
|
|
37
|
-
freemem(): void;
|
|
38
|
-
reset(): void;
|
|
39
|
-
run(params?: BindParams): void;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export class Database {
|
|
43
|
-
constructor(data?: ArrayLike<number> | Buffer | null);
|
|
44
|
-
run(sql: string, params?: BindParams): Database;
|
|
45
|
-
exec(sql: string, params?: BindParams): QueryExecResult[];
|
|
46
|
-
each(
|
|
47
|
-
sql: string,
|
|
48
|
-
params: BindParams,
|
|
49
|
-
callback: ParamsCallback,
|
|
50
|
-
done?: () => void
|
|
51
|
-
): Database;
|
|
52
|
-
each(sql: string, callback: ParamsCallback, done?: () => void): Database;
|
|
53
|
-
prepare(sql: string, params?: BindParams): Statement;
|
|
54
|
-
export(): Uint8Array;
|
|
55
|
-
close(): void;
|
|
56
|
-
getRowsModified(): number;
|
|
57
|
-
create_function(
|
|
58
|
-
name: string,
|
|
59
|
-
func: (...args: (string | number | Uint8Array | null)[]) => unknown
|
|
60
|
-
): Database;
|
|
61
|
-
create_aggregate(
|
|
62
|
-
name: string,
|
|
63
|
-
init: () => unknown,
|
|
64
|
-
step: (state: unknown, value: unknown) => void,
|
|
65
|
-
finalize: (state: unknown) => unknown
|
|
66
|
-
): Database;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export default function initSqlJs(config?: SqlJsConfig): Promise<SqlJsStatic>;
|
|
70
|
-
}
|