@soleri/forge 8.0.0 → 9.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/dist/agent-schema.d.ts +16 -18
- package/dist/agent-schema.js +6 -6
- package/dist/agent-schema.js.map +1 -1
- package/dist/compose-claude-md.js +10 -8
- package/dist/compose-claude-md.js.map +1 -1
- package/dist/facades/forge.facade.js +20 -4
- package/dist/facades/forge.facade.js.map +1 -1
- package/dist/scaffold-filetree.js +80 -9
- package/dist/scaffold-filetree.js.map +1 -1
- package/dist/scaffolder.js +0 -4
- package/dist/scaffolder.js.map +1 -1
- package/dist/skills/agent-guide.md +1 -1
- package/dist/templates/agents-md.js +0 -1
- package/dist/templates/agents-md.js.map +1 -1
- package/dist/templates/claude-md-template.js +2 -2
- package/dist/templates/claude-md-template.js.map +1 -1
- package/dist/templates/entry-point.js +3 -3
- package/dist/templates/entry-point.js.map +1 -1
- package/dist/templates/shared-rules.js +19 -0
- package/dist/templates/shared-rules.js.map +1 -1
- package/dist/templates/test-facades.js +0 -11
- package/dist/templates/test-facades.js.map +1 -1
- package/dist/types.d.ts +20 -20
- package/dist/types.js +6 -6
- package/dist/types.js.map +1 -1
- package/package.json +2 -1
- package/src/__tests__/scaffold-filetree.test.ts +0 -2
- package/src/agent-schema.ts +6 -6
- package/src/compose-claude-md.ts +10 -18
- package/src/facades/forge.facade.ts +21 -4
- package/src/scaffold-filetree.ts +83 -8
- package/src/scaffolder.ts +0 -4
- package/src/skills/agent-guide.md +1 -1
- package/src/templates/agents-md.ts +0 -1
- package/src/templates/claude-md-template.ts +1 -2
- package/src/templates/entry-point.ts +3 -3
- package/src/templates/shared-rules.ts +20 -0
- package/src/templates/test-facades.ts +0 -11
- package/src/types.ts +6 -6
package/src/scaffold-filetree.ts
CHANGED
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
* Replaces the old scaffold() that generated TypeScript projects.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { mkdirSync, writeFileSync, existsSync } from 'node:fs';
|
|
11
|
-
import { join } from 'node:path';
|
|
10
|
+
import { mkdirSync, writeFileSync, existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
11
|
+
import { join, dirname } from 'node:path';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
12
13
|
import { stringify as yamlStringify } from 'yaml';
|
|
13
14
|
import type { AgentYaml, AgentYamlInput } from './agent-schema.js';
|
|
14
15
|
import { AgentYamlSchema } from './agent-schema.js';
|
|
@@ -93,8 +94,8 @@ When building a new feature, adding functionality, or creating components.
|
|
|
93
94
|
- soleri_vault op:search_intelligent
|
|
94
95
|
- soleri_vault op:capture_knowledge
|
|
95
96
|
- soleri_vault op:link_entries
|
|
96
|
-
-
|
|
97
|
-
-
|
|
97
|
+
- soleri_plan op:create_plan
|
|
98
|
+
- soleri_plan op:approve_plan
|
|
98
99
|
- soleri_brain op:recommend
|
|
99
100
|
`,
|
|
100
101
|
},
|
|
@@ -146,7 +147,7 @@ When fixing bugs, resolving errors, or addressing regressions.
|
|
|
146
147
|
tools: `tools:
|
|
147
148
|
- soleri_vault op:search_intelligent
|
|
148
149
|
- soleri_vault op:capture_knowledge
|
|
149
|
-
-
|
|
150
|
+
- soleri_plan op:create_plan
|
|
150
151
|
- soleri_brain op:recommend
|
|
151
152
|
`,
|
|
152
153
|
},
|
|
@@ -307,12 +308,16 @@ export function scaffoldFileTree(input: AgentYamlInput, outputDir: string): File
|
|
|
307
308
|
writeFile(agentDir, `workflows/${wf.name}/tools.yaml`, wf.tools, filesCreated);
|
|
308
309
|
}
|
|
309
310
|
|
|
310
|
-
// ─── 8. Write
|
|
311
|
+
// ─── 8. Write knowledge bundles (seed from starter packs if available) ──
|
|
312
|
+
const starterPacksDir = resolveStarterPacksDir();
|
|
313
|
+
let totalSeeded = 0;
|
|
314
|
+
|
|
311
315
|
for (const domain of config.domains) {
|
|
316
|
+
const starterEntries = loadStarterEntries(starterPacksDir, domain);
|
|
312
317
|
const bundle = {
|
|
313
318
|
domain,
|
|
314
319
|
version: '1.0.0',
|
|
315
|
-
entries:
|
|
320
|
+
entries: starterEntries,
|
|
316
321
|
};
|
|
317
322
|
writeFile(
|
|
318
323
|
agentDir,
|
|
@@ -320,6 +325,7 @@ export function scaffoldFileTree(input: AgentYamlInput, outputDir: string): File
|
|
|
320
325
|
JSON.stringify(bundle, null, 2) + '\n',
|
|
321
326
|
filesCreated,
|
|
322
327
|
);
|
|
328
|
+
totalSeeded += starterEntries.length;
|
|
323
329
|
}
|
|
324
330
|
|
|
325
331
|
// ─── 9. Generate CLAUDE.md ──────────────────────────────────
|
|
@@ -332,6 +338,7 @@ export function scaffoldFileTree(input: AgentYamlInput, outputDir: string): File
|
|
|
332
338
|
'',
|
|
333
339
|
` Files: ${filesCreated.length}`,
|
|
334
340
|
` Domains: ${config.domains.join(', ')}`,
|
|
341
|
+
` Knowledge: ${totalSeeded} starter entries seeded`,
|
|
335
342
|
` Workflows: ${BUILTIN_WORKFLOWS.map((w) => w.name).join(', ')}`,
|
|
336
343
|
'',
|
|
337
344
|
'Next steps:',
|
|
@@ -393,11 +400,15 @@ function buildAgentYaml(config: AgentYaml): Record<string, unknown> {
|
|
|
393
400
|
yaml.greeting = config.greeting;
|
|
394
401
|
}
|
|
395
402
|
|
|
403
|
+
// Persona config — include if present
|
|
404
|
+
if (config.persona && Object.keys(config.persona).length > 0) {
|
|
405
|
+
yaml.persona = config.persona;
|
|
406
|
+
}
|
|
407
|
+
|
|
396
408
|
// Engine config — only include non-defaults
|
|
397
409
|
const engine: Record<string, unknown> = {};
|
|
398
410
|
if (config.engine?.vault) engine.vault = config.engine.vault;
|
|
399
411
|
if (config.engine?.learning === false) engine.learning = false;
|
|
400
|
-
if (config.engine?.cognee === true) engine.cognee = true;
|
|
401
412
|
if (Object.keys(engine).length > 0) yaml.engine = engine;
|
|
402
413
|
|
|
403
414
|
// Vaults
|
|
@@ -419,3 +430,67 @@ function buildAgentYaml(config: AgentYaml): Record<string, unknown> {
|
|
|
419
430
|
|
|
420
431
|
return yaml;
|
|
421
432
|
}
|
|
433
|
+
|
|
434
|
+
// ─── Starter Pack Helpers ────────────────────────────────────────────
|
|
435
|
+
|
|
436
|
+
/** Domain aliases — map agent domains to starter pack directories. */
|
|
437
|
+
const DOMAIN_TO_STARTER: Record<string, string> = {
|
|
438
|
+
// design starter
|
|
439
|
+
frontend: 'design',
|
|
440
|
+
design: 'design',
|
|
441
|
+
'ui-design': 'design',
|
|
442
|
+
accessibility: 'design',
|
|
443
|
+
styling: 'design',
|
|
444
|
+
react: 'design',
|
|
445
|
+
'component-patterns': 'design',
|
|
446
|
+
'responsive-design': 'design',
|
|
447
|
+
// security starter
|
|
448
|
+
security: 'security',
|
|
449
|
+
auth: 'security',
|
|
450
|
+
authentication: 'security',
|
|
451
|
+
// architecture starter
|
|
452
|
+
architecture: 'architecture',
|
|
453
|
+
'api-design': 'architecture',
|
|
454
|
+
database: 'architecture',
|
|
455
|
+
backend: 'architecture',
|
|
456
|
+
infrastructure: 'architecture',
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
function resolveStarterPacksDir(): string | null {
|
|
460
|
+
// Try repo-relative path (monorepo development)
|
|
461
|
+
const forgeDir = dirname(fileURLToPath(import.meta.url));
|
|
462
|
+
const candidates = [
|
|
463
|
+
join(forgeDir, '..', '..', '..', 'knowledge-packs', 'starter'),
|
|
464
|
+
join(forgeDir, '..', '..', 'knowledge-packs', 'starter'),
|
|
465
|
+
];
|
|
466
|
+
for (const dir of candidates) {
|
|
467
|
+
if (existsSync(dir)) return dir;
|
|
468
|
+
}
|
|
469
|
+
return null;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function loadStarterEntries(starterDir: string | null, domain: string): unknown[] {
|
|
473
|
+
if (!starterDir) return [];
|
|
474
|
+
|
|
475
|
+
const packName = DOMAIN_TO_STARTER[domain];
|
|
476
|
+
if (!packName) return [];
|
|
477
|
+
|
|
478
|
+
const vaultDir = join(starterDir, packName, 'vault');
|
|
479
|
+
if (!existsSync(vaultDir)) return [];
|
|
480
|
+
|
|
481
|
+
const entries: unknown[] = [];
|
|
482
|
+
try {
|
|
483
|
+
const files = readdirSync(vaultDir).filter((f: string) => f.endsWith('.json'));
|
|
484
|
+
for (const file of files) {
|
|
485
|
+
const data = JSON.parse(readFileSync(join(vaultDir, file), 'utf-8'));
|
|
486
|
+
if (Array.isArray(data)) {
|
|
487
|
+
entries.push(...data);
|
|
488
|
+
} else if (data.entries && Array.isArray(data.entries)) {
|
|
489
|
+
entries.push(...data.entries);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
} catch {
|
|
493
|
+
// Starter pack unavailable — return empty
|
|
494
|
+
}
|
|
495
|
+
return entries;
|
|
496
|
+
}
|
package/src/scaffolder.ts
CHANGED
|
@@ -257,10 +257,6 @@ export function previewScaffold(config: AgentConfig): ScaffoldPreview {
|
|
|
257
257
|
name: `${config.id}_control`,
|
|
258
258
|
ops: ['get_identity', 'route_intent', 'governance_policy', '...control+governance ops'],
|
|
259
259
|
},
|
|
260
|
-
{
|
|
261
|
-
name: `${config.id}_cognee`,
|
|
262
|
-
ops: ['cognee_status', 'cognee_search', '...cognee ops', '...cognee-sync'],
|
|
263
|
-
},
|
|
264
260
|
// Agent-specific facade
|
|
265
261
|
{
|
|
266
262
|
name: `${config.id}_core`,
|
|
@@ -38,7 +38,7 @@ This returns the agent's persona: name, role, description, tone, principles, and
|
|
|
38
38
|
YOUR_AGENT_core op:admin_health
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
Shows what subsystems are active: vault (how many entries), brain (vocabulary size), LLM availability
|
|
41
|
+
Shows what subsystems are active: vault (how many entries), brain (vocabulary size), LLM availability. This tells the user what the agent currently has to work with.
|
|
42
42
|
|
|
43
43
|
### Step 3: Available Tools
|
|
44
44
|
|
|
@@ -80,7 +80,6 @@ ${domainRows}
|
|
|
80
80
|
| ${bt}${tp}_memory${bt} | ${bt}memory_search${bt}, ${bt}memory_capture${bt}, ${bt}session_capture${bt} |
|
|
81
81
|
| ${bt}${tp}_control${bt} | ${bt}route_intent${bt}, ${bt}morph${bt}, ${bt}get_behavior_rules${bt}, ${bt}governance_dashboard${bt}, ${bt}governance_policy${bt} |
|
|
82
82
|
| ${bt}${tp}_loop${bt} | ${bt}loop_start${bt}, ${bt}loop_iterate${bt}, ${bt}loop_status${bt}, ${bt}loop_cancel${bt} |
|
|
83
|
-
| ${bt}${tp}_cognee${bt} | ${bt}cognee_search${bt}, ${bt}cognee_graph_stats${bt}, ${bt}cognee_export_status${bt} |
|
|
84
83
|
| ${bt}${tp}_context${bt} | ${bt}context_extract_entities${bt}, ${bt}context_retrieve_knowledge${bt}, ${bt}context_analyze${bt} |
|
|
85
84
|
| ${bt}${tp}_agency${bt} | ${bt}agency_enable${bt}, ${bt}agency_status${bt}, ${bt}agency_surface_patterns${bt}, ${bt}agency_warnings${bt}, ${bt}agency_clarify${bt} |
|
|
86
85
|
| ${bt}${tp}_admin${bt} | ${bt}admin_health${bt}, ${bt}admin_tool_list${bt}, ${bt}admin_diagnostic${bt} |
|
|
@@ -115,8 +115,7 @@ export function generateClaudeMdTemplate(config: AgentConfig): string {
|
|
|
115
115
|
`| ${bt}${toolPrefix}_memory${bt} | ${bt}memory_search${bt}, ${bt}memory_capture${bt}, ${bt}session_capture${bt} |`,
|
|
116
116
|
`| ${bt}${toolPrefix}_control${bt} | ${bt}route_intent${bt}, ${bt}morph${bt}, ${bt}get_behavior_rules${bt}, ${bt}governance_dashboard${bt}, ${bt}governance_policy${bt} |`,
|
|
117
117
|
`| ${bt}${toolPrefix}_loop${bt} | ${bt}loop_start${bt}, ${bt}loop_iterate${bt}, ${bt}loop_status${bt}, ${bt}loop_cancel${bt} |`,
|
|
118
|
-
// Intelligence —
|
|
119
|
-
`| ${bt}${toolPrefix}_cognee${bt} | ${bt}cognee_search${bt}, ${bt}cognee_graph_stats${bt}, ${bt}cognee_export_status${bt} |`,
|
|
118
|
+
// Intelligence — context, agency
|
|
120
119
|
`| ${bt}${toolPrefix}_context${bt} | ${bt}context_extract_entities${bt}, ${bt}context_retrieve_knowledge${bt}, ${bt}context_analyze${bt} |`,
|
|
121
120
|
`| ${bt}${toolPrefix}_agency${bt} | ${bt}agency_enable${bt}, ${bt}agency_status${bt}, ${bt}agency_surface_patterns${bt}, ${bt}agency_warnings${bt}, ${bt}agency_clarify${bt} |`,
|
|
122
121
|
// Admin
|
|
@@ -56,7 +56,7 @@ async function main(): Promise<void> {
|
|
|
56
56
|
// ─── Runtime — vault, brain, planner, curator, LLM, key pools ───
|
|
57
57
|
const runtime = createAgentRuntime({
|
|
58
58
|
agentId: '${config.id}',
|
|
59
|
-
dataDir: join(__dirname, 'intelligence', 'data'),${config.sharedVaultPath ? `\n sharedVaultPath: '${config.sharedVaultPath}',` : ''}
|
|
59
|
+
dataDir: join(__dirname, 'intelligence', 'data'),${config.sharedVaultPath ? `\n sharedVaultPath: '${config.sharedVaultPath}',` : ''}
|
|
60
60
|
});
|
|
61
61
|
|
|
62
62
|
const tag = PERSONA.name.toLowerCase();
|
|
@@ -263,12 +263,12 @@ ${
|
|
|
263
263
|
const domainPacks = await loadDomainPacksFromConfig(${JSON.stringify(config.domainPacks)});
|
|
264
264
|
console.error(\`[\${tag}] Loaded \${domainPacks.length} domain packs\`);
|
|
265
265
|
for (const pack of domainPacks) {
|
|
266
|
-
|
|
266
|
+
const packRuntime = createPackRuntime(runtime);
|
|
267
|
+
if (pack.onActivate) await pack.onActivate(packRuntime, runtime);
|
|
267
268
|
}
|
|
268
269
|
|
|
269
270
|
// ─── Capability Registry ─────────────────────────────────────
|
|
270
271
|
const capabilityRegistry = new CapabilityRegistry();
|
|
271
|
-
const packRuntime = createPackRuntime(runtime);
|
|
272
272
|
|
|
273
273
|
// Register domain pack capabilities
|
|
274
274
|
for (const pack of domainPacks) {
|
|
@@ -69,6 +69,26 @@ const ENGINE_RULES_LINES: string[] = [
|
|
|
69
69
|
'- After every response, rate your confidence from 1 to 10. Anything below 7, flag it.',
|
|
70
70
|
'',
|
|
71
71
|
|
|
72
|
+
// ─── MCP Tool Schema Validation ─────────────────────────
|
|
73
|
+
'## MCP Tool Schema Validation',
|
|
74
|
+
'<!-- soleri:tool-schema-validation -->',
|
|
75
|
+
'',
|
|
76
|
+
'**MANDATORY**: Before calling any MCP tool for the first time in a session, fetch its full JSON schema first.',
|
|
77
|
+
'',
|
|
78
|
+
'- Use `ToolSearch` (or platform equivalent) to retrieve the tool definition before invoking it.',
|
|
79
|
+
'- Read required fields, types, enum constraints, and nesting structure.',
|
|
80
|
+
'- Do NOT guess parameter shapes from memory or training data — schemas evolve between versions.',
|
|
81
|
+
'- Once fetched, the schema is valid for the remainder of the session.',
|
|
82
|
+
'',
|
|
83
|
+
'**Why:** MCP tools have strict parameter validation. Guessing formats causes repeated failures (wrong nesting, invalid enums, missing required fields), wasting tokens and eroding user trust. The schema is always available — use it.',
|
|
84
|
+
'',
|
|
85
|
+
'| Wrong | Right |',
|
|
86
|
+
'|-------|-------|',
|
|
87
|
+
'| Call tool, fail, retry with different shape | ToolSearch first, call once correctly |',
|
|
88
|
+
'| Assume `severity: "suggestion"` is valid | Read schema: `"critical" \\| "warning" \\| "info"` |',
|
|
89
|
+
'| Pass flat params when tool expects `entries[]` | Read schema: `entries` is required array |',
|
|
90
|
+
'',
|
|
91
|
+
|
|
72
92
|
// ─── Memory Quality Gate ───────────────────────────────
|
|
73
93
|
'## Memory Quality Gate',
|
|
74
94
|
'<!-- soleri:memory-quality -->',
|
|
@@ -86,7 +86,6 @@ ${domainPackDescribes ? `\n${domainPackDescribes}\n` : ''}
|
|
|
86
86
|
expect(names).toContain('${config.id}_loop');
|
|
87
87
|
expect(names).toContain('${config.id}_orchestrate');
|
|
88
88
|
expect(names).toContain('${config.id}_control');
|
|
89
|
-
expect(names).toContain('${config.id}_cognee');
|
|
90
89
|
});
|
|
91
90
|
|
|
92
91
|
it('total ops across all facades should meet minimum threshold', () => {
|
|
@@ -273,16 +272,6 @@ ${domainPackDescribes ? `\n${domainPackDescribes}\n` : ''}
|
|
|
273
272
|
});
|
|
274
273
|
});
|
|
275
274
|
|
|
276
|
-
describe('${config.id}_cognee', () => {
|
|
277
|
-
it('should contain cognee ops', () => {
|
|
278
|
-
const facade = createSemanticFacades(runtime, '${config.id}').find(f => f.name === '${config.id}_cognee')!;
|
|
279
|
-
const opNames = facade.ops.map(o => o.name);
|
|
280
|
-
expect(opNames).toContain('cognee_status');
|
|
281
|
-
expect(opNames).toContain('cognee_search');
|
|
282
|
-
expect(opNames).toContain('cognee_sync_status');
|
|
283
|
-
});
|
|
284
|
-
});
|
|
285
|
-
|
|
286
275
|
describe('${config.id}_core (agent-specific)', () => {
|
|
287
276
|
function buildAgentFacade(): FacadeConfig {
|
|
288
277
|
const agentOps: OpDefinition[] = [
|
package/src/types.ts
CHANGED
|
@@ -31,10 +31,10 @@ export const AgentConfigSchema = z.object({
|
|
|
31
31
|
role: z.string().min(1).max(100),
|
|
32
32
|
/** Longer description of what this agent does */
|
|
33
33
|
description: z.string().min(10).max(500),
|
|
34
|
-
/** Knowledge domains this agent covers */
|
|
35
|
-
domains: z.array(z.string().min(1)).
|
|
36
|
-
/** Core principles the agent follows (
|
|
37
|
-
principles: z.array(z.string()).
|
|
34
|
+
/** Knowledge domains this agent covers (discovered from usage if empty) */
|
|
35
|
+
domains: z.array(z.string().min(1)).max(20).optional().default([]),
|
|
36
|
+
/** Core principles the agent follows (discovered from usage if empty) */
|
|
37
|
+
principles: z.array(z.string()).max(10).optional().default([]),
|
|
38
38
|
/** Communication tone: precise, mentor, or pragmatic */
|
|
39
39
|
tone: z.enum(TONES).optional().default('pragmatic'),
|
|
40
40
|
/** Greeting message when agent introduces itself (auto-generated if omitted) */
|
|
@@ -51,8 +51,6 @@ export const AgentConfigSchema = z.object({
|
|
|
51
51
|
setupTarget: z.enum(SETUP_TARGETS).optional().default('claude'),
|
|
52
52
|
/** Enable Telegram transport scaffolding. Default: false. */
|
|
53
53
|
telegram: z.boolean().optional().default(false),
|
|
54
|
-
/** Enable Cognee vector search integration. Default: false. */
|
|
55
|
-
cognee: z.boolean().optional().default(false),
|
|
56
54
|
/** Domain packs — npm packages with custom ops, knowledge, rules, and skills. */
|
|
57
55
|
domainPacks: z
|
|
58
56
|
.array(
|
|
@@ -78,6 +76,8 @@ export const AgentConfigSchema = z.object({
|
|
|
78
76
|
.optional(),
|
|
79
77
|
/** @deprecated Use vaults[] instead. Shorthand for a single shared vault at priority 0.6. */
|
|
80
78
|
sharedVaultPath: z.string().optional(),
|
|
79
|
+
/** Composable persona configuration. If omitted, Italian Craftsperson default is used. */
|
|
80
|
+
persona: z.record(z.unknown()).optional(),
|
|
81
81
|
});
|
|
82
82
|
|
|
83
83
|
export type AgentConfig = z.infer<typeof AgentConfigSchema>;
|