@soleri/forge 4.2.0 → 4.2.1
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.
|
@@ -42,6 +42,8 @@ describe('Facades', () => {
|
|
|
42
42
|
let openaiKeyPool: KeyPool;
|
|
43
43
|
let anthropicKeyPool: KeyPool;
|
|
44
44
|
let llmClient: LLMClient;
|
|
45
|
+
const makeCoreFacade = () =>
|
|
46
|
+
createCoreFacade(vault, planner, brain, undefined, llmClient, openaiKeyPool, anthropicKeyPool);
|
|
45
47
|
|
|
46
48
|
beforeEach(() => {
|
|
47
49
|
vault = new Vault(':memory:');
|
|
@@ -63,7 +65,7 @@ ${domainDescribes}
|
|
|
63
65
|
|
|
64
66
|
describe('${config.id}_core', () => {
|
|
65
67
|
it('should create core facade with expected ops', () => {
|
|
66
|
-
const facade =
|
|
68
|
+
const facade = makeCoreFacade();
|
|
67
69
|
expect(facade.name).toBe('${config.id}_core');
|
|
68
70
|
const opNames = facade.ops.map((o) => o.name);
|
|
69
71
|
expect(opNames).toContain('search');
|
|
@@ -84,7 +86,7 @@ ${domainDescribes}
|
|
|
84
86
|
makeEntry({ id: 'c2', domain: 'beta', title: 'Beta pattern', tags: ['b'] }),
|
|
85
87
|
]);
|
|
86
88
|
brain = new Brain(vault);
|
|
87
|
-
const facade =
|
|
89
|
+
const facade = makeCoreFacade();
|
|
88
90
|
const searchOp = facade.ops.find((o) => o.name === 'search')!;
|
|
89
91
|
const results = (await searchOp.handler({ query: 'pattern' })) as Array<{ entry: unknown; score: number; breakdown: unknown }>;
|
|
90
92
|
expect(Array.isArray(results)).toBe(true);
|
|
@@ -98,21 +100,21 @@ ${domainDescribes}
|
|
|
98
100
|
makeEntry({ id: 'vs1', domain: 'd1', tags: ['x'] }),
|
|
99
101
|
makeEntry({ id: 'vs2', domain: 'd2', tags: ['y'] }),
|
|
100
102
|
]);
|
|
101
|
-
const facade =
|
|
103
|
+
const facade = makeCoreFacade();
|
|
102
104
|
const statsOp = facade.ops.find((o) => o.name === 'vault_stats')!;
|
|
103
105
|
const stats = (await statsOp.handler({})) as { totalEntries: number };
|
|
104
106
|
expect(stats.totalEntries).toBe(2);
|
|
105
107
|
});
|
|
106
108
|
|
|
107
109
|
it('health should return ok status', async () => {
|
|
108
|
-
const facade =
|
|
110
|
+
const facade = makeCoreFacade();
|
|
109
111
|
const healthOp = facade.ops.find((o) => o.name === 'health')!;
|
|
110
112
|
const health = (await healthOp.handler({})) as { status: string };
|
|
111
113
|
expect(health.status).toBe('ok');
|
|
112
114
|
});
|
|
113
115
|
|
|
114
116
|
it('identity should return persona', async () => {
|
|
115
|
-
const facade =
|
|
117
|
+
const facade = makeCoreFacade();
|
|
116
118
|
const identityOp = facade.ops.find((o) => o.name === 'identity')!;
|
|
117
119
|
const persona = (await identityOp.handler({})) as { name: string; role: string };
|
|
118
120
|
expect(persona.name).toBe('${escapeQuotes(config.name)}');
|
|
@@ -124,14 +126,14 @@ ${domainDescribes}
|
|
|
124
126
|
makeEntry({ id: 'la1', domain: 'alpha', tags: ['a'] }),
|
|
125
127
|
makeEntry({ id: 'la2', domain: 'beta', tags: ['b'] }),
|
|
126
128
|
]);
|
|
127
|
-
const facade =
|
|
129
|
+
const facade = makeCoreFacade();
|
|
128
130
|
const listOp = facade.ops.find((o) => o.name === 'list_all')!;
|
|
129
131
|
const filtered = (await listOp.handler({ domain: 'alpha' })) as unknown[];
|
|
130
132
|
expect(filtered).toHaveLength(1);
|
|
131
133
|
});
|
|
132
134
|
|
|
133
135
|
it('activate should return persona and setup status', async () => {
|
|
134
|
-
const facade =
|
|
136
|
+
const facade = makeCoreFacade();
|
|
135
137
|
const activateOp = facade.ops.find((o) => o.name === 'activate')!;
|
|
136
138
|
const result = (await activateOp.handler({ projectPath: '/tmp/nonexistent-test' })) as {
|
|
137
139
|
activated: boolean;
|
|
@@ -150,7 +152,7 @@ ${domainDescribes}
|
|
|
150
152
|
});
|
|
151
153
|
|
|
152
154
|
it('activate with deactivate flag should return deactivation', async () => {
|
|
153
|
-
const facade =
|
|
155
|
+
const facade = makeCoreFacade();
|
|
154
156
|
const activateOp = facade.ops.find((o) => o.name === 'activate')!;
|
|
155
157
|
const result = (await activateOp.handler({ deactivate: true })) as { deactivated: boolean; message: string };
|
|
156
158
|
expect(result.deactivated).toBe(true);
|
|
@@ -161,7 +163,7 @@ ${domainDescribes}
|
|
|
161
163
|
const tempDir = join(tmpdir(), 'forge-inject-test-' + Date.now());
|
|
162
164
|
mkdirSync(tempDir, { recursive: true });
|
|
163
165
|
try {
|
|
164
|
-
const facade =
|
|
166
|
+
const facade = makeCoreFacade();
|
|
165
167
|
const injectOp = facade.ops.find((o) => o.name === 'inject_claude_md')!;
|
|
166
168
|
const result = (await injectOp.handler({ projectPath: tempDir })) as {
|
|
167
169
|
injected: boolean;
|
|
@@ -181,7 +183,7 @@ ${domainDescribes}
|
|
|
181
183
|
it('inject_claude_md with global flag should target ~/.claude/CLAUDE.md', async () => {
|
|
182
184
|
// We test the global path resolution by checking the returned path
|
|
183
185
|
// contains .claude/CLAUDE.md (actual write may or may not happen depending on env)
|
|
184
|
-
const facade =
|
|
186
|
+
const facade = makeCoreFacade();
|
|
185
187
|
const injectOp = facade.ops.find((o) => o.name === 'inject_claude_md')!;
|
|
186
188
|
const result = (await injectOp.handler({ global: true })) as {
|
|
187
189
|
injected: boolean;
|
|
@@ -194,7 +196,7 @@ ${domainDescribes}
|
|
|
194
196
|
});
|
|
195
197
|
|
|
196
198
|
it('setup should return project and global CLAUDE.md status', async () => {
|
|
197
|
-
const facade =
|
|
199
|
+
const facade = makeCoreFacade();
|
|
198
200
|
const setupOp = facade.ops.find((o) => o.name === 'setup')!;
|
|
199
201
|
const result = (await setupOp.handler({ projectPath: '/tmp/nonexistent-test' })) as {
|
|
200
202
|
agent: { name: string };
|
|
@@ -213,7 +215,7 @@ ${domainDescribes}
|
|
|
213
215
|
});
|
|
214
216
|
|
|
215
217
|
it('register should track new project', async () => {
|
|
216
|
-
const facade =
|
|
218
|
+
const facade = makeCoreFacade();
|
|
217
219
|
const registerOp = facade.ops.find((o) => o.name === 'register')!;
|
|
218
220
|
const result = (await registerOp.handler({ projectPath: '/tmp/reg-test-project', name: 'reg-test' })) as {
|
|
219
221
|
project: { path: string; name: string; sessionCount: number };
|
|
@@ -228,7 +230,7 @@ ${domainDescribes}
|
|
|
228
230
|
});
|
|
229
231
|
|
|
230
232
|
it('register should increment session count for returning project', async () => {
|
|
231
|
-
const facade =
|
|
233
|
+
const facade = makeCoreFacade();
|
|
232
234
|
const registerOp = facade.ops.find((o) => o.name === 'register')!;
|
|
233
235
|
await registerOp.handler({ projectPath: '/tmp/reg-test-returning', name: 'returning' });
|
|
234
236
|
const result = (await registerOp.handler({ projectPath: '/tmp/reg-test-returning', name: 'returning' })) as {
|
|
@@ -242,7 +244,7 @@ ${domainDescribes}
|
|
|
242
244
|
});
|
|
243
245
|
|
|
244
246
|
it('memory_capture should store a memory', async () => {
|
|
245
|
-
const facade =
|
|
247
|
+
const facade = makeCoreFacade();
|
|
246
248
|
const captureOp = facade.ops.find((o) => o.name === 'memory_capture')!;
|
|
247
249
|
const result = (await captureOp.handler({
|
|
248
250
|
projectPath: '/test',
|
|
@@ -259,7 +261,7 @@ ${domainDescribes}
|
|
|
259
261
|
});
|
|
260
262
|
|
|
261
263
|
it('memory_search should find captured memories', async () => {
|
|
262
|
-
const facade =
|
|
264
|
+
const facade = makeCoreFacade();
|
|
263
265
|
const captureOp = facade.ops.find((o) => o.name === 'memory_capture')!;
|
|
264
266
|
await captureOp.handler({
|
|
265
267
|
projectPath: '/test',
|
|
@@ -277,7 +279,7 @@ ${domainDescribes}
|
|
|
277
279
|
});
|
|
278
280
|
|
|
279
281
|
it('memory_list should return all memories with stats', async () => {
|
|
280
|
-
const facade =
|
|
282
|
+
const facade = makeCoreFacade();
|
|
281
283
|
const captureOp = facade.ops.find((o) => o.name === 'memory_capture')!;
|
|
282
284
|
await captureOp.handler({
|
|
283
285
|
projectPath: '/test',
|
|
@@ -298,7 +300,7 @@ ${domainDescribes}
|
|
|
298
300
|
});
|
|
299
301
|
|
|
300
302
|
it('session_capture should store a session memory', async () => {
|
|
301
|
-
const facade =
|
|
303
|
+
const facade = makeCoreFacade();
|
|
302
304
|
const sessionOp = facade.ops.find((o) => o.name === 'session_capture')!;
|
|
303
305
|
const result = (await sessionOp.handler({
|
|
304
306
|
projectPath: '/tmp/test-session',
|
|
@@ -319,7 +321,7 @@ ${domainDescribes}
|
|
|
319
321
|
makeEntry({ id: 'exp2', domain: 'security', tags: ['xss'] }),
|
|
320
322
|
makeEntry({ id: 'exp3', domain: 'api-design', tags: ['rest'] }),
|
|
321
323
|
]);
|
|
322
|
-
const facade =
|
|
324
|
+
const facade = makeCoreFacade();
|
|
323
325
|
const exportOp = facade.ops.find((o) => o.name === 'export')!;
|
|
324
326
|
const result = (await exportOp.handler({})) as {
|
|
325
327
|
exported: boolean;
|
|
@@ -339,7 +341,7 @@ ${domainDescribes}
|
|
|
339
341
|
makeEntry({ id: 'exp-d1', domain: 'security', tags: ['auth'] }),
|
|
340
342
|
makeEntry({ id: 'exp-d2', domain: 'api-design', tags: ['rest'] }),
|
|
341
343
|
]);
|
|
342
|
-
const facade =
|
|
344
|
+
const facade = makeCoreFacade();
|
|
343
345
|
const exportOp = facade.ops.find((o) => o.name === 'export')!;
|
|
344
346
|
const result = (await exportOp.handler({ domain: 'security' })) as {
|
|
345
347
|
bundles: Array<{ domain: string; entries: unknown[] }>;
|
|
@@ -351,7 +353,7 @@ ${domainDescribes}
|
|
|
351
353
|
});
|
|
352
354
|
|
|
353
355
|
it('create_plan should create a draft plan', async () => {
|
|
354
|
-
const facade =
|
|
356
|
+
const facade = makeCoreFacade();
|
|
355
357
|
const createOp = facade.ops.find((o) => o.name === 'create_plan')!;
|
|
356
358
|
const result = (await createOp.handler({
|
|
357
359
|
objective: 'Add caching',
|
|
@@ -364,7 +366,7 @@ ${domainDescribes}
|
|
|
364
366
|
});
|
|
365
367
|
|
|
366
368
|
it('get_plan should return a plan by id', async () => {
|
|
367
|
-
const facade =
|
|
369
|
+
const facade = makeCoreFacade();
|
|
368
370
|
const createOp = facade.ops.find((o) => o.name === 'create_plan')!;
|
|
369
371
|
const created = (await createOp.handler({ objective: 'Test', scope: 'test' })) as { plan: { id: string } };
|
|
370
372
|
const getOp = facade.ops.find((o) => o.name === 'get_plan')!;
|
|
@@ -374,7 +376,7 @@ ${domainDescribes}
|
|
|
374
376
|
});
|
|
375
377
|
|
|
376
378
|
it('get_plan without id should return active plans', async () => {
|
|
377
|
-
const facade =
|
|
379
|
+
const facade = makeCoreFacade();
|
|
378
380
|
const createOp = facade.ops.find((o) => o.name === 'create_plan')!;
|
|
379
381
|
await createOp.handler({ objective: 'Plan A', scope: 'a' });
|
|
380
382
|
const getOp = facade.ops.find((o) => o.name === 'get_plan')!;
|
|
@@ -384,7 +386,7 @@ ${domainDescribes}
|
|
|
384
386
|
});
|
|
385
387
|
|
|
386
388
|
it('approve_plan should approve and optionally start execution', async () => {
|
|
387
|
-
const facade =
|
|
389
|
+
const facade = makeCoreFacade();
|
|
388
390
|
const createOp = facade.ops.find((o) => o.name === 'create_plan')!;
|
|
389
391
|
const created = (await createOp.handler({ objective: 'Approve', scope: 'test' })) as { plan: { id: string } };
|
|
390
392
|
const approveOp = facade.ops.find((o) => o.name === 'approve_plan')!;
|
|
@@ -399,7 +401,7 @@ ${domainDescribes}
|
|
|
399
401
|
});
|
|
400
402
|
|
|
401
403
|
it('update_task should update task status', async () => {
|
|
402
|
-
const facade =
|
|
404
|
+
const facade = makeCoreFacade();
|
|
403
405
|
const createOp = facade.ops.find((o) => o.name === 'create_plan')!;
|
|
404
406
|
const created = (await createOp.handler({
|
|
405
407
|
objective: 'Task update',
|
|
@@ -419,7 +421,7 @@ ${domainDescribes}
|
|
|
419
421
|
});
|
|
420
422
|
|
|
421
423
|
it('complete_plan should complete with task summary', async () => {
|
|
422
|
-
const facade =
|
|
424
|
+
const facade = makeCoreFacade();
|
|
423
425
|
const createOp = facade.ops.find((o) => o.name === 'create_plan')!;
|
|
424
426
|
const created = (await createOp.handler({
|
|
425
427
|
objective: 'Complete me',
|
|
@@ -446,7 +448,7 @@ ${domainDescribes}
|
|
|
446
448
|
});
|
|
447
449
|
|
|
448
450
|
it('should have brain ops in core facade', () => {
|
|
449
|
-
const facade =
|
|
451
|
+
const facade = makeCoreFacade();
|
|
450
452
|
const opNames = facade.ops.map((o) => o.name);
|
|
451
453
|
expect(opNames).toContain('record_feedback');
|
|
452
454
|
expect(opNames).toContain('rebuild_vocabulary');
|
|
@@ -454,7 +456,7 @@ ${domainDescribes}
|
|
|
454
456
|
});
|
|
455
457
|
|
|
456
458
|
it('record_feedback should record search feedback', async () => {
|
|
457
|
-
const facade =
|
|
459
|
+
const facade = makeCoreFacade();
|
|
458
460
|
const feedbackOp = facade.ops.find((o) => o.name === 'record_feedback')!;
|
|
459
461
|
const result = (await feedbackOp.handler({
|
|
460
462
|
query: 'test query',
|
|
@@ -471,7 +473,7 @@ ${domainDescribes}
|
|
|
471
473
|
makeEntry({ id: 'rv1', title: 'Rebuild vocab test', description: 'Testing vocabulary rebuild.', tags: ['rebuild'] }),
|
|
472
474
|
]);
|
|
473
475
|
brain = new Brain(vault);
|
|
474
|
-
const facade =
|
|
476
|
+
const facade = makeCoreFacade();
|
|
475
477
|
const rebuildOp = facade.ops.find((o) => o.name === 'rebuild_vocabulary')!;
|
|
476
478
|
const result = (await rebuildOp.handler({})) as { rebuilt: boolean; vocabularySize: number };
|
|
477
479
|
expect(result.rebuilt).toBe(true);
|
|
@@ -479,7 +481,7 @@ ${domainDescribes}
|
|
|
479
481
|
});
|
|
480
482
|
|
|
481
483
|
it('brain_stats should return intelligence stats', async () => {
|
|
482
|
-
const facade =
|
|
484
|
+
const facade = makeCoreFacade();
|
|
483
485
|
const statsOp = facade.ops.find((o) => o.name === 'brain_stats')!;
|
|
484
486
|
const result = (await statsOp.handler({})) as {
|
|
485
487
|
vocabularySize: number;
|
|
@@ -493,7 +495,7 @@ ${domainDescribes}
|
|
|
493
495
|
});
|
|
494
496
|
|
|
495
497
|
it('brain_stats should reflect feedback count after recording', async () => {
|
|
496
|
-
const facade =
|
|
498
|
+
const facade = makeCoreFacade();
|
|
497
499
|
const feedbackOp = facade.ops.find((o) => o.name === 'record_feedback')!;
|
|
498
500
|
await feedbackOp.handler({ query: 'q1', entryId: 'e1', action: 'accepted' });
|
|
499
501
|
await feedbackOp.handler({ query: 'q2', entryId: 'e2', action: 'dismissed' });
|
|
@@ -503,7 +505,7 @@ ${domainDescribes}
|
|
|
503
505
|
});
|
|
504
506
|
|
|
505
507
|
it('llm_status should return provider availability', async () => {
|
|
506
|
-
const facade =
|
|
508
|
+
const facade = makeCoreFacade();
|
|
507
509
|
const llmStatusOp = facade.ops.find((o) => o.name === 'llm_status')!;
|
|
508
510
|
const result = (await llmStatusOp.handler({})) as {
|
|
509
511
|
providers: {
|
|
@@ -522,7 +524,7 @@ ${domainDescribes}
|
|
|
522
524
|
const oPool = new KeyPool({ keys: ['sk-test'] });
|
|
523
525
|
const aPool = new KeyPool({ keys: ['sk-ant-test'] });
|
|
524
526
|
const client = new LLMClient(oPool, aPool);
|
|
525
|
-
const facade = createCoreFacade(vault, planner, brain, client, oPool, aPool);
|
|
527
|
+
const facade = createCoreFacade(vault, planner, brain, undefined, client, oPool, aPool);
|
|
526
528
|
const llmStatusOp = facade.ops.find((o) => o.name === 'llm_status')!;
|
|
527
529
|
const result = (await llmStatusOp.handler({})) as {
|
|
528
530
|
providers: {
|
|
@@ -541,7 +543,7 @@ ${domainDescribes}
|
|
|
541
543
|
makeEntry({ id: 'bs-1', domain: 'security', title: 'Authentication token handling', severity: 'critical', description: 'Always validate JWT tokens.', tags: ['auth', 'jwt'] }),
|
|
542
544
|
]);
|
|
543
545
|
brain = new Brain(vault);
|
|
544
|
-
const facade =
|
|
546
|
+
const facade = makeCoreFacade();
|
|
545
547
|
const searchOp = facade.ops.find((o) => o.name === 'search')!;
|
|
546
548
|
const results = (await searchOp.handler({ query: 'authentication token' })) as Array<{ entry: { id: string }; score: number; breakdown: { semantic: number; total: number } }>;
|
|
547
549
|
expect(results.length).toBeGreaterThan(0);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-facades.js","sourceRoot":"","sources":["../../src/templates/test-facades.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAmB;IACrD,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO;SACjC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,MAAM,EAAE,GAAG,SAAS,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC1C,OAAO,YAAY,EAAE,uBAAuB,CAAC,cAAc,CAAC;IAC9D,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,MAAM,eAAe,GAAG,MAAM,CAAC,OAAO;SACnC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,sBAAsB,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;SAChD,IAAI,CAAC,MAAM,CAAC,CAAC;IAEhB,OAAO;;;;;;EAMP,aAAa
|
|
1
|
+
{"version":3,"file":"test-facades.js","sourceRoot":"","sources":["../../src/templates/test-facades.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -46,6 +46,8 @@ describe('Facades', () => {
|
|
|
46
46
|
let openaiKeyPool: KeyPool;
|
|
47
47
|
let anthropicKeyPool: KeyPool;
|
|
48
48
|
let llmClient: LLMClient;
|
|
49
|
+
const makeCoreFacade = () =>
|
|
50
|
+
createCoreFacade(vault, planner, brain, undefined, llmClient, openaiKeyPool, anthropicKeyPool);
|
|
49
51
|
|
|
50
52
|
beforeEach(() => {
|
|
51
53
|
vault = new Vault(':memory:');
|
|
@@ -67,7 +69,7 @@ ${domainDescribes}
|
|
|
67
69
|
|
|
68
70
|
describe('${config.id}_core', () => {
|
|
69
71
|
it('should create core facade with expected ops', () => {
|
|
70
|
-
const facade =
|
|
72
|
+
const facade = makeCoreFacade();
|
|
71
73
|
expect(facade.name).toBe('${config.id}_core');
|
|
72
74
|
const opNames = facade.ops.map((o) => o.name);
|
|
73
75
|
expect(opNames).toContain('search');
|
|
@@ -88,7 +90,7 @@ ${domainDescribes}
|
|
|
88
90
|
makeEntry({ id: 'c2', domain: 'beta', title: 'Beta pattern', tags: ['b'] }),
|
|
89
91
|
]);
|
|
90
92
|
brain = new Brain(vault);
|
|
91
|
-
const facade =
|
|
93
|
+
const facade = makeCoreFacade();
|
|
92
94
|
const searchOp = facade.ops.find((o) => o.name === 'search')!;
|
|
93
95
|
const results = (await searchOp.handler({ query: 'pattern' })) as Array<{ entry: unknown; score: number; breakdown: unknown }>;
|
|
94
96
|
expect(Array.isArray(results)).toBe(true);
|
|
@@ -102,21 +104,21 @@ ${domainDescribes}
|
|
|
102
104
|
makeEntry({ id: 'vs1', domain: 'd1', tags: ['x'] }),
|
|
103
105
|
makeEntry({ id: 'vs2', domain: 'd2', tags: ['y'] }),
|
|
104
106
|
]);
|
|
105
|
-
const facade =
|
|
107
|
+
const facade = makeCoreFacade();
|
|
106
108
|
const statsOp = facade.ops.find((o) => o.name === 'vault_stats')!;
|
|
107
109
|
const stats = (await statsOp.handler({})) as { totalEntries: number };
|
|
108
110
|
expect(stats.totalEntries).toBe(2);
|
|
109
111
|
});
|
|
110
112
|
|
|
111
113
|
it('health should return ok status', async () => {
|
|
112
|
-
const facade =
|
|
114
|
+
const facade = makeCoreFacade();
|
|
113
115
|
const healthOp = facade.ops.find((o) => o.name === 'health')!;
|
|
114
116
|
const health = (await healthOp.handler({})) as { status: string };
|
|
115
117
|
expect(health.status).toBe('ok');
|
|
116
118
|
});
|
|
117
119
|
|
|
118
120
|
it('identity should return persona', async () => {
|
|
119
|
-
const facade =
|
|
121
|
+
const facade = makeCoreFacade();
|
|
120
122
|
const identityOp = facade.ops.find((o) => o.name === 'identity')!;
|
|
121
123
|
const persona = (await identityOp.handler({})) as { name: string; role: string };
|
|
122
124
|
expect(persona.name).toBe('${escapeQuotes(config.name)}');
|
|
@@ -128,14 +130,14 @@ ${domainDescribes}
|
|
|
128
130
|
makeEntry({ id: 'la1', domain: 'alpha', tags: ['a'] }),
|
|
129
131
|
makeEntry({ id: 'la2', domain: 'beta', tags: ['b'] }),
|
|
130
132
|
]);
|
|
131
|
-
const facade =
|
|
133
|
+
const facade = makeCoreFacade();
|
|
132
134
|
const listOp = facade.ops.find((o) => o.name === 'list_all')!;
|
|
133
135
|
const filtered = (await listOp.handler({ domain: 'alpha' })) as unknown[];
|
|
134
136
|
expect(filtered).toHaveLength(1);
|
|
135
137
|
});
|
|
136
138
|
|
|
137
139
|
it('activate should return persona and setup status', async () => {
|
|
138
|
-
const facade =
|
|
140
|
+
const facade = makeCoreFacade();
|
|
139
141
|
const activateOp = facade.ops.find((o) => o.name === 'activate')!;
|
|
140
142
|
const result = (await activateOp.handler({ projectPath: '/tmp/nonexistent-test' })) as {
|
|
141
143
|
activated: boolean;
|
|
@@ -154,7 +156,7 @@ ${domainDescribes}
|
|
|
154
156
|
});
|
|
155
157
|
|
|
156
158
|
it('activate with deactivate flag should return deactivation', async () => {
|
|
157
|
-
const facade =
|
|
159
|
+
const facade = makeCoreFacade();
|
|
158
160
|
const activateOp = facade.ops.find((o) => o.name === 'activate')!;
|
|
159
161
|
const result = (await activateOp.handler({ deactivate: true })) as { deactivated: boolean; message: string };
|
|
160
162
|
expect(result.deactivated).toBe(true);
|
|
@@ -165,7 +167,7 @@ ${domainDescribes}
|
|
|
165
167
|
const tempDir = join(tmpdir(), 'forge-inject-test-' + Date.now());
|
|
166
168
|
mkdirSync(tempDir, { recursive: true });
|
|
167
169
|
try {
|
|
168
|
-
const facade =
|
|
170
|
+
const facade = makeCoreFacade();
|
|
169
171
|
const injectOp = facade.ops.find((o) => o.name === 'inject_claude_md')!;
|
|
170
172
|
const result = (await injectOp.handler({ projectPath: tempDir })) as {
|
|
171
173
|
injected: boolean;
|
|
@@ -185,7 +187,7 @@ ${domainDescribes}
|
|
|
185
187
|
it('inject_claude_md with global flag should target ~/.claude/CLAUDE.md', async () => {
|
|
186
188
|
// We test the global path resolution by checking the returned path
|
|
187
189
|
// contains .claude/CLAUDE.md (actual write may or may not happen depending on env)
|
|
188
|
-
const facade =
|
|
190
|
+
const facade = makeCoreFacade();
|
|
189
191
|
const injectOp = facade.ops.find((o) => o.name === 'inject_claude_md')!;
|
|
190
192
|
const result = (await injectOp.handler({ global: true })) as {
|
|
191
193
|
injected: boolean;
|
|
@@ -198,7 +200,7 @@ ${domainDescribes}
|
|
|
198
200
|
});
|
|
199
201
|
|
|
200
202
|
it('setup should return project and global CLAUDE.md status', async () => {
|
|
201
|
-
const facade =
|
|
203
|
+
const facade = makeCoreFacade();
|
|
202
204
|
const setupOp = facade.ops.find((o) => o.name === 'setup')!;
|
|
203
205
|
const result = (await setupOp.handler({ projectPath: '/tmp/nonexistent-test' })) as {
|
|
204
206
|
agent: { name: string };
|
|
@@ -217,7 +219,7 @@ ${domainDescribes}
|
|
|
217
219
|
});
|
|
218
220
|
|
|
219
221
|
it('register should track new project', async () => {
|
|
220
|
-
const facade =
|
|
222
|
+
const facade = makeCoreFacade();
|
|
221
223
|
const registerOp = facade.ops.find((o) => o.name === 'register')!;
|
|
222
224
|
const result = (await registerOp.handler({ projectPath: '/tmp/reg-test-project', name: 'reg-test' })) as {
|
|
223
225
|
project: { path: string; name: string; sessionCount: number };
|
|
@@ -232,7 +234,7 @@ ${domainDescribes}
|
|
|
232
234
|
});
|
|
233
235
|
|
|
234
236
|
it('register should increment session count for returning project', async () => {
|
|
235
|
-
const facade =
|
|
237
|
+
const facade = makeCoreFacade();
|
|
236
238
|
const registerOp = facade.ops.find((o) => o.name === 'register')!;
|
|
237
239
|
await registerOp.handler({ projectPath: '/tmp/reg-test-returning', name: 'returning' });
|
|
238
240
|
const result = (await registerOp.handler({ projectPath: '/tmp/reg-test-returning', name: 'returning' })) as {
|
|
@@ -246,7 +248,7 @@ ${domainDescribes}
|
|
|
246
248
|
});
|
|
247
249
|
|
|
248
250
|
it('memory_capture should store a memory', async () => {
|
|
249
|
-
const facade =
|
|
251
|
+
const facade = makeCoreFacade();
|
|
250
252
|
const captureOp = facade.ops.find((o) => o.name === 'memory_capture')!;
|
|
251
253
|
const result = (await captureOp.handler({
|
|
252
254
|
projectPath: '/test',
|
|
@@ -263,7 +265,7 @@ ${domainDescribes}
|
|
|
263
265
|
});
|
|
264
266
|
|
|
265
267
|
it('memory_search should find captured memories', async () => {
|
|
266
|
-
const facade =
|
|
268
|
+
const facade = makeCoreFacade();
|
|
267
269
|
const captureOp = facade.ops.find((o) => o.name === 'memory_capture')!;
|
|
268
270
|
await captureOp.handler({
|
|
269
271
|
projectPath: '/test',
|
|
@@ -281,7 +283,7 @@ ${domainDescribes}
|
|
|
281
283
|
});
|
|
282
284
|
|
|
283
285
|
it('memory_list should return all memories with stats', async () => {
|
|
284
|
-
const facade =
|
|
286
|
+
const facade = makeCoreFacade();
|
|
285
287
|
const captureOp = facade.ops.find((o) => o.name === 'memory_capture')!;
|
|
286
288
|
await captureOp.handler({
|
|
287
289
|
projectPath: '/test',
|
|
@@ -302,7 +304,7 @@ ${domainDescribes}
|
|
|
302
304
|
});
|
|
303
305
|
|
|
304
306
|
it('session_capture should store a session memory', async () => {
|
|
305
|
-
const facade =
|
|
307
|
+
const facade = makeCoreFacade();
|
|
306
308
|
const sessionOp = facade.ops.find((o) => o.name === 'session_capture')!;
|
|
307
309
|
const result = (await sessionOp.handler({
|
|
308
310
|
projectPath: '/tmp/test-session',
|
|
@@ -323,7 +325,7 @@ ${domainDescribes}
|
|
|
323
325
|
makeEntry({ id: 'exp2', domain: 'security', tags: ['xss'] }),
|
|
324
326
|
makeEntry({ id: 'exp3', domain: 'api-design', tags: ['rest'] }),
|
|
325
327
|
]);
|
|
326
|
-
const facade =
|
|
328
|
+
const facade = makeCoreFacade();
|
|
327
329
|
const exportOp = facade.ops.find((o) => o.name === 'export')!;
|
|
328
330
|
const result = (await exportOp.handler({})) as {
|
|
329
331
|
exported: boolean;
|
|
@@ -343,7 +345,7 @@ ${domainDescribes}
|
|
|
343
345
|
makeEntry({ id: 'exp-d1', domain: 'security', tags: ['auth'] }),
|
|
344
346
|
makeEntry({ id: 'exp-d2', domain: 'api-design', tags: ['rest'] }),
|
|
345
347
|
]);
|
|
346
|
-
const facade =
|
|
348
|
+
const facade = makeCoreFacade();
|
|
347
349
|
const exportOp = facade.ops.find((o) => o.name === 'export')!;
|
|
348
350
|
const result = (await exportOp.handler({ domain: 'security' })) as {
|
|
349
351
|
bundles: Array<{ domain: string; entries: unknown[] }>;
|
|
@@ -355,7 +357,7 @@ ${domainDescribes}
|
|
|
355
357
|
});
|
|
356
358
|
|
|
357
359
|
it('create_plan should create a draft plan', async () => {
|
|
358
|
-
const facade =
|
|
360
|
+
const facade = makeCoreFacade();
|
|
359
361
|
const createOp = facade.ops.find((o) => o.name === 'create_plan')!;
|
|
360
362
|
const result = (await createOp.handler({
|
|
361
363
|
objective: 'Add caching',
|
|
@@ -368,7 +370,7 @@ ${domainDescribes}
|
|
|
368
370
|
});
|
|
369
371
|
|
|
370
372
|
it('get_plan should return a plan by id', async () => {
|
|
371
|
-
const facade =
|
|
373
|
+
const facade = makeCoreFacade();
|
|
372
374
|
const createOp = facade.ops.find((o) => o.name === 'create_plan')!;
|
|
373
375
|
const created = (await createOp.handler({ objective: 'Test', scope: 'test' })) as { plan: { id: string } };
|
|
374
376
|
const getOp = facade.ops.find((o) => o.name === 'get_plan')!;
|
|
@@ -378,7 +380,7 @@ ${domainDescribes}
|
|
|
378
380
|
});
|
|
379
381
|
|
|
380
382
|
it('get_plan without id should return active plans', async () => {
|
|
381
|
-
const facade =
|
|
383
|
+
const facade = makeCoreFacade();
|
|
382
384
|
const createOp = facade.ops.find((o) => o.name === 'create_plan')!;
|
|
383
385
|
await createOp.handler({ objective: 'Plan A', scope: 'a' });
|
|
384
386
|
const getOp = facade.ops.find((o) => o.name === 'get_plan')!;
|
|
@@ -388,7 +390,7 @@ ${domainDescribes}
|
|
|
388
390
|
});
|
|
389
391
|
|
|
390
392
|
it('approve_plan should approve and optionally start execution', async () => {
|
|
391
|
-
const facade =
|
|
393
|
+
const facade = makeCoreFacade();
|
|
392
394
|
const createOp = facade.ops.find((o) => o.name === 'create_plan')!;
|
|
393
395
|
const created = (await createOp.handler({ objective: 'Approve', scope: 'test' })) as { plan: { id: string } };
|
|
394
396
|
const approveOp = facade.ops.find((o) => o.name === 'approve_plan')!;
|
|
@@ -403,7 +405,7 @@ ${domainDescribes}
|
|
|
403
405
|
});
|
|
404
406
|
|
|
405
407
|
it('update_task should update task status', async () => {
|
|
406
|
-
const facade =
|
|
408
|
+
const facade = makeCoreFacade();
|
|
407
409
|
const createOp = facade.ops.find((o) => o.name === 'create_plan')!;
|
|
408
410
|
const created = (await createOp.handler({
|
|
409
411
|
objective: 'Task update',
|
|
@@ -423,7 +425,7 @@ ${domainDescribes}
|
|
|
423
425
|
});
|
|
424
426
|
|
|
425
427
|
it('complete_plan should complete with task summary', async () => {
|
|
426
|
-
const facade =
|
|
428
|
+
const facade = makeCoreFacade();
|
|
427
429
|
const createOp = facade.ops.find((o) => o.name === 'create_plan')!;
|
|
428
430
|
const created = (await createOp.handler({
|
|
429
431
|
objective: 'Complete me',
|
|
@@ -450,7 +452,7 @@ ${domainDescribes}
|
|
|
450
452
|
});
|
|
451
453
|
|
|
452
454
|
it('should have brain ops in core facade', () => {
|
|
453
|
-
const facade =
|
|
455
|
+
const facade = makeCoreFacade();
|
|
454
456
|
const opNames = facade.ops.map((o) => o.name);
|
|
455
457
|
expect(opNames).toContain('record_feedback');
|
|
456
458
|
expect(opNames).toContain('rebuild_vocabulary');
|
|
@@ -458,7 +460,7 @@ ${domainDescribes}
|
|
|
458
460
|
});
|
|
459
461
|
|
|
460
462
|
it('record_feedback should record search feedback', async () => {
|
|
461
|
-
const facade =
|
|
463
|
+
const facade = makeCoreFacade();
|
|
462
464
|
const feedbackOp = facade.ops.find((o) => o.name === 'record_feedback')!;
|
|
463
465
|
const result = (await feedbackOp.handler({
|
|
464
466
|
query: 'test query',
|
|
@@ -475,7 +477,7 @@ ${domainDescribes}
|
|
|
475
477
|
makeEntry({ id: 'rv1', title: 'Rebuild vocab test', description: 'Testing vocabulary rebuild.', tags: ['rebuild'] }),
|
|
476
478
|
]);
|
|
477
479
|
brain = new Brain(vault);
|
|
478
|
-
const facade =
|
|
480
|
+
const facade = makeCoreFacade();
|
|
479
481
|
const rebuildOp = facade.ops.find((o) => o.name === 'rebuild_vocabulary')!;
|
|
480
482
|
const result = (await rebuildOp.handler({})) as { rebuilt: boolean; vocabularySize: number };
|
|
481
483
|
expect(result.rebuilt).toBe(true);
|
|
@@ -483,7 +485,7 @@ ${domainDescribes}
|
|
|
483
485
|
});
|
|
484
486
|
|
|
485
487
|
it('brain_stats should return intelligence stats', async () => {
|
|
486
|
-
const facade =
|
|
488
|
+
const facade = makeCoreFacade();
|
|
487
489
|
const statsOp = facade.ops.find((o) => o.name === 'brain_stats')!;
|
|
488
490
|
const result = (await statsOp.handler({})) as {
|
|
489
491
|
vocabularySize: number;
|
|
@@ -497,7 +499,7 @@ ${domainDescribes}
|
|
|
497
499
|
});
|
|
498
500
|
|
|
499
501
|
it('brain_stats should reflect feedback count after recording', async () => {
|
|
500
|
-
const facade =
|
|
502
|
+
const facade = makeCoreFacade();
|
|
501
503
|
const feedbackOp = facade.ops.find((o) => o.name === 'record_feedback')!;
|
|
502
504
|
await feedbackOp.handler({ query: 'q1', entryId: 'e1', action: 'accepted' });
|
|
503
505
|
await feedbackOp.handler({ query: 'q2', entryId: 'e2', action: 'dismissed' });
|
|
@@ -507,7 +509,7 @@ ${domainDescribes}
|
|
|
507
509
|
});
|
|
508
510
|
|
|
509
511
|
it('llm_status should return provider availability', async () => {
|
|
510
|
-
const facade =
|
|
512
|
+
const facade = makeCoreFacade();
|
|
511
513
|
const llmStatusOp = facade.ops.find((o) => o.name === 'llm_status')!;
|
|
512
514
|
const result = (await llmStatusOp.handler({})) as {
|
|
513
515
|
providers: {
|
|
@@ -526,7 +528,7 @@ ${domainDescribes}
|
|
|
526
528
|
const oPool = new KeyPool({ keys: ['sk-test'] });
|
|
527
529
|
const aPool = new KeyPool({ keys: ['sk-ant-test'] });
|
|
528
530
|
const client = new LLMClient(oPool, aPool);
|
|
529
|
-
const facade = createCoreFacade(vault, planner, brain, client, oPool, aPool);
|
|
531
|
+
const facade = createCoreFacade(vault, planner, brain, undefined, client, oPool, aPool);
|
|
530
532
|
const llmStatusOp = facade.ops.find((o) => o.name === 'llm_status')!;
|
|
531
533
|
const result = (await llmStatusOp.handler({})) as {
|
|
532
534
|
providers: {
|
|
@@ -545,7 +547,7 @@ ${domainDescribes}
|
|
|
545
547
|
makeEntry({ id: 'bs-1', domain: 'security', title: 'Authentication token handling', severity: 'critical', description: 'Always validate JWT tokens.', tags: ['auth', 'jwt'] }),
|
|
546
548
|
]);
|
|
547
549
|
brain = new Brain(vault);
|
|
548
|
-
const facade =
|
|
550
|
+
const facade = makeCoreFacade();
|
|
549
551
|
const searchOp = facade.ops.find((o) => o.name === 'search')!;
|
|
550
552
|
const results = (await searchOp.handler({ query: 'authentication token' })) as Array<{ entry: { id: string }; score: number; breakdown: { semantic: number; total: number } }>;
|
|
551
553
|
expect(results.length).toBeGreaterThan(0);
|