@soleri/forge 9.7.2 → 9.9.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 +177 -6
- package/dist/agent-schema.js +58 -0
- package/dist/agent-schema.js.map +1 -1
- package/dist/compose-claude-md.js +56 -3
- package/dist/compose-claude-md.js.map +1 -1
- package/dist/domain-manager.d.ts +1 -0
- package/dist/domain-manager.js +57 -1
- package/dist/domain-manager.js.map +1 -1
- package/dist/knowledge-installer.d.ts +2 -0
- package/dist/knowledge-installer.js +107 -1
- package/dist/knowledge-installer.js.map +1 -1
- package/dist/lib.d.ts +1 -1
- package/dist/lib.js +1 -1
- package/dist/lib.js.map +1 -1
- package/dist/scaffold-filetree.d.ts +12 -0
- package/dist/scaffold-filetree.js +356 -10
- package/dist/scaffold-filetree.js.map +1 -1
- package/dist/scaffolder.js +12 -0
- package/dist/scaffolder.js.map +1 -1
- package/dist/skills/subagent-driven-development/SKILL.md +87 -20
- package/dist/templates/setup-script.js +71 -0
- package/dist/templates/setup-script.js.map +1 -1
- package/dist/templates/shared-rules.js +163 -6
- package/dist/templates/shared-rules.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/domain-manager.test.ts +140 -0
- package/src/__tests__/scaffold-filetree.test.ts +326 -1
- package/src/__tests__/scaffolder.test.ts +7 -5
- package/src/__tests__/shared-rules.test.ts +48 -0
- package/src/agent-schema.ts +66 -0
- package/src/compose-claude-md.ts +63 -3
- package/src/domain-manager.ts +74 -1
- package/src/knowledge-installer.ts +124 -1
- package/src/lib.ts +6 -1
- package/src/scaffold-filetree.ts +404 -10
- package/src/scaffolder.ts +17 -0
- package/src/skills/subagent-driven-development/SKILL.md +87 -20
- package/src/templates/setup-script.ts +71 -0
- package/src/templates/shared-rules.ts +166 -6
package/src/agent-schema.ts
CHANGED
|
@@ -145,6 +145,32 @@ export const WorkflowDefinitionSchema = z.object({
|
|
|
145
145
|
verificationCriteria: z.array(z.string()).optional().default([]),
|
|
146
146
|
});
|
|
147
147
|
|
|
148
|
+
// ─── Workspace & Routing Schemas ─────────────────────────────────────
|
|
149
|
+
|
|
150
|
+
/** Workspace definition — scoped context area within an agent */
|
|
151
|
+
export const WorkspaceSchema = z.object({
|
|
152
|
+
/** Unique workspace identifier (kebab-case) */
|
|
153
|
+
id: z.string().min(1),
|
|
154
|
+
/** Human-readable workspace name */
|
|
155
|
+
name: z.string().min(1),
|
|
156
|
+
/** What this workspace is for */
|
|
157
|
+
description: z.string().min(1),
|
|
158
|
+
/** Context file name within the workspace directory. Default: CONTEXT.md */
|
|
159
|
+
contextFile: z.string().optional().default('CONTEXT.md'),
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
/** Routing entry — maps task patterns to workspaces */
|
|
163
|
+
export const RoutingEntrySchema = z.object({
|
|
164
|
+
/** Task pattern that triggers this route (e.g., "write script", "review code") */
|
|
165
|
+
pattern: z.string().min(1),
|
|
166
|
+
/** Target workspace id */
|
|
167
|
+
workspace: z.string().min(1),
|
|
168
|
+
/** Extra context files to load for this route */
|
|
169
|
+
context: z.array(z.string()).optional().default([]),
|
|
170
|
+
/** Skills to activate for this route */
|
|
171
|
+
skills: z.array(z.string()).optional().default([]),
|
|
172
|
+
});
|
|
173
|
+
|
|
148
174
|
// ─── Main Agent Schema ────────────────────────────────────────────────
|
|
149
175
|
|
|
150
176
|
/**
|
|
@@ -187,13 +213,53 @@ export const AgentYamlSchema = z.object({
|
|
|
187
213
|
/** LLM client integration settings */
|
|
188
214
|
setup: SetupConfigSchema.optional().default({}),
|
|
189
215
|
|
|
216
|
+
// ─── Skills ─────────────────────────────────────
|
|
217
|
+
/**
|
|
218
|
+
* Controls which skills are scaffolded.
|
|
219
|
+
* - 'essential' (default): ~7 core skills for a lightweight start
|
|
220
|
+
* - 'all': scaffold all available skills (backward compat)
|
|
221
|
+
* - string[]: scaffold only the named skills
|
|
222
|
+
*/
|
|
223
|
+
skillsFilter: z
|
|
224
|
+
.union([z.literal('all'), z.literal('essential'), z.array(z.string())])
|
|
225
|
+
.optional()
|
|
226
|
+
.default('essential'),
|
|
227
|
+
|
|
228
|
+
// ─── Workspaces & Routing ───────────────────────
|
|
229
|
+
/** Scoped context areas within the agent */
|
|
230
|
+
workspaces: z.array(WorkspaceSchema).optional(),
|
|
231
|
+
/** Task pattern → workspace routing table */
|
|
232
|
+
routing: z.array(RoutingEntrySchema).optional(),
|
|
233
|
+
|
|
190
234
|
// ─── Domain Packs ──────────────────────────────
|
|
191
235
|
/** npm domain packs with custom ops and knowledge */
|
|
192
236
|
packs: z.array(DomainPackSchema).optional(),
|
|
237
|
+
|
|
238
|
+
// ─── Git Initialization ────────────────────────
|
|
239
|
+
/** Git initialization configuration. If omitted, git is not initialized. */
|
|
240
|
+
git: z
|
|
241
|
+
.object({
|
|
242
|
+
/** Whether to run git init in the scaffolded agent directory */
|
|
243
|
+
init: z.boolean(),
|
|
244
|
+
/** Optional remote repository configuration */
|
|
245
|
+
remote: z
|
|
246
|
+
.object({
|
|
247
|
+
/** How to set up the remote: 'gh' creates via GitHub CLI, 'manual' uses a provided URL */
|
|
248
|
+
type: z.enum(['gh', 'manual']),
|
|
249
|
+
/** Remote URL (required for 'manual', auto-generated for 'gh') */
|
|
250
|
+
url: z.string().optional(),
|
|
251
|
+
/** Repository visibility for 'gh' type. Default: 'private' */
|
|
252
|
+
visibility: z.enum(['public', 'private']).optional().default('private'),
|
|
253
|
+
})
|
|
254
|
+
.optional(),
|
|
255
|
+
})
|
|
256
|
+
.optional(),
|
|
193
257
|
});
|
|
194
258
|
|
|
195
259
|
export type AgentYaml = z.infer<typeof AgentYamlSchema>;
|
|
196
260
|
export type AgentYamlInput = z.input<typeof AgentYamlSchema>;
|
|
261
|
+
export type Workspace = z.infer<typeof WorkspaceSchema>;
|
|
262
|
+
export type RoutingEntry = z.infer<typeof RoutingEntrySchema>;
|
|
197
263
|
export type WorkflowDefinition = z.infer<typeof WorkflowDefinitionSchema>;
|
|
198
264
|
export type WorkflowGate = z.infer<typeof WorkflowGateSchema>;
|
|
199
265
|
export type WorkflowTaskTemplate = z.infer<typeof WorkflowTaskTemplateSchema>;
|
package/src/compose-claude-md.ts
CHANGED
|
@@ -56,7 +56,26 @@ export function composeClaudeMd(agentDir: string, tools?: ToolEntry[]): Composed
|
|
|
56
56
|
// 5. Essential tools table
|
|
57
57
|
sections.push(composeToolsTable(agentYaml, tools));
|
|
58
58
|
|
|
59
|
-
// 6.
|
|
59
|
+
// 6. User custom instructions (instructions/user.md) — priority placement
|
|
60
|
+
// This file is user-editable and appears BEFORE engine rules and other instructions.
|
|
61
|
+
const userMdPath = join(agentDir, 'instructions', 'user.md');
|
|
62
|
+
if (existsSync(userMdPath)) {
|
|
63
|
+
const userContent = readFileSync(userMdPath, 'utf-8').trim();
|
|
64
|
+
if (userContent) {
|
|
65
|
+
sections.push(userContent);
|
|
66
|
+
sources.push(userMdPath);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// 6b. Workspaces section (if defined)
|
|
71
|
+
const workspacesSection = composeWorkspacesSection(agentYaml);
|
|
72
|
+
if (workspacesSection) sections.push(workspacesSection);
|
|
73
|
+
|
|
74
|
+
// 6c. Routing table (if defined)
|
|
75
|
+
const routingSection = composeRoutingTable(agentYaml);
|
|
76
|
+
if (routingSection) sections.push(routingSection);
|
|
77
|
+
|
|
78
|
+
// 7. Engine rules — NOT inlined (they are injected once into ~/.claude/CLAUDE.md
|
|
60
79
|
// or project CLAUDE.md via `soleri install`). Including them here would
|
|
61
80
|
// triple-load the rules (~8k tokens duplicated per layer).
|
|
62
81
|
// We emit a short reference so the agent knows rules exist.
|
|
@@ -72,11 +91,11 @@ export function composeClaudeMd(agentDir: string, tools?: ToolEntry[]): Composed
|
|
|
72
91
|
sources.push(enginePath);
|
|
73
92
|
}
|
|
74
93
|
|
|
75
|
-
//
|
|
94
|
+
// 8. User instructions (instructions/*.md, excluding _engine.md and user.md)
|
|
76
95
|
const instructionsDir = join(agentDir, 'instructions');
|
|
77
96
|
if (existsSync(instructionsDir)) {
|
|
78
97
|
const files = readdirSync(instructionsDir)
|
|
79
|
-
.filter((f) => f.endsWith('.md') && f !== '_engine.md')
|
|
98
|
+
.filter((f) => f.endsWith('.md') && f !== '_engine.md' && f !== 'user.md')
|
|
80
99
|
.sort();
|
|
81
100
|
for (const file of files) {
|
|
82
101
|
const filePath = join(instructionsDir, file);
|
|
@@ -198,6 +217,47 @@ function composeToolsTable(agent: AgentYaml, tools?: ToolEntry[]): string {
|
|
|
198
217
|
return lines.join('\n');
|
|
199
218
|
}
|
|
200
219
|
|
|
220
|
+
function composeWorkspacesSection(agent: AgentYaml): string | null {
|
|
221
|
+
if (!agent.workspaces || agent.workspaces.length === 0) return null;
|
|
222
|
+
|
|
223
|
+
const lines: string[] = [
|
|
224
|
+
'## Workspaces',
|
|
225
|
+
'',
|
|
226
|
+
'Scoped context areas — each workspace has its own CONTEXT.md with task-specific instructions.',
|
|
227
|
+
'',
|
|
228
|
+
'| Workspace | Description |',
|
|
229
|
+
'|-----------|-------------|',
|
|
230
|
+
];
|
|
231
|
+
|
|
232
|
+
for (const ws of agent.workspaces) {
|
|
233
|
+
lines.push(`| \`${ws.id}\` | ${ws.description} |`);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return lines.join('\n');
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function composeRoutingTable(agent: AgentYaml): string | null {
|
|
240
|
+
if (!agent.routing || agent.routing.length === 0) return null;
|
|
241
|
+
|
|
242
|
+
const lines: string[] = [
|
|
243
|
+
'## Task Routing',
|
|
244
|
+
'',
|
|
245
|
+
'When a task matches a pattern below, navigate to the target workspace, load its CONTEXT.md, and activate the listed skills.',
|
|
246
|
+
'If no pattern matches, use the default root context.',
|
|
247
|
+
'',
|
|
248
|
+
'| Task Pattern | Route To | Context | Skills |',
|
|
249
|
+
'|--------------|----------|---------|--------|',
|
|
250
|
+
];
|
|
251
|
+
|
|
252
|
+
for (const route of agent.routing) {
|
|
253
|
+
const ctx = route.context.length > 0 ? route.context.join(', ') : '—';
|
|
254
|
+
const skills = route.skills.length > 0 ? route.skills.map((s) => `\`${s}\``).join(', ') : '—';
|
|
255
|
+
lines.push(`| ${route.pattern} | \`${route.workspace}\` | ${ctx} | ${skills} |`);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return lines.join('\n');
|
|
259
|
+
}
|
|
260
|
+
|
|
201
261
|
function composeWorkflowIndex(workflowsDir: string): string | null {
|
|
202
262
|
const dirs = readdirSync(workflowsDir, { withFileTypes: true })
|
|
203
263
|
.filter((d) => d.isDirectory())
|
package/src/domain-manager.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
9
9
|
import { join } from 'node:path';
|
|
10
10
|
import { execFileSync } from 'node:child_process';
|
|
11
|
+
import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
|
|
11
12
|
import { generateDomainFacade } from './templates/domain-facade.js';
|
|
12
13
|
import { generateVaultOnlyDomainFacade } from './knowledge-installer.js';
|
|
13
14
|
import { patchIndexTs, patchClaudeMdContent } from './patching.js';
|
|
@@ -17,6 +18,7 @@ interface AddDomainParams {
|
|
|
17
18
|
agentPath: string;
|
|
18
19
|
domain: string;
|
|
19
20
|
noBuild?: boolean;
|
|
21
|
+
format?: 'filetree' | 'typescript';
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
/**
|
|
@@ -41,9 +43,17 @@ function isV5Agent(agentPath: string): boolean {
|
|
|
41
43
|
* 6. Rebuild (unless noBuild)
|
|
42
44
|
*/
|
|
43
45
|
export async function addDomain(params: AddDomainParams): Promise<AddDomainResult> {
|
|
44
|
-
const { agentPath, domain, noBuild = false } = params;
|
|
46
|
+
const { agentPath, domain, noBuild = false, format } = params;
|
|
45
47
|
const warnings: string[] = [];
|
|
46
48
|
|
|
49
|
+
// ── File-tree agent path ──
|
|
50
|
+
|
|
51
|
+
if (format === 'filetree') {
|
|
52
|
+
return addDomainFileTree(agentPath, domain);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ── TypeScript agent path (default / backward compat) ──
|
|
56
|
+
|
|
47
57
|
// ── Validate agent ──
|
|
48
58
|
|
|
49
59
|
const pkgPath = join(agentPath, 'package.json');
|
|
@@ -174,6 +184,69 @@ export async function addDomain(params: AddDomainParams): Promise<AddDomainResul
|
|
|
174
184
|
};
|
|
175
185
|
}
|
|
176
186
|
|
|
187
|
+
/**
|
|
188
|
+
* Add a domain to a file-tree agent (agent.yaml + knowledge/).
|
|
189
|
+
* No facade generation, no src/ patching, no build step.
|
|
190
|
+
*/
|
|
191
|
+
function addDomainFileTree(agentPath: string, domain: string): AddDomainResult {
|
|
192
|
+
// ── Validate domain name ──
|
|
193
|
+
|
|
194
|
+
if (!/^[a-z][a-z0-9-]*$/.test(domain)) {
|
|
195
|
+
return fail(agentPath, domain, `Invalid domain name "${domain}" — must be kebab-case`);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// ── Read and validate agent.yaml ──
|
|
199
|
+
|
|
200
|
+
const yamlPath = join(agentPath, 'agent.yaml');
|
|
201
|
+
if (!existsSync(yamlPath)) {
|
|
202
|
+
return fail(agentPath, domain, 'No agent.yaml found — is this a file-tree agent?');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
let agentYaml: Record<string, unknown>;
|
|
206
|
+
try {
|
|
207
|
+
agentYaml = parseYaml(readFileSync(yamlPath, 'utf-8')) as Record<string, unknown>;
|
|
208
|
+
} catch {
|
|
209
|
+
return fail(agentPath, domain, 'Failed to parse agent.yaml — is it valid YAML?');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const agentId = (agentYaml.id as string) ?? '';
|
|
213
|
+
if (!agentId) {
|
|
214
|
+
return fail(agentPath, domain, 'agent.yaml is missing an "id" field');
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// ── Check if domain already exists ──
|
|
218
|
+
|
|
219
|
+
const domains: string[] = Array.isArray(agentYaml.domains) ? (agentYaml.domains as string[]) : [];
|
|
220
|
+
if (domains.includes(domain)) {
|
|
221
|
+
return fail(agentPath, domain, `Domain "${domain}" already exists in agent.yaml`);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// ── Update agent.yaml domains array ──
|
|
225
|
+
|
|
226
|
+
agentYaml.domains = [...domains, domain];
|
|
227
|
+
writeFileSync(yamlPath, stringifyYaml(agentYaml), 'utf-8');
|
|
228
|
+
|
|
229
|
+
// ── Create knowledge/{domain}.json ──
|
|
230
|
+
|
|
231
|
+
const knowledgeDir = join(agentPath, 'knowledge');
|
|
232
|
+
mkdirSync(knowledgeDir, { recursive: true });
|
|
233
|
+
|
|
234
|
+
const bundlePath = join(knowledgeDir, `${domain}.json`);
|
|
235
|
+
const emptyBundle = JSON.stringify({ domain, entries: [] }, null, 2);
|
|
236
|
+
writeFileSync(bundlePath, emptyBundle, 'utf-8');
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
success: true,
|
|
240
|
+
agentPath,
|
|
241
|
+
domain,
|
|
242
|
+
agentId,
|
|
243
|
+
facadeGenerated: false,
|
|
244
|
+
buildOutput: '',
|
|
245
|
+
warnings: [],
|
|
246
|
+
summary: `Added domain "${domain}" to ${agentId} (file-tree agent)`,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
177
250
|
function fail(agentPath: string, domain: string, message: string): AddDomainResult {
|
|
178
251
|
return {
|
|
179
252
|
success: false,
|
|
@@ -154,16 +154,25 @@ interface InstallKnowledgeParams {
|
|
|
154
154
|
agentPath: string;
|
|
155
155
|
bundlePath: string;
|
|
156
156
|
generateFacades?: boolean;
|
|
157
|
+
/** Agent format: 'filetree' skips package.json check and src/ patching */
|
|
158
|
+
format?: 'filetree' | 'typescript';
|
|
157
159
|
}
|
|
158
160
|
|
|
159
161
|
export async function installKnowledge(
|
|
160
162
|
params: InstallKnowledgeParams,
|
|
161
163
|
): Promise<InstallKnowledgeResult> {
|
|
162
|
-
const { agentPath, bundlePath, generateFacades = true } = params;
|
|
164
|
+
const { agentPath, bundlePath, generateFacades = true, format } = params;
|
|
163
165
|
const warnings: string[] = [];
|
|
164
166
|
const facadesGenerated: string[] = [];
|
|
165
167
|
const sourceFilesPatched: string[] = [];
|
|
166
168
|
|
|
169
|
+
// ── File-tree agent path ─────────────────────────────────────────
|
|
170
|
+
if (format === 'filetree') {
|
|
171
|
+
return installKnowledgeFiletree(agentPath, bundlePath);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ── TypeScript agent path (existing behavior) ────────────────────
|
|
175
|
+
|
|
167
176
|
// ── Step 1: Validate agent path ──────────────────────────────────
|
|
168
177
|
|
|
169
178
|
const pkgPath = join(agentPath, 'package.json');
|
|
@@ -363,6 +372,120 @@ export async function installKnowledge(
|
|
|
363
372
|
};
|
|
364
373
|
}
|
|
365
374
|
|
|
375
|
+
// ---------- File-tree agent installer ----------
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Install knowledge bundles into a file-tree agent.
|
|
379
|
+
* Writes to {agentPath}/knowledge/ — no package.json, no src/ patching, no build step.
|
|
380
|
+
* The engine picks up knowledge bundles from this directory at runtime.
|
|
381
|
+
*/
|
|
382
|
+
async function installKnowledgeFiletree(
|
|
383
|
+
agentPath: string,
|
|
384
|
+
bundlePath: string,
|
|
385
|
+
): Promise<InstallKnowledgeResult> {
|
|
386
|
+
const warnings: string[] = [];
|
|
387
|
+
|
|
388
|
+
// Derive agentId from agent.yaml
|
|
389
|
+
let agentId = '';
|
|
390
|
+
const yamlPath = join(agentPath, 'agent.yaml');
|
|
391
|
+
if (existsSync(yamlPath)) {
|
|
392
|
+
try {
|
|
393
|
+
const raw = readFileSync(yamlPath, 'utf-8');
|
|
394
|
+
// Simple extraction — avoid importing yaml parser here
|
|
395
|
+
const idMatch = raw.match(/^id:\s*["']?([^\s"']+)/m);
|
|
396
|
+
if (idMatch) agentId = idMatch[1];
|
|
397
|
+
} catch {
|
|
398
|
+
// best-effort
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
if (!agentId) {
|
|
402
|
+
return fail(agentPath, '', 'No agent.yaml with valid id found — is this a file-tree agent?');
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Read and validate bundles
|
|
406
|
+
const bundleFiles = collectBundleFiles(bundlePath);
|
|
407
|
+
if (bundleFiles.length === 0) {
|
|
408
|
+
return fail(agentPath, agentId, `No .json bundle files found at ${bundlePath}`);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const bundles: Array<{ file: string; bundle: Bundle }> = [];
|
|
412
|
+
const issues: string[] = [];
|
|
413
|
+
|
|
414
|
+
for (const file of bundleFiles) {
|
|
415
|
+
try {
|
|
416
|
+
const raw = readFileSync(file, 'utf-8');
|
|
417
|
+
const parsed = JSON.parse(raw) as Bundle;
|
|
418
|
+
const fileIssues = validateBundle(parsed, file);
|
|
419
|
+
if (fileIssues.length > 0) {
|
|
420
|
+
issues.push(...fileIssues);
|
|
421
|
+
} else {
|
|
422
|
+
bundles.push({ file, bundle: parsed });
|
|
423
|
+
}
|
|
424
|
+
} catch (err) {
|
|
425
|
+
issues.push(
|
|
426
|
+
`${basename(file)}: invalid JSON — ${err instanceof Error ? err.message : String(err)}`,
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
if (bundles.length === 0) {
|
|
432
|
+
return fail(agentPath, agentId, `All bundles failed validation:\n${issues.join('\n')}`);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if (issues.length > 0) {
|
|
436
|
+
warnings.push(...issues);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Determine new vs existing domains
|
|
440
|
+
const knowledgeDir = join(agentPath, 'knowledge');
|
|
441
|
+
mkdirSync(knowledgeDir, { recursive: true });
|
|
442
|
+
|
|
443
|
+
const existingFiles = readdirSync(knowledgeDir).filter((f) => f.endsWith('.json'));
|
|
444
|
+
const existingDomains = new Set(existingFiles.map((f) => f.replace(/\.json$/, '')));
|
|
445
|
+
const domainsAdded: string[] = [];
|
|
446
|
+
const domainsUpdated: string[] = [];
|
|
447
|
+
|
|
448
|
+
for (const { bundle } of bundles) {
|
|
449
|
+
if (existingDomains.has(bundle.domain)) {
|
|
450
|
+
domainsUpdated.push(bundle.domain);
|
|
451
|
+
} else {
|
|
452
|
+
domainsAdded.push(bundle.domain);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// Copy bundles to knowledge/
|
|
457
|
+
for (const { file, bundle } of bundles) {
|
|
458
|
+
const dest = join(knowledgeDir, `${bundle.domain}.json`);
|
|
459
|
+
copyFileSync(file, dest);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// No facade generation, no src/ patching, no build step for file-tree agents
|
|
463
|
+
|
|
464
|
+
const entriesTotal = bundles.reduce((sum, { bundle }) => sum + bundle.entries.length, 0);
|
|
465
|
+
|
|
466
|
+
const summaryParts = [
|
|
467
|
+
`Installed ${bundles.length} bundle(s) with ${entriesTotal} entries into ${agentId} (file-tree)`,
|
|
468
|
+
];
|
|
469
|
+
if (domainsAdded.length > 0) summaryParts.push(`New domains: ${domainsAdded.join(', ')}`);
|
|
470
|
+
if (domainsUpdated.length > 0) summaryParts.push(`Updated domains: ${domainsUpdated.join(', ')}`);
|
|
471
|
+
if (warnings.length > 0) summaryParts.push(`${warnings.length} warning(s)`);
|
|
472
|
+
|
|
473
|
+
return {
|
|
474
|
+
success: true,
|
|
475
|
+
agentPath,
|
|
476
|
+
agentId,
|
|
477
|
+
bundlesInstalled: bundles.length,
|
|
478
|
+
entriesTotal,
|
|
479
|
+
domainsAdded,
|
|
480
|
+
domainsUpdated,
|
|
481
|
+
facadesGenerated: [],
|
|
482
|
+
sourceFilesPatched: [],
|
|
483
|
+
buildOutput: '',
|
|
484
|
+
warnings,
|
|
485
|
+
summary: summaryParts.join('. ') + '.',
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
|
|
366
489
|
// ---------- Helpers ----------
|
|
367
490
|
|
|
368
491
|
function fail(agentPath: string, agentId: string, message: string): InstallKnowledgeResult {
|
package/src/lib.ts
CHANGED
|
@@ -21,7 +21,12 @@ export type {
|
|
|
21
21
|
export { AgentConfigSchema, SETUP_TARGETS, MODEL_PRESETS } from './types.js';
|
|
22
22
|
|
|
23
23
|
// ─── v7 File-Tree Agent ──────────────────────────────────────────────
|
|
24
|
-
export {
|
|
24
|
+
export {
|
|
25
|
+
scaffoldFileTree,
|
|
26
|
+
SKILLS_REGISTRY,
|
|
27
|
+
ESSENTIAL_SKILLS,
|
|
28
|
+
resolveSkillsFilter,
|
|
29
|
+
} from './scaffold-filetree.js';
|
|
25
30
|
export type { FileTreeScaffoldResult } from './scaffold-filetree.js';
|
|
26
31
|
export { AgentYamlSchema, TONES } from './agent-schema.js';
|
|
27
32
|
export type { AgentYaml, AgentYamlInput } from './agent-schema.js';
|