@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,176 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { loadIntelligenceData } from '../intelligence/loader.js';
3
+ import { mkdirSync, writeFileSync, rmSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ import { tmpdir } from 'node:os';
6
+
7
+ describe('loadIntelligenceData', () => {
8
+ let tempDir: string;
9
+
10
+ beforeEach(() => {
11
+ tempDir = join(tmpdir(), `loader-test-${Date.now()}`);
12
+ mkdirSync(tempDir, { recursive: true });
13
+ });
14
+
15
+ afterEach(() => {
16
+ rmSync(tempDir, { recursive: true, force: true });
17
+ });
18
+
19
+ it('should load valid entries from JSON files', () => {
20
+ const bundle = {
21
+ domain: 'testing',
22
+ version: '1.0.0',
23
+ entries: [
24
+ {
25
+ id: 'test-1',
26
+ type: 'pattern',
27
+ domain: 'testing',
28
+ title: 'Test Pattern',
29
+ severity: 'warning',
30
+ description: 'A test pattern.',
31
+ tags: ['test'],
32
+ },
33
+ ],
34
+ };
35
+ writeFileSync(join(tempDir, 'testing.json'), JSON.stringify(bundle), 'utf-8');
36
+ const entries = loadIntelligenceData(tempDir);
37
+ expect(entries).toHaveLength(1);
38
+ expect(entries[0].id).toBe('test-1');
39
+ });
40
+
41
+ it('should skip invalid entries', () => {
42
+ const bundle = {
43
+ domain: 'testing',
44
+ version: '1.0.0',
45
+ entries: [
46
+ {
47
+ id: '',
48
+ type: 'pattern',
49
+ title: '',
50
+ severity: 'warning',
51
+ description: 'Missing fields',
52
+ tags: [],
53
+ },
54
+ {
55
+ id: 'valid-1',
56
+ type: 'pattern',
57
+ domain: 'testing',
58
+ title: 'Valid',
59
+ severity: 'warning',
60
+ description: 'Valid entry.',
61
+ tags: ['a'],
62
+ },
63
+ ],
64
+ };
65
+ writeFileSync(join(tempDir, 'mixed.json'), JSON.stringify(bundle), 'utf-8');
66
+ const entries = loadIntelligenceData(tempDir);
67
+ expect(entries).toHaveLength(1);
68
+ expect(entries[0].id).toBe('valid-1');
69
+ });
70
+
71
+ it('should return empty array for missing directory', () => {
72
+ const entries = loadIntelligenceData('/nonexistent/path');
73
+ expect(entries).toEqual([]);
74
+ });
75
+
76
+ it('should skip non-JSON files', () => {
77
+ writeFileSync(join(tempDir, 'readme.txt'), 'Not JSON', 'utf-8');
78
+ const entries = loadIntelligenceData(tempDir);
79
+ expect(entries).toEqual([]);
80
+ });
81
+
82
+ it('should handle malformed JSON gracefully', () => {
83
+ writeFileSync(join(tempDir, 'bad.json'), '{not valid json', 'utf-8');
84
+ const entries = loadIntelligenceData(tempDir);
85
+ expect(entries).toEqual([]);
86
+ });
87
+
88
+ it('should handle bundle without entries array', () => {
89
+ writeFileSync(
90
+ join(tempDir, 'empty.json'),
91
+ JSON.stringify({ domain: 'test', version: '1.0' }),
92
+ 'utf-8',
93
+ );
94
+ const entries = loadIntelligenceData(tempDir);
95
+ expect(entries).toEqual([]);
96
+ });
97
+
98
+ it('should load from multiple files', () => {
99
+ const bundle1 = {
100
+ domain: 'security',
101
+ version: '1.0.0',
102
+ entries: [
103
+ {
104
+ id: 'sec-1',
105
+ type: 'pattern',
106
+ domain: 'security',
107
+ title: 'Security Pattern',
108
+ severity: 'critical',
109
+ description: 'A security pattern.',
110
+ tags: ['security'],
111
+ },
112
+ ],
113
+ };
114
+ const bundle2 = {
115
+ domain: 'performance',
116
+ version: '1.0.0',
117
+ entries: [
118
+ {
119
+ id: 'perf-1',
120
+ type: 'rule',
121
+ domain: 'performance',
122
+ title: 'Performance Rule',
123
+ severity: 'suggestion',
124
+ description: 'A performance rule.',
125
+ tags: ['perf'],
126
+ },
127
+ ],
128
+ };
129
+ writeFileSync(join(tempDir, 'security.json'), JSON.stringify(bundle1), 'utf-8');
130
+ writeFileSync(join(tempDir, 'performance.json'), JSON.stringify(bundle2), 'utf-8');
131
+ const entries = loadIntelligenceData(tempDir);
132
+ expect(entries).toHaveLength(2);
133
+ });
134
+
135
+ it('should validate entry type field', () => {
136
+ const bundle = {
137
+ domain: 'testing',
138
+ version: '1.0.0',
139
+ entries: [
140
+ {
141
+ id: 'bad-type',
142
+ type: 'invalid-type',
143
+ domain: 'testing',
144
+ title: 'Bad Type',
145
+ severity: 'warning',
146
+ description: 'Invalid type.',
147
+ tags: [],
148
+ },
149
+ ],
150
+ };
151
+ writeFileSync(join(tempDir, 'bad-type.json'), JSON.stringify(bundle), 'utf-8');
152
+ const entries = loadIntelligenceData(tempDir);
153
+ expect(entries).toHaveLength(0);
154
+ });
155
+
156
+ it('should validate entry severity field', () => {
157
+ const bundle = {
158
+ domain: 'testing',
159
+ version: '1.0.0',
160
+ entries: [
161
+ {
162
+ id: 'bad-sev',
163
+ type: 'pattern',
164
+ domain: 'testing',
165
+ title: 'Bad Severity',
166
+ severity: 'invalid',
167
+ description: 'Invalid severity.',
168
+ tags: [],
169
+ },
170
+ ],
171
+ };
172
+ writeFileSync(join(tempDir, 'bad-sev.json'), JSON.stringify(bundle), 'utf-8');
173
+ const entries = loadIntelligenceData(tempDir);
174
+ expect(entries).toHaveLength(0);
175
+ });
176
+ });
@@ -0,0 +1,261 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { Planner } from '../planning/planner.js';
3
+ import { mkdirSync, rmSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ import { tmpdir } from 'node:os';
6
+
7
+ describe('Planner', () => {
8
+ let tempDir: string;
9
+ let planner: Planner;
10
+
11
+ beforeEach(() => {
12
+ tempDir = join(tmpdir(), `planner-test-${Date.now()}`);
13
+ mkdirSync(tempDir, { recursive: true });
14
+ planner = new Planner(join(tempDir, 'plans.json'));
15
+ });
16
+
17
+ afterEach(() => {
18
+ rmSync(tempDir, { recursive: true, force: true });
19
+ });
20
+
21
+ describe('create', () => {
22
+ it('should create a plan in draft status', () => {
23
+ const plan = planner.create({ objective: 'Add auth', scope: 'backend' });
24
+ expect(plan.id).toMatch(/^plan-/);
25
+ expect(plan.status).toBe('draft');
26
+ expect(plan.objective).toBe('Add auth');
27
+ expect(plan.scope).toBe('backend');
28
+ });
29
+
30
+ it('should create a plan with tasks', () => {
31
+ const plan = planner.create({
32
+ objective: 'Add auth',
33
+ scope: 'backend',
34
+ tasks: [
35
+ { title: 'Add JWT', description: 'Implement JWT signing' },
36
+ { title: 'Add middleware', description: 'Auth middleware' },
37
+ ],
38
+ });
39
+ expect(plan.tasks).toHaveLength(2);
40
+ expect(plan.tasks[0].id).toBe('task-1');
41
+ expect(plan.tasks[0].status).toBe('pending');
42
+ expect(plan.tasks[1].id).toBe('task-2');
43
+ });
44
+
45
+ it('should create a plan with decisions', () => {
46
+ const plan = planner.create({
47
+ objective: 'Add caching',
48
+ scope: 'api',
49
+ decisions: ['Use Redis', 'TTL of 5 minutes'],
50
+ });
51
+ expect(plan.decisions).toEqual(['Use Redis', 'TTL of 5 minutes']);
52
+ });
53
+
54
+ it('should persist plan to disk', () => {
55
+ planner.create({ objective: 'Test persistence', scope: 'test' });
56
+ const planner2 = new Planner(join(tempDir, 'plans.json'));
57
+ expect(planner2.list()).toHaveLength(1);
58
+ });
59
+ });
60
+
61
+ describe('get', () => {
62
+ it('should return a plan by id', () => {
63
+ const created = planner.create({ objective: 'Find me', scope: 'test' });
64
+ const found = planner.get(created.id);
65
+ expect(found).not.toBeNull();
66
+ expect(found!.objective).toBe('Find me');
67
+ });
68
+
69
+ it('should return null for unknown id', () => {
70
+ expect(planner.get('plan-nonexistent')).toBeNull();
71
+ });
72
+ });
73
+
74
+ describe('list', () => {
75
+ it('should list all plans', () => {
76
+ planner.create({ objective: 'Plan A', scope: 'a' });
77
+ planner.create({ objective: 'Plan B', scope: 'b' });
78
+ expect(planner.list()).toHaveLength(2);
79
+ });
80
+
81
+ it('should return empty array when no plans', () => {
82
+ expect(planner.list()).toEqual([]);
83
+ });
84
+ });
85
+
86
+ describe('approve', () => {
87
+ it('should transition draft to approved', () => {
88
+ const plan = planner.create({ objective: 'Approve me', scope: 'test' });
89
+ const approved = planner.approve(plan.id);
90
+ expect(approved.status).toBe('approved');
91
+ });
92
+
93
+ it('should throw when approving non-draft plan', () => {
94
+ const plan = planner.create({ objective: 'Already approved', scope: 'test' });
95
+ planner.approve(plan.id);
96
+ expect(() => planner.approve(plan.id)).toThrow('must be');
97
+ });
98
+
99
+ it('should throw for unknown plan', () => {
100
+ expect(() => planner.approve('plan-xxx')).toThrow('not found');
101
+ });
102
+ });
103
+
104
+ describe('startExecution', () => {
105
+ it('should transition approved to executing', () => {
106
+ const plan = planner.create({ objective: 'Execute me', scope: 'test' });
107
+ planner.approve(plan.id);
108
+ const executing = planner.startExecution(plan.id);
109
+ expect(executing.status).toBe('executing');
110
+ });
111
+
112
+ it('should throw when executing non-approved plan', () => {
113
+ const plan = planner.create({ objective: 'Not approved', scope: 'test' });
114
+ expect(() => planner.startExecution(plan.id)).toThrow('must be');
115
+ });
116
+ });
117
+
118
+ describe('updateTask', () => {
119
+ it('should update task status on executing plan', () => {
120
+ const plan = planner.create({
121
+ objective: 'Task test',
122
+ scope: 'test',
123
+ tasks: [{ title: 'Task 1', description: 'Do thing' }],
124
+ });
125
+ planner.approve(plan.id);
126
+ planner.startExecution(plan.id);
127
+ const updated = planner.updateTask(plan.id, 'task-1', 'in_progress');
128
+ expect(updated.tasks[0].status).toBe('in_progress');
129
+ });
130
+
131
+ it('should support all task statuses', () => {
132
+ const plan = planner.create({
133
+ objective: 'Status test',
134
+ scope: 'test',
135
+ tasks: [
136
+ { title: 'T1', description: 'd' },
137
+ { title: 'T2', description: 'd' },
138
+ { title: 'T3', description: 'd' },
139
+ { title: 'T4', description: 'd' },
140
+ { title: 'T5', description: 'd' },
141
+ ],
142
+ });
143
+ planner.approve(plan.id);
144
+ planner.startExecution(plan.id);
145
+ planner.updateTask(plan.id, 'task-1', 'completed');
146
+ planner.updateTask(plan.id, 'task-2', 'skipped');
147
+ planner.updateTask(plan.id, 'task-3', 'failed');
148
+ planner.updateTask(plan.id, 'task-4', 'in_progress');
149
+ const result = planner.get(plan.id)!;
150
+ expect(result.tasks[0].status).toBe('completed');
151
+ expect(result.tasks[1].status).toBe('skipped');
152
+ expect(result.tasks[2].status).toBe('failed');
153
+ expect(result.tasks[3].status).toBe('in_progress');
154
+ expect(result.tasks[4].status).toBe('pending');
155
+ });
156
+
157
+ it('should throw when updating tasks on non-executing plan', () => {
158
+ const plan = planner.create({
159
+ objective: 'Not executing',
160
+ scope: 'test',
161
+ tasks: [{ title: 'T1', description: 'd' }],
162
+ });
163
+ expect(() => planner.updateTask(plan.id, 'task-1', 'completed')).toThrow('must be');
164
+ });
165
+
166
+ it('should throw for unknown task', () => {
167
+ const plan = planner.create({
168
+ objective: 'Unknown task',
169
+ scope: 'test',
170
+ tasks: [{ title: 'T1', description: 'd' }],
171
+ });
172
+ planner.approve(plan.id);
173
+ planner.startExecution(plan.id);
174
+ expect(() => planner.updateTask(plan.id, 'task-99', 'completed')).toThrow('not found');
175
+ });
176
+ });
177
+
178
+ describe('complete', () => {
179
+ it('should transition executing to completed', () => {
180
+ const plan = planner.create({ objective: 'Complete me', scope: 'test' });
181
+ planner.approve(plan.id);
182
+ planner.startExecution(plan.id);
183
+ const completed = planner.complete(plan.id);
184
+ expect(completed.status).toBe('completed');
185
+ });
186
+
187
+ it('should throw when completing non-executing plan', () => {
188
+ const plan = planner.create({ objective: 'Not executing', scope: 'test' });
189
+ expect(() => planner.complete(plan.id)).toThrow('must be');
190
+ });
191
+ });
192
+
193
+ describe('getExecuting', () => {
194
+ it('should return only executing plans', () => {
195
+ const p1 = planner.create({ objective: 'Executing', scope: 'a' });
196
+ planner.create({ objective: 'Draft', scope: 'b' });
197
+ planner.approve(p1.id);
198
+ planner.startExecution(p1.id);
199
+ const executing = planner.getExecuting();
200
+ expect(executing).toHaveLength(1);
201
+ expect(executing[0].objective).toBe('Executing');
202
+ });
203
+
204
+ it('should return empty when nothing is executing', () => {
205
+ planner.create({ objective: 'Draft only', scope: 'test' });
206
+ expect(planner.getExecuting()).toEqual([]);
207
+ });
208
+ });
209
+
210
+ describe('getActive', () => {
211
+ it('should return draft, approved, and executing plans', () => {
212
+ planner.create({ objective: 'Draft', scope: 'a' });
213
+ const p2 = planner.create({ objective: 'Approved', scope: 'b' });
214
+ const p3 = planner.create({ objective: 'Executing', scope: 'c' });
215
+ const p4 = planner.create({ objective: 'Completed', scope: 'd' });
216
+ planner.approve(p2.id);
217
+ planner.approve(p3.id);
218
+ planner.startExecution(p3.id);
219
+ planner.approve(p4.id);
220
+ planner.startExecution(p4.id);
221
+ planner.complete(p4.id);
222
+ const active = planner.getActive();
223
+ expect(active).toHaveLength(3);
224
+ expect(active.map((p) => p.status).sort()).toEqual(['approved', 'draft', 'executing']);
225
+ });
226
+ });
227
+
228
+ describe('full lifecycle', () => {
229
+ it('should support draft → approved → executing → completed with tasks', () => {
230
+ const plan = planner.create({
231
+ objective: 'Full lifecycle test',
232
+ scope: 'integration',
233
+ decisions: ['Use TDD'],
234
+ tasks: [
235
+ { title: 'Write tests', description: 'Write failing tests first' },
236
+ { title: 'Implement', description: 'Make tests pass' },
237
+ { title: 'Refactor', description: 'Clean up' },
238
+ ],
239
+ });
240
+ expect(plan.status).toBe('draft');
241
+
242
+ planner.approve(plan.id);
243
+ expect(planner.get(plan.id)!.status).toBe('approved');
244
+
245
+ planner.startExecution(plan.id);
246
+ expect(planner.get(plan.id)!.status).toBe('executing');
247
+
248
+ planner.updateTask(plan.id, 'task-1', 'in_progress');
249
+ planner.updateTask(plan.id, 'task-1', 'completed');
250
+ planner.updateTask(plan.id, 'task-2', 'in_progress');
251
+ planner.updateTask(plan.id, 'task-2', 'completed');
252
+ planner.updateTask(plan.id, 'task-3', 'skipped');
253
+
254
+ const final = planner.complete(plan.id);
255
+ expect(final.status).toBe('completed');
256
+ expect(final.tasks[0].status).toBe('completed');
257
+ expect(final.tasks[1].status).toBe('completed');
258
+ expect(final.tasks[2].status).toBe('skipped');
259
+ });
260
+ });
261
+ });