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