@soleri/core 0.0.1 → 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.
Files changed (74) hide show
  1. package/dist/brain/brain.d.ts +85 -0
  2. package/dist/brain/brain.d.ts.map +1 -0
  3. package/dist/brain/brain.js +506 -0
  4. package/dist/brain/brain.js.map +1 -0
  5. package/dist/cognee/client.d.ts +35 -0
  6. package/dist/cognee/client.d.ts.map +1 -0
  7. package/dist/cognee/client.js +289 -0
  8. package/dist/cognee/client.js.map +1 -0
  9. package/dist/cognee/types.d.ts +46 -0
  10. package/dist/cognee/types.d.ts.map +1 -0
  11. package/dist/cognee/types.js +3 -0
  12. package/dist/cognee/types.js.map +1 -0
  13. package/dist/facades/facade-factory.d.ts +5 -0
  14. package/dist/facades/facade-factory.d.ts.map +1 -0
  15. package/dist/facades/facade-factory.js +49 -0
  16. package/dist/facades/facade-factory.js.map +1 -0
  17. package/dist/facades/types.d.ts +42 -0
  18. package/dist/facades/types.d.ts.map +1 -0
  19. package/dist/facades/types.js +6 -0
  20. package/dist/facades/types.js.map +1 -0
  21. package/dist/index.d.ts +19 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +19 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/intelligence/loader.d.ts +3 -0
  26. package/dist/intelligence/loader.d.ts.map +1 -0
  27. package/dist/intelligence/loader.js +41 -0
  28. package/dist/intelligence/loader.js.map +1 -0
  29. package/dist/intelligence/types.d.ts +20 -0
  30. package/dist/intelligence/types.d.ts.map +1 -0
  31. package/dist/intelligence/types.js +2 -0
  32. package/dist/intelligence/types.js.map +1 -0
  33. package/dist/llm/key-pool.d.ts +38 -0
  34. package/dist/llm/key-pool.d.ts.map +1 -0
  35. package/dist/llm/key-pool.js +154 -0
  36. package/dist/llm/key-pool.js.map +1 -0
  37. package/dist/llm/types.d.ts +80 -0
  38. package/dist/llm/types.d.ts.map +1 -0
  39. package/dist/llm/types.js +37 -0
  40. package/dist/llm/types.js.map +1 -0
  41. package/dist/llm/utils.d.ts +26 -0
  42. package/dist/llm/utils.d.ts.map +1 -0
  43. package/dist/llm/utils.js +197 -0
  44. package/dist/llm/utils.js.map +1 -0
  45. package/dist/planning/planner.d.ts +48 -0
  46. package/dist/planning/planner.d.ts.map +1 -0
  47. package/dist/planning/planner.js +109 -0
  48. package/dist/planning/planner.js.map +1 -0
  49. package/dist/vault/vault.d.ts +80 -0
  50. package/dist/vault/vault.d.ts.map +1 -0
  51. package/dist/vault/vault.js +353 -0
  52. package/dist/vault/vault.js.map +1 -0
  53. package/package.json +56 -4
  54. package/src/__tests__/brain.test.ts +740 -0
  55. package/src/__tests__/cognee-client.test.ts +524 -0
  56. package/src/__tests__/llm.test.ts +556 -0
  57. package/src/__tests__/loader.test.ts +176 -0
  58. package/src/__tests__/planner.test.ts +261 -0
  59. package/src/__tests__/vault.test.ts +494 -0
  60. package/src/brain/brain.ts +678 -0
  61. package/src/cognee/client.ts +350 -0
  62. package/src/cognee/types.ts +62 -0
  63. package/src/facades/facade-factory.ts +64 -0
  64. package/src/facades/types.ts +42 -0
  65. package/src/index.ts +75 -0
  66. package/src/intelligence/loader.ts +42 -0
  67. package/src/intelligence/types.ts +20 -0
  68. package/src/llm/key-pool.ts +190 -0
  69. package/src/llm/types.ts +116 -0
  70. package/src/llm/utils.ts +248 -0
  71. package/src/planning/planner.ts +151 -0
  72. package/src/vault/vault.ts +455 -0
  73. package/tsconfig.json +22 -0
  74. package/vitest.config.ts +15 -0
@@ -0,0 +1,494 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { Vault } from '../vault/vault.js';
3
+ import type { IntelligenceEntry } from '../intelligence/types.js';
4
+
5
+ function makeEntry(overrides: Partial<IntelligenceEntry> = {}): IntelligenceEntry {
6
+ return {
7
+ id: overrides.id ?? 'test-entry-1',
8
+ type: overrides.type ?? 'pattern',
9
+ domain: overrides.domain ?? 'testing',
10
+ title: overrides.title ?? 'Test Pattern',
11
+ severity: overrides.severity ?? 'warning',
12
+ description: overrides.description ?? 'A test pattern for unit tests.',
13
+ context: overrides.context ?? 'Use in test suites.',
14
+ example: overrides.example ?? 'expect(result).toBe(true);',
15
+ counterExample: overrides.counterExample ?? 'assert(result);',
16
+ why: overrides.why ?? 'Tests should be explicit about expectations.',
17
+ tags: overrides.tags ?? ['testing', 'assertions'],
18
+ appliesTo: overrides.appliesTo ?? ['*.test.ts'],
19
+ };
20
+ }
21
+
22
+ describe('Vault', () => {
23
+ let vault: Vault;
24
+
25
+ beforeEach(() => {
26
+ vault = new Vault(':memory:');
27
+ });
28
+
29
+ afterEach(() => {
30
+ vault.close();
31
+ });
32
+
33
+ describe('constructor', () => {
34
+ it('should create an in-memory vault', () => {
35
+ const stats = vault.stats();
36
+ expect(stats.totalEntries).toBe(0);
37
+ });
38
+
39
+ it('should expose db via getDb()', () => {
40
+ const db = vault.getDb();
41
+ expect(db).toBeDefined();
42
+ const row = db.prepare('SELECT COUNT(*) as count FROM brain_vocabulary').get() as {
43
+ count: number;
44
+ };
45
+ expect(row.count).toBe(0);
46
+ });
47
+ });
48
+
49
+ describe('seed', () => {
50
+ it('should seed entries and return count', () => {
51
+ const entries = [makeEntry({ id: 'e1' }), makeEntry({ id: 'e2' })];
52
+ const count = vault.seed(entries);
53
+ expect(count).toBe(2);
54
+ expect(vault.stats().totalEntries).toBe(2);
55
+ });
56
+
57
+ it('should upsert on duplicate id', () => {
58
+ vault.seed([makeEntry({ id: 'e1', title: 'Original' })]);
59
+ vault.seed([makeEntry({ id: 'e1', title: 'Updated' })]);
60
+ expect(vault.stats().totalEntries).toBe(1);
61
+ const entry = vault.get('e1');
62
+ expect(entry?.title).toBe('Updated');
63
+ });
64
+
65
+ it('should handle empty array', () => {
66
+ const count = vault.seed([]);
67
+ expect(count).toBe(0);
68
+ });
69
+ });
70
+
71
+ describe('search', () => {
72
+ beforeEach(() => {
73
+ vault.seed([
74
+ makeEntry({
75
+ id: 'search-1',
76
+ title: 'Input validation pattern',
77
+ description: 'Always validate user input at boundaries.',
78
+ domain: 'security',
79
+ tags: ['validation'],
80
+ }),
81
+ makeEntry({
82
+ id: 'search-2',
83
+ title: 'Caching strategy',
84
+ description: 'Use cache-aside for read-heavy workloads.',
85
+ domain: 'performance',
86
+ tags: ['caching'],
87
+ }),
88
+ makeEntry({
89
+ id: 'search-3',
90
+ title: 'Error handling pattern',
91
+ description: 'Use typed errors with context for debugging.',
92
+ domain: 'clean-code',
93
+ tags: ['errors'],
94
+ }),
95
+ ]);
96
+ });
97
+
98
+ it('should find entries matching query', () => {
99
+ const results = vault.search('validation input');
100
+ expect(results.length).toBeGreaterThan(0);
101
+ expect(results[0].entry.id).toBe('search-1');
102
+ });
103
+
104
+ it('should return scores with results', () => {
105
+ const results = vault.search('caching');
106
+ expect(results[0].score).toBeGreaterThan(0);
107
+ });
108
+
109
+ it('should filter by domain', () => {
110
+ const results = vault.search('pattern', { domain: 'security' });
111
+ expect(results.every((r) => r.entry.domain === 'security')).toBe(true);
112
+ });
113
+
114
+ it('should respect limit', () => {
115
+ const results = vault.search('pattern', { limit: 1 });
116
+ expect(results.length).toBeLessThanOrEqual(1);
117
+ });
118
+
119
+ it('should return empty for no matches', () => {
120
+ const results = vault.search('xyznonexistent');
121
+ expect(results).toEqual([]);
122
+ });
123
+ });
124
+
125
+ describe('get', () => {
126
+ it('should return entry by id', () => {
127
+ vault.seed([makeEntry({ id: 'get-1', title: 'Get Test' })]);
128
+ const entry = vault.get('get-1');
129
+ expect(entry).not.toBeNull();
130
+ expect(entry!.title).toBe('Get Test');
131
+ });
132
+
133
+ it('should return null for missing id', () => {
134
+ expect(vault.get('nonexistent')).toBeNull();
135
+ });
136
+
137
+ it('should preserve all fields', () => {
138
+ const original = makeEntry({ id: 'full-1' });
139
+ vault.seed([original]);
140
+ const entry = vault.get('full-1')!;
141
+ expect(entry.id).toBe(original.id);
142
+ expect(entry.type).toBe(original.type);
143
+ expect(entry.domain).toBe(original.domain);
144
+ expect(entry.tags).toEqual(original.tags);
145
+ });
146
+ });
147
+
148
+ describe('list', () => {
149
+ beforeEach(() => {
150
+ vault.seed([
151
+ makeEntry({ id: 'l1', domain: 'api', type: 'pattern', severity: 'critical' }),
152
+ makeEntry({ id: 'l2', domain: 'api', type: 'anti-pattern', severity: 'warning' }),
153
+ makeEntry({ id: 'l3', domain: 'db', type: 'rule', severity: 'suggestion' }),
154
+ makeEntry({
155
+ id: 'l4',
156
+ domain: 'db',
157
+ type: 'pattern',
158
+ severity: 'critical',
159
+ tags: ['indexing', 'query'],
160
+ }),
161
+ ]);
162
+ });
163
+
164
+ it('should list all entries', () => {
165
+ const entries = vault.list();
166
+ expect(entries).toHaveLength(4);
167
+ });
168
+
169
+ it('should filter by domain', () => {
170
+ const entries = vault.list({ domain: 'api' });
171
+ expect(entries).toHaveLength(2);
172
+ expect(entries.every((e) => e.domain === 'api')).toBe(true);
173
+ });
174
+
175
+ it('should filter by type', () => {
176
+ const entries = vault.list({ type: 'pattern' });
177
+ expect(entries).toHaveLength(2);
178
+ });
179
+
180
+ it('should filter by severity', () => {
181
+ const entries = vault.list({ severity: 'critical' });
182
+ expect(entries).toHaveLength(2);
183
+ });
184
+
185
+ it('should filter by tags', () => {
186
+ const entries = vault.list({ tags: ['indexing'] });
187
+ expect(entries).toHaveLength(1);
188
+ expect(entries[0].id).toBe('l4');
189
+ });
190
+
191
+ it('should support limit and offset', () => {
192
+ const page1 = vault.list({ limit: 2, offset: 0 });
193
+ const page2 = vault.list({ limit: 2, offset: 2 });
194
+ expect(page1).toHaveLength(2);
195
+ expect(page2).toHaveLength(2);
196
+ expect(page1[0].id).not.toBe(page2[0].id);
197
+ });
198
+ });
199
+
200
+ describe('stats', () => {
201
+ it('should return zero counts for empty vault', () => {
202
+ const stats = vault.stats();
203
+ expect(stats.totalEntries).toBe(0);
204
+ expect(stats.byType).toEqual({});
205
+ expect(stats.byDomain).toEqual({});
206
+ expect(stats.bySeverity).toEqual({});
207
+ });
208
+
209
+ it('should return correct breakdowns', () => {
210
+ vault.seed([
211
+ makeEntry({ id: 's1', domain: 'api', type: 'pattern', severity: 'critical' }),
212
+ makeEntry({ id: 's2', domain: 'api', type: 'rule', severity: 'warning' }),
213
+ makeEntry({ id: 's3', domain: 'db', type: 'pattern', severity: 'critical' }),
214
+ ]);
215
+ const stats = vault.stats();
216
+ expect(stats.totalEntries).toBe(3);
217
+ expect(stats.byDomain).toEqual({ api: 2, db: 1 });
218
+ expect(stats.byType).toEqual({ pattern: 2, rule: 1 });
219
+ expect(stats.bySeverity).toEqual({ critical: 2, warning: 1 });
220
+ });
221
+ });
222
+
223
+ describe('add', () => {
224
+ it('should add a single entry', () => {
225
+ vault.add(makeEntry({ id: 'add-1' }));
226
+ expect(vault.stats().totalEntries).toBe(1);
227
+ expect(vault.get('add-1')).not.toBeNull();
228
+ });
229
+ });
230
+
231
+ describe('remove', () => {
232
+ it('should remove an existing entry', () => {
233
+ vault.seed([makeEntry({ id: 'rm-1' })]);
234
+ const removed = vault.remove('rm-1');
235
+ expect(removed).toBe(true);
236
+ expect(vault.get('rm-1')).toBeNull();
237
+ expect(vault.stats().totalEntries).toBe(0);
238
+ });
239
+
240
+ it('should return false for nonexistent id', () => {
241
+ expect(vault.remove('nonexistent')).toBe(false);
242
+ });
243
+ });
244
+
245
+ describe('registerProject', () => {
246
+ it('should register a new project', () => {
247
+ const project = vault.registerProject('/home/user/my-project', 'my-project');
248
+ expect(project.path).toBe('/home/user/my-project');
249
+ expect(project.name).toBe('my-project');
250
+ expect(project.sessionCount).toBe(1);
251
+ });
252
+
253
+ it('should derive name from path when not provided', () => {
254
+ const project = vault.registerProject('/home/user/cool-app');
255
+ expect(project.name).toBe('cool-app');
256
+ });
257
+
258
+ it('should increment session count on re-registration', () => {
259
+ vault.registerProject('/home/user/app');
260
+ const second = vault.registerProject('/home/user/app');
261
+ expect(second.sessionCount).toBe(2);
262
+ const third = vault.registerProject('/home/user/app');
263
+ expect(third.sessionCount).toBe(3);
264
+ });
265
+
266
+ it('should update last_seen_at on re-registration', () => {
267
+ const first = vault.registerProject('/home/user/app');
268
+ const second = vault.registerProject('/home/user/app');
269
+ expect(second.lastSeenAt).toBeGreaterThanOrEqual(first.lastSeenAt);
270
+ });
271
+ });
272
+
273
+ describe('getProject', () => {
274
+ it('should return null for unregistered project', () => {
275
+ expect(vault.getProject('/nonexistent')).toBeNull();
276
+ });
277
+
278
+ it('should return registered project', () => {
279
+ vault.registerProject('/home/user/app', 'app');
280
+ const project = vault.getProject('/home/user/app');
281
+ expect(project).not.toBeNull();
282
+ expect(project!.name).toBe('app');
283
+ });
284
+ });
285
+
286
+ describe('listProjects', () => {
287
+ it('should return empty array when no projects', () => {
288
+ expect(vault.listProjects()).toEqual([]);
289
+ });
290
+
291
+ it('should list all registered projects', () => {
292
+ vault.registerProject('/home/user/app-a', 'app-a');
293
+ vault.registerProject('/home/user/app-b', 'app-b');
294
+ const projects = vault.listProjects();
295
+ expect(projects).toHaveLength(2);
296
+ });
297
+ });
298
+
299
+ describe('captureMemory', () => {
300
+ it('should capture a memory and return it', () => {
301
+ const memory = vault.captureMemory({
302
+ projectPath: '/test',
303
+ type: 'lesson',
304
+ context: 'Debugging session',
305
+ summary: 'Learned about FTS5 tokenizers',
306
+ topics: ['sqlite', 'fts5'],
307
+ filesModified: ['vault.ts'],
308
+ toolsUsed: ['Bash'],
309
+ });
310
+ expect(memory.id).toMatch(/^mem-/);
311
+ expect(memory.type).toBe('lesson');
312
+ expect(memory.summary).toBe('Learned about FTS5 tokenizers');
313
+ expect(memory.topics).toEqual(['sqlite', 'fts5']);
314
+ expect(memory.archivedAt).toBeNull();
315
+ });
316
+
317
+ it('should capture session memories', () => {
318
+ const memory = vault.captureMemory({
319
+ projectPath: '/test',
320
+ type: 'session',
321
+ context: 'refactoring vault module',
322
+ summary: 'Refactored vault to use FTS5',
323
+ topics: ['vault'],
324
+ filesModified: [],
325
+ toolsUsed: [],
326
+ });
327
+ expect(memory.type).toBe('session');
328
+ expect(memory.createdAt).toBeGreaterThan(0);
329
+ });
330
+
331
+ it('should capture preference memories', () => {
332
+ const memory = vault.captureMemory({
333
+ projectPath: '/test',
334
+ type: 'preference',
335
+ context: 'user prefers bun over npm',
336
+ summary: 'Use bun for package management',
337
+ topics: ['tooling'],
338
+ filesModified: [],
339
+ toolsUsed: [],
340
+ });
341
+ expect(memory.type).toBe('preference');
342
+ });
343
+ });
344
+
345
+ describe('searchMemories', () => {
346
+ beforeEach(() => {
347
+ vault.captureMemory({
348
+ projectPath: '/test',
349
+ type: 'lesson',
350
+ context: 'Debugging SQL queries',
351
+ summary: 'Always use parameterized queries to prevent injection',
352
+ topics: ['sql', 'security'],
353
+ filesModified: [],
354
+ toolsUsed: [],
355
+ });
356
+ vault.captureMemory({
357
+ projectPath: '/test',
358
+ type: 'session',
359
+ context: 'Working on API design',
360
+ summary: 'Implemented REST endpoints with pagination',
361
+ topics: ['api', 'rest'],
362
+ filesModified: ['routes.ts'],
363
+ toolsUsed: ['Edit'],
364
+ });
365
+ vault.captureMemory({
366
+ projectPath: '/other',
367
+ type: 'preference',
368
+ context: 'User likes TypeScript strict mode',
369
+ summary: 'Always enable strict mode in tsconfig',
370
+ topics: ['typescript'],
371
+ filesModified: [],
372
+ toolsUsed: [],
373
+ });
374
+ });
375
+
376
+ it('should find memories matching query', () => {
377
+ const results = vault.searchMemories('parameterized queries');
378
+ expect(results.length).toBeGreaterThan(0);
379
+ expect(results[0].summary).toContain('parameterized');
380
+ });
381
+
382
+ it('should filter by type', () => {
383
+ const results = vault.searchMemories('queries OR endpoints OR strict', { type: 'lesson' });
384
+ expect(results.every((m) => m.type === 'lesson')).toBe(true);
385
+ });
386
+
387
+ it('should filter by project path', () => {
388
+ const results = vault.searchMemories('queries OR endpoints OR strict', {
389
+ projectPath: '/other',
390
+ });
391
+ expect(results.every((m) => m.projectPath === '/other')).toBe(true);
392
+ });
393
+
394
+ it('should respect limit', () => {
395
+ const results = vault.searchMemories('queries OR endpoints OR strict', { limit: 1 });
396
+ expect(results.length).toBeLessThanOrEqual(1);
397
+ });
398
+
399
+ it('should return empty for no matches', () => {
400
+ const results = vault.searchMemories('xyznonexistent');
401
+ expect(results).toEqual([]);
402
+ });
403
+ });
404
+
405
+ describe('listMemories', () => {
406
+ beforeEach(() => {
407
+ vault.captureMemory({
408
+ projectPath: '/proj-a',
409
+ type: 'lesson',
410
+ context: 'ctx',
411
+ summary: 'lesson one',
412
+ topics: [],
413
+ filesModified: [],
414
+ toolsUsed: [],
415
+ });
416
+ vault.captureMemory({
417
+ projectPath: '/proj-a',
418
+ type: 'session',
419
+ context: 'ctx',
420
+ summary: 'session one',
421
+ topics: [],
422
+ filesModified: [],
423
+ toolsUsed: [],
424
+ });
425
+ vault.captureMemory({
426
+ projectPath: '/proj-b',
427
+ type: 'preference',
428
+ context: 'ctx',
429
+ summary: 'pref one',
430
+ topics: [],
431
+ filesModified: [],
432
+ toolsUsed: [],
433
+ });
434
+ });
435
+
436
+ it('should list all memories', () => {
437
+ const memories = vault.listMemories();
438
+ expect(memories).toHaveLength(3);
439
+ });
440
+
441
+ it('should filter by type', () => {
442
+ const memories = vault.listMemories({ type: 'lesson' });
443
+ expect(memories).toHaveLength(1);
444
+ expect(memories[0].type).toBe('lesson');
445
+ });
446
+
447
+ it('should filter by project path', () => {
448
+ const memories = vault.listMemories({ projectPath: '/proj-a' });
449
+ expect(memories).toHaveLength(2);
450
+ });
451
+ });
452
+
453
+ describe('memoryStats', () => {
454
+ it('should return zero counts for empty memories', () => {
455
+ const stats = vault.memoryStats();
456
+ expect(stats.total).toBe(0);
457
+ expect(stats.byType).toEqual({});
458
+ });
459
+
460
+ it('should return correct breakdown', () => {
461
+ vault.captureMemory({
462
+ projectPath: '/a',
463
+ type: 'lesson',
464
+ context: 'ctx',
465
+ summary: 's',
466
+ topics: [],
467
+ filesModified: [],
468
+ toolsUsed: [],
469
+ });
470
+ vault.captureMemory({
471
+ projectPath: '/a',
472
+ type: 'lesson',
473
+ context: 'ctx',
474
+ summary: 's',
475
+ topics: [],
476
+ filesModified: [],
477
+ toolsUsed: [],
478
+ });
479
+ vault.captureMemory({
480
+ projectPath: '/b',
481
+ type: 'session',
482
+ context: 'ctx',
483
+ summary: 's',
484
+ topics: [],
485
+ filesModified: [],
486
+ toolsUsed: [],
487
+ });
488
+ const stats = vault.memoryStats();
489
+ expect(stats.total).toBe(3);
490
+ expect(stats.byType).toEqual({ lesson: 2, session: 1 });
491
+ expect(stats.byProject).toEqual({ '/a': 2, '/b': 1 });
492
+ });
493
+ });
494
+ });