@forwardimpact/pathway 0.25.12 → 0.25.20

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.
@@ -24,9 +24,6 @@
24
24
  * bunx pathway agent software_engineering --track=platform --output=./agents
25
25
  */
26
26
 
27
- import { writeFile, mkdir, readFile } from "fs/promises";
28
- import { join, dirname } from "path";
29
- import { existsSync } from "fs";
30
27
  import { createDataLoader } from "@forwardimpact/map/loader";
31
28
  import {
32
29
  generateStageAgentProfile,
@@ -41,47 +38,14 @@ import {
41
38
  interpolateTeamInstructions,
42
39
  } from "@forwardimpact/libskill";
43
40
  import { formatAgentProfile } from "../formatters/agent/profile.js";
44
- import {
45
- formatAgentSkill,
46
- formatInstallScript,
47
- formatReference,
48
- } from "../formatters/agent/skill.js";
49
41
  import { formatError, formatSuccess } from "../lib/cli-output.js";
50
42
  import { toolkitToPlainList } from "../formatters/toolkit/markdown.js";
51
-
52
- /**
53
- * Ensure directory exists for a file path
54
- * @param {string} filePath - Full file path
55
- */
56
- async function ensureDir(filePath) {
57
- await mkdir(dirname(filePath), { recursive: true });
58
- }
59
-
60
- /**
61
- * Generate Claude Code settings file
62
- * Merges with existing settings if file exists
63
- * @param {string} baseDir - Base output directory
64
- * @param {Object} claudeCodeSettings - Settings loaded from data
65
- */
66
- async function generateClaudeCodeSettings(baseDir, claudeCodeSettings) {
67
- const settingsPath = join(baseDir, ".claude", "settings.json");
68
-
69
- let settings = {};
70
- if (existsSync(settingsPath)) {
71
- const content = await readFile(settingsPath, "utf-8");
72
- settings = JSON.parse(content);
73
- }
74
-
75
- const merged = { ...settings, ...claudeCodeSettings };
76
-
77
- await ensureDir(settingsPath);
78
- await writeFile(
79
- settingsPath,
80
- JSON.stringify(merged, null, 2) + "\n",
81
- "utf-8",
82
- );
83
- console.log(formatSuccess(`Updated: ${settingsPath}`));
84
- }
43
+ import {
44
+ generateClaudeCodeSettings,
45
+ writeProfile,
46
+ writeTeamInstructions,
47
+ writeSkills,
48
+ } from "./agent-io.js";
85
49
 
86
50
  /**
87
51
  * Show agent summary with stats
@@ -125,34 +89,54 @@ function showAgentSummary(data, agentData, skillsWithAgent) {
125
89
  }
126
90
 
127
91
  /**
128
- * List available agent combinations (clean output for piping)
92
+ * Find valid agent combination pairs
129
93
  * @param {Object} data - Pathway data
130
94
  * @param {Object} agentData - Agent-specific data
131
- * @param {boolean} verbose - Show verbose output
95
+ * @returns {Array<{discipline: Object, track: Object, humanDiscipline: Object, humanTrack: Object}>}
132
96
  */
133
- function listAgentCombinations(data, agentData, verbose = false) {
134
- if (!verbose) {
135
- // Descriptive output for piping and AI agent discovery
136
- for (const discipline of agentData.disciplines) {
137
- for (const track of agentData.tracks) {
138
- const humanDiscipline = data.disciplines.find(
139
- (d) => d.id === discipline.id,
140
- );
141
- const humanTrack = data.tracks.find((t) => t.id === track.id);
142
- if (humanDiscipline && humanTrack) {
143
- const abbrev = getDisciplineAbbreviation(discipline.id);
144
- const agentName = `${abbrev}-${toKebabCase(track.id)}`;
145
- const specName = humanDiscipline.specialization || humanDiscipline.id;
146
- console.log(
147
- `${agentName} ${discipline.id} ${track.id}, ${specName} (${humanTrack.name})`,
148
- );
149
- }
97
+ function findValidCombinations(data, agentData) {
98
+ const pairs = [];
99
+ for (const discipline of agentData.disciplines) {
100
+ for (const track of agentData.tracks) {
101
+ const humanDiscipline = data.disciplines.find(
102
+ (d) => d.id === discipline.id,
103
+ );
104
+ const humanTrack = data.tracks.find((t) => t.id === track.id);
105
+ if (humanDiscipline && humanTrack) {
106
+ pairs.push({ discipline, track, humanDiscipline, humanTrack });
150
107
  }
151
108
  }
152
- return;
153
109
  }
110
+ return pairs;
111
+ }
112
+
113
+ /**
114
+ * List available agent combinations — compact output for piping
115
+ * @param {Object} data - Pathway data
116
+ * @param {Object} agentData - Agent-specific data
117
+ */
118
+ function listAgentCombinationsCompact(data, agentData) {
119
+ for (const {
120
+ discipline,
121
+ track,
122
+ humanDiscipline,
123
+ humanTrack,
124
+ } of findValidCombinations(data, agentData)) {
125
+ const abbrev = getDisciplineAbbreviation(discipline.id);
126
+ const agentName = `${abbrev}-${toKebabCase(track.id)}`;
127
+ const specName = humanDiscipline.specialization || humanDiscipline.id;
128
+ console.log(
129
+ `${agentName} ${discipline.id} ${track.id}, ${specName} (${humanTrack.name})`,
130
+ );
131
+ }
132
+ }
154
133
 
155
- // Verbose output
134
+ /**
135
+ * List available agent combinations — verbose output
136
+ * @param {Object} data - Pathway data
137
+ * @param {Object} agentData - Agent-specific data
138
+ */
139
+ function listAgentCombinationsVerbose(data, agentData) {
156
140
  console.log("# 🤖 Available Agent Combinations\n");
157
141
 
158
142
  const agentDisciplineIds = new Set(agentData.disciplines.map((d) => d.id));
@@ -160,8 +144,7 @@ function listAgentCombinations(data, agentData, verbose = false) {
160
144
 
161
145
  console.log("## Disciplines with agent definitions:\n");
162
146
  for (const discipline of data.disciplines) {
163
- const hasAgent = agentDisciplineIds.has(discipline.id);
164
- const status = hasAgent ? "✅" : "⬜";
147
+ const status = agentDisciplineIds.has(discipline.id) ? "✅" : "⬜";
165
148
  console.log(
166
149
  ` ${status} ${discipline.id} - ${discipline.specialization || discipline.name}`,
167
150
  );
@@ -169,22 +152,13 @@ function listAgentCombinations(data, agentData, verbose = false) {
169
152
 
170
153
  console.log("\n## Tracks with agent definitions:\n");
171
154
  for (const track of data.tracks) {
172
- const hasAgent = agentTrackIds.has(track.id);
173
- const status = hasAgent ? "✅" : "⬜";
155
+ const status = agentTrackIds.has(track.id) ? "✅" : "⬜";
174
156
  console.log(` ${status} ${track.id} - ${track.name}`);
175
157
  }
176
158
 
177
159
  console.log("\n## Valid combinations:\n");
178
- for (const discipline of agentData.disciplines) {
179
- for (const track of agentData.tracks) {
180
- const humanDiscipline = data.disciplines.find(
181
- (d) => d.id === discipline.id,
182
- );
183
- const humanTrack = data.tracks.find((t) => t.id === track.id);
184
- if (humanDiscipline && humanTrack) {
185
- console.log(` bunx pathway agent ${discipline.id} ${track.id}`);
186
- }
187
- }
160
+ for (const { discipline, track } of findValidCombinations(data, agentData)) {
161
+ console.log(` bunx pathway agent ${discipline.id} ${track.id}`);
188
162
  }
189
163
 
190
164
  console.log("\n## Available stages:\n");
@@ -194,158 +168,44 @@ function listAgentCombinations(data, agentData, verbose = false) {
194
168
  }
195
169
 
196
170
  /**
197
- * Write agent profile to file
198
- * @param {Object} profile - Generated profile
199
- * @param {string} baseDir - Base output directory
200
- * @param {string} template - Mustache template for agent profile
201
- */
202
- async function writeProfile(profile, baseDir, template) {
203
- const profilePath = join(baseDir, ".claude", "agents", profile.filename);
204
- const profileContent = formatAgentProfile(profile, template);
205
- await ensureDir(profilePath);
206
- await writeFile(profilePath, profileContent, "utf-8");
207
- console.log(formatSuccess(`Created: ${profilePath}`));
208
- return profilePath;
209
- }
210
-
211
- /**
212
- * Write team instructions to CLAUDE.md
213
- * @param {string|null} teamInstructions - Interpolated team instructions content
214
- * @param {string} baseDir - Base output directory
215
- * @returns {string|null} Path written, or null if skipped
216
- */
217
- async function writeTeamInstructions(teamInstructions, baseDir) {
218
- if (!teamInstructions) return null;
219
- const filePath = join(baseDir, ".claude", "CLAUDE.md");
220
- await ensureDir(filePath);
221
- await writeFile(filePath, teamInstructions.trim() + "\n", "utf-8");
222
- console.log(formatSuccess(`Created: ${filePath}`));
223
- return filePath;
224
- }
225
-
226
- /**
227
- * Write skill files (SKILL.md, scripts/install.sh, references/REFERENCE.md)
228
- * @param {Array} skills - Generated skills
229
- * @param {string} baseDir - Base output directory
230
- * @param {Object} templates - Templates object with skill, install, reference
171
+ * List available agent combinations (clean output for piping)
172
+ * @param {Object} data - Pathway data
173
+ * @param {Object} agentData - Agent-specific data
174
+ * @param {boolean} verbose - Show verbose output
231
175
  */
232
- async function writeSkills(skills, baseDir, templates) {
233
- let fileCount = 0;
234
- for (const skill of skills) {
235
- const skillDir = join(baseDir, ".claude", "skills", skill.dirname);
236
-
237
- // Write SKILL.md (always)
238
- const skillPath = join(skillDir, "SKILL.md");
239
- const skillContent = formatAgentSkill(skill, templates.skill);
240
- await ensureDir(skillPath);
241
- await writeFile(skillPath, skillContent, "utf-8");
242
- console.log(formatSuccess(`Created: ${skillPath}`));
243
- fileCount++;
244
-
245
- // Write scripts/install.sh (only when installScript exists)
246
- if (skill.installScript) {
247
- const installPath = join(skillDir, "scripts", "install.sh");
248
- const installContent = formatInstallScript(skill, templates.install);
249
- await ensureDir(installPath);
250
- await writeFile(installPath, installContent, { mode: 0o755 });
251
- console.log(formatSuccess(`Created: ${installPath}`));
252
- fileCount++;
253
- }
254
-
255
- // Write references/REFERENCE.md (only when implementationReference exists)
256
- if (skill.implementationReference) {
257
- const refPath = join(skillDir, "references", "REFERENCE.md");
258
- const refContent = formatReference(skill, templates.reference);
259
- await ensureDir(refPath);
260
- await writeFile(refPath, refContent, "utf-8");
261
- console.log(formatSuccess(`Created: ${refPath}`));
262
- fileCount++;
263
- }
176
+ function listAgentCombinations(data, agentData, verbose = false) {
177
+ if (verbose) {
178
+ listAgentCombinationsVerbose(data, agentData);
179
+ } else {
180
+ listAgentCombinationsCompact(data, agentData);
264
181
  }
265
- return fileCount;
266
182
  }
267
183
 
268
184
  /**
269
- * Run the agent command
270
- * @param {Object} params - Command parameters
271
- * @param {Object} params.data - Loaded pathway data
272
- * @param {string[]} params.args - Command arguments [discipline_id]
273
- * @param {Object} params.options - Command options
274
- * @param {string} params.dataDir - Path to data directory
185
+ * Resolve and validate human + agent entities for a discipline/track pair
186
+ * @param {Object} data - Full pathway data
187
+ * @param {Object} agentData - Agent-specific data
188
+ * @param {string} disciplineId
189
+ * @param {string|null} trackId
190
+ * @returns {{humanDiscipline: Object, humanTrack: Object|null, agentDiscipline: Object, agentTrack: Object|null}}
275
191
  */
276
- export async function runAgentCommand({
277
- data,
278
- args,
279
- options,
280
- dataDir,
281
- templateLoader,
282
- loader,
283
- }) {
284
- // Load agent-specific data
285
- const dataLoader = loader || createDataLoader();
286
- const agentData = await dataLoader.loadAgentData(dataDir);
287
- const skillsWithAgent = await dataLoader.loadSkillsWithAgentData(dataDir);
288
-
289
- // --list: Output clean lines for piping
290
- if (options.list) {
291
- listAgentCombinations(data, agentData, false);
292
- return;
293
- }
294
-
295
- // No args: Show summary
296
- if (args.length === 0) {
297
- showAgentSummary(data, agentData, skillsWithAgent);
298
- return;
299
- }
300
-
301
- const [disciplineId] = args;
302
- const trackId = options.track;
303
-
304
- // Reject unexpected positional args (track must use --track=<id>)
305
- if (args.length > 1) {
306
- console.error(
307
- formatError(
308
- `Unexpected argument: ${args[1]}. Did you mean --track=${args[1]}?`,
309
- ),
310
- );
311
- process.exit(1);
312
- }
313
-
314
- if (!disciplineId) {
315
- console.error(
316
- formatError(
317
- "Usage: bunx pathway agent <discipline_id> [--track=<track_id>]",
318
- ),
319
- );
320
- console.error(
321
- "\nRun 'bunx pathway agent --list' to see available combinations.",
322
- );
323
- process.exit(1);
324
- }
325
-
326
- // Look up human definitions
192
+ function resolveAgentEntities(data, agentData, disciplineId, trackId) {
327
193
  const humanDiscipline = data.disciplines.find((d) => d.id === disciplineId);
328
194
  const humanTrack = trackId ? data.tracks.find((t) => t.id === trackId) : null;
329
195
 
330
196
  if (!humanDiscipline) {
331
197
  console.error(formatError(`Unknown discipline: ${disciplineId}`));
332
198
  console.error("\nAvailable disciplines:");
333
- for (const d of data.disciplines) {
334
- console.error(` - ${d.id}`);
335
- }
199
+ for (const d of data.disciplines) console.error(` - ${d.id}`);
336
200
  process.exit(1);
337
201
  }
338
-
339
202
  if (trackId && !humanTrack) {
340
203
  console.error(formatError(`Unknown track: ${trackId}`));
341
204
  console.error("\nAvailable tracks:");
342
- for (const t of data.tracks) {
343
- console.error(` - ${t.id}`);
344
- }
205
+ for (const t of data.tracks) console.error(` - ${t.id}`);
345
206
  process.exit(1);
346
207
  }
347
208
 
348
- // Look up agent definitions
349
209
  const agentDiscipline = agentData.disciplines.find(
350
210
  (d) => d.id === disciplineId,
351
211
  );
@@ -358,135 +218,109 @@ export async function runAgentCommand({
358
218
  formatError(`No agent definition for discipline: ${disciplineId}`),
359
219
  );
360
220
  console.error("\nAgent definitions exist for:");
361
- for (const d of agentData.disciplines) {
362
- console.error(` - ${d.id}`);
363
- }
221
+ for (const d of agentData.disciplines) console.error(` - ${d.id}`);
364
222
  process.exit(1);
365
223
  }
366
-
367
224
  if (trackId && !agentTrack) {
368
225
  console.error(formatError(`No agent definition for track: ${trackId}`));
369
226
  console.error("\nAgent definitions exist for:");
370
- for (const t of agentData.tracks) {
371
- console.error(` - ${t.id}`);
372
- }
227
+ for (const t of agentData.tracks) console.error(` - ${t.id}`);
373
228
  process.exit(1);
374
229
  }
375
230
 
376
- // Get reference level for derivation
377
- const level = deriveReferenceLevel(data.levels);
231
+ return { humanDiscipline, humanTrack, agentDiscipline, agentTrack };
232
+ }
378
233
 
379
- // --skills: Output plain list of skill IDs (for piping)
380
- if (options.skills) {
381
- const derivedSkills = deriveAgentSkills({
382
- discipline: humanDiscipline,
383
- track: humanTrack,
384
- level,
385
- skills: skillsWithAgent,
386
- });
387
- for (const skill of derivedSkills) {
388
- console.log(skill.skillId);
389
- }
390
- return;
234
+ /**
235
+ * Output team instructions to console if present
236
+ * @param {Object|null} agentTrack
237
+ * @param {Object} humanDiscipline
238
+ */
239
+ function printTeamInstructions(agentTrack, humanDiscipline) {
240
+ const teamInstructions = interpolateTeamInstructions(
241
+ agentTrack,
242
+ humanDiscipline,
243
+ );
244
+ if (teamInstructions) {
245
+ console.log("# Team Instructions (CLAUDE.md)\n");
246
+ console.log(teamInstructions.trim());
247
+ console.log("\n---\n");
391
248
  }
249
+ }
392
250
 
393
- // --tools: Output plain list of tool names (for piping)
394
- if (options.tools) {
395
- const derivedSkills = deriveAgentSkills({
396
- discipline: humanDiscipline,
397
- track: humanTrack,
398
- level,
399
- skills: skillsWithAgent,
400
- });
401
- const toolkit = deriveToolkit({
402
- skillMatrix: derivedSkills,
403
- skills: skillsWithAgent,
404
- });
405
- console.log(toolkitToPlainList(toolkit));
406
- return;
251
+ /**
252
+ * Handle single-stage agent generation
253
+ * @param {Object} params
254
+ */
255
+ async function handleSingleStage({
256
+ stageParams,
257
+ options,
258
+ data,
259
+ agentTrack,
260
+ humanDiscipline,
261
+ agentData,
262
+ templateLoader,
263
+ dataDir,
264
+ }) {
265
+ const stage = data.stages.find((s) => s.id === options.stage);
266
+ if (!stage) {
267
+ console.error(formatError(`Unknown stage: ${options.stage}`));
268
+ console.error("\nAvailable stages:");
269
+ for (const s of data.stages) console.error(` - ${s.id}`);
270
+ process.exit(1);
407
271
  }
408
272
 
409
- const baseDir = options.output || ".";
410
-
411
- // Common params for stage-based generation
412
- const stageParams = {
413
- discipline: humanDiscipline,
414
- track: humanTrack,
415
- level,
416
- skills: skillsWithAgent,
417
- behaviours: data.behaviours,
418
- agentBehaviours: agentData.behaviours,
419
- agentDiscipline,
420
- agentTrack,
421
- stages: data.stages,
422
- };
423
-
424
- // Handle --stage flag for single stage agent
425
- if (options.stage) {
426
- const stage = data.stages.find((s) => s.id === options.stage);
427
- if (!stage) {
428
- console.error(formatError(`Unknown stage: ${options.stage}`));
429
- console.error("\nAvailable stages:");
430
- for (const s of data.stages) {
431
- console.error(` - ${s.id}`);
432
- }
433
- process.exit(1);
434
- }
435
-
436
- const profile = generateStageAgentProfile({ ...stageParams, stage });
437
-
438
- // Validate
439
- const errors = validateAgentProfile(profile);
440
- if (errors.length > 0) {
441
- console.error(formatError("Profile validation failed:"));
442
- for (const err of errors) {
443
- console.error(` - ${err}`);
444
- }
445
- process.exit(1);
446
- }
447
-
448
- // Load template
449
- const agentTemplate = templateLoader.load("agent.template.md", dataDir);
273
+ const profile = generateStageAgentProfile({ ...stageParams, stage });
274
+ const errors = validateAgentProfile(profile);
275
+ if (errors.length > 0) {
276
+ console.error(formatError("Profile validation failed:"));
277
+ for (const err of errors) console.error(` - ${err}`);
278
+ process.exit(1);
279
+ }
450
280
 
451
- // Output to console (default) or write to files (with --output)
452
- if (!options.output) {
453
- const teamInstructions = interpolateTeamInstructions(
454
- agentTrack,
455
- humanDiscipline,
456
- );
457
- if (teamInstructions) {
458
- console.log("# Team Instructions (CLAUDE.md)\n");
459
- console.log(teamInstructions.trim());
460
- console.log("\n---\n");
461
- }
462
- console.log(formatAgentProfile(profile, agentTemplate));
463
- return;
464
- }
281
+ const agentTemplate = templateLoader.load("agent.template.md", dataDir);
282
+ const baseDir = options.output || ".";
465
283
 
466
- const teamInstructions = interpolateTeamInstructions(
467
- agentTrack,
468
- humanDiscipline,
469
- );
470
- await writeTeamInstructions(teamInstructions, baseDir);
471
- await writeProfile(profile, baseDir, agentTemplate);
472
- await generateClaudeCodeSettings(baseDir, agentData.claudeCodeSettings);
473
- console.log("");
474
- console.log(
475
- formatSuccess(`Generated stage agent: ${profile.frontmatter.name}`),
476
- );
284
+ if (!options.output) {
285
+ printTeamInstructions(agentTrack, humanDiscipline);
286
+ console.log(formatAgentProfile(profile, agentTemplate));
477
287
  return;
478
288
  }
479
289
 
480
- // Default behavior: generate all stage agents
481
- const profiles = [];
290
+ const teamInstructions = interpolateTeamInstructions(
291
+ agentTrack,
292
+ humanDiscipline,
293
+ );
294
+ await writeTeamInstructions(teamInstructions, baseDir);
295
+ await writeProfile(profile, baseDir, agentTemplate);
296
+ await generateClaudeCodeSettings(baseDir, agentData.claudeCodeSettings);
297
+ console.log("");
298
+ console.log(
299
+ formatSuccess(`Generated stage agent: ${profile.frontmatter.name}`),
300
+ );
301
+ }
482
302
 
483
- // Generate all stage agents
484
- for (const stage of data.stages) {
485
- const profile = generateStageAgentProfile({ ...stageParams, stage });
486
- profiles.push(profile);
487
- }
303
+ /**
304
+ * Handle all-stages agent generation
305
+ * @param {Object} params
306
+ */
307
+ async function handleAllStages({
308
+ stageParams,
309
+ options,
310
+ data,
311
+ agentTrack,
312
+ humanDiscipline,
313
+ humanTrack,
314
+ agentData,
315
+ skillsWithAgent,
316
+ level,
317
+ templateLoader,
318
+ dataDir,
319
+ }) {
320
+ const profiles = data.stages.map((stage) =>
321
+ generateStageAgentProfile({ ...stageParams, stage }),
322
+ );
488
323
 
489
- // Derive skills once for all stages
490
324
  const derivedSkills = deriveAgentSkills({
491
325
  discipline: humanDiscipline,
492
326
  track: humanTrack,
@@ -499,62 +333,39 @@ export async function runAgentCommand({
499
333
  .filter((skill) => skill?.agent)
500
334
  .map((skill) => generateSkillMarkdown(skill, data.stages));
501
335
 
502
- // Validate all profiles
503
336
  for (const profile of profiles) {
504
337
  const errors = validateAgentProfile(profile);
505
338
  if (errors.length > 0) {
506
339
  console.error(
507
340
  formatError(`Profile ${profile.frontmatter.name} validation failed:`),
508
341
  );
509
- for (const err of errors) {
510
- console.error(` - ${err}`);
511
- }
342
+ for (const err of errors) console.error(` - ${err}`);
512
343
  process.exit(1);
513
344
  }
514
345
  }
515
346
 
516
- // Validate all skills
517
347
  for (const skill of skillFiles) {
518
348
  const errors = validateAgentSkill(skill);
519
349
  if (errors.length > 0) {
520
350
  console.error(
521
351
  formatError(`Skill ${skill.frontmatter.name} validation failed:`),
522
352
  );
523
- for (const err of errors) {
524
- console.error(` - ${err}`);
525
- }
353
+ for (const err of errors) console.error(` - ${err}`);
526
354
  process.exit(1);
527
355
  }
528
356
  }
529
357
 
530
- // Load templates
531
358
  const agentTemplate = templateLoader.load("agent.template.md", dataDir);
532
- const skillTemplate = templateLoader.load("skill.template.md", dataDir);
533
- const installTemplate = templateLoader.load(
534
- "skill-install.template.sh",
535
- dataDir,
536
- );
537
- const referenceTemplate = templateLoader.load(
538
- "skill-reference.template.md",
539
- dataDir,
540
- );
541
359
  const skillTemplates = {
542
- skill: skillTemplate,
543
- install: installTemplate,
544
- reference: referenceTemplate,
360
+ skill: templateLoader.load("skill.template.md", dataDir),
361
+ install: templateLoader.load("skill-install.template.sh", dataDir),
362
+ reference: templateLoader.load("skill-reference.template.md", dataDir),
545
363
  };
546
364
 
547
- // Output to console (default) or write to files (with --output)
365
+ const baseDir = options.output || ".";
366
+
548
367
  if (!options.output) {
549
- const teamInstructions = interpolateTeamInstructions(
550
- agentTrack,
551
- humanDiscipline,
552
- );
553
- if (teamInstructions) {
554
- console.log("# Team Instructions (CLAUDE.md)\n");
555
- console.log(teamInstructions.trim());
556
- console.log("\n---\n");
557
- }
368
+ printTeamInstructions(agentTrack, humanDiscipline);
558
369
  for (const profile of profiles) {
559
370
  console.log(formatAgentProfile(profile, agentTemplate));
560
371
  console.log("\n---\n");
@@ -580,3 +391,109 @@ export async function runAgentCommand({
580
391
  }
581
392
  console.log(` Skills: ${fileCount} files`);
582
393
  }
394
+
395
+ /**
396
+ * Run the agent command
397
+ * @param {Object} params - Command parameters
398
+ * @param {Object} params.data - Loaded pathway data
399
+ * @param {string[]} params.args - Command arguments [discipline_id]
400
+ * @param {Object} params.options - Command options
401
+ * @param {string} params.dataDir - Path to data directory
402
+ */
403
+ export async function runAgentCommand({
404
+ data,
405
+ args,
406
+ options,
407
+ dataDir,
408
+ templateLoader,
409
+ loader,
410
+ }) {
411
+ const dataLoader = loader || createDataLoader();
412
+ const agentData = await dataLoader.loadAgentData(dataDir);
413
+ const skillsWithAgent = await dataLoader.loadSkillsWithAgentData(dataDir);
414
+
415
+ if (options.list) {
416
+ listAgentCombinations(data, agentData, false);
417
+ return;
418
+ }
419
+
420
+ if (args.length === 0) {
421
+ showAgentSummary(data, agentData, skillsWithAgent);
422
+ return;
423
+ }
424
+
425
+ const [disciplineId] = args;
426
+ const trackId = options.track;
427
+
428
+ if (args.length > 1) {
429
+ console.error(
430
+ formatError(
431
+ `Unexpected argument: ${args[1]}. Did you mean --track=${args[1]}?`,
432
+ ),
433
+ );
434
+ process.exit(1);
435
+ }
436
+
437
+ const { humanDiscipline, humanTrack, agentDiscipline, agentTrack } =
438
+ resolveAgentEntities(data, agentData, disciplineId, trackId);
439
+
440
+ const level = deriveReferenceLevel(data.levels);
441
+
442
+ if (options.skills) {
443
+ const derivedSkills = deriveAgentSkills({
444
+ discipline: humanDiscipline,
445
+ track: humanTrack,
446
+ level,
447
+ skills: skillsWithAgent,
448
+ });
449
+ for (const skill of derivedSkills) console.log(skill.skillId);
450
+ return;
451
+ }
452
+
453
+ if (options.tools) {
454
+ const derivedSkills = deriveAgentSkills({
455
+ discipline: humanDiscipline,
456
+ track: humanTrack,
457
+ level,
458
+ skills: skillsWithAgent,
459
+ });
460
+ const toolkit = deriveToolkit({
461
+ skillMatrix: derivedSkills,
462
+ skills: skillsWithAgent,
463
+ });
464
+ console.log(toolkitToPlainList(toolkit));
465
+ return;
466
+ }
467
+
468
+ const stageParams = {
469
+ discipline: humanDiscipline,
470
+ track: humanTrack,
471
+ level,
472
+ skills: skillsWithAgent,
473
+ behaviours: data.behaviours,
474
+ agentBehaviours: agentData.behaviours,
475
+ agentDiscipline,
476
+ agentTrack,
477
+ stages: data.stages,
478
+ };
479
+
480
+ const commonCtx = {
481
+ stageParams,
482
+ options,
483
+ data,
484
+ agentTrack,
485
+ humanDiscipline,
486
+ humanTrack,
487
+ agentData,
488
+ skillsWithAgent,
489
+ level,
490
+ templateLoader,
491
+ dataDir,
492
+ };
493
+
494
+ if (options.stage) {
495
+ await handleSingleStage(commonCtx);
496
+ } else {
497
+ await handleAllStages(commonCtx);
498
+ }
499
+ }