@forwardimpact/pathway 0.24.0 → 0.25.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/README.md CHANGED
@@ -14,8 +14,8 @@ web experience and command line.
14
14
 
15
15
  - **Web application** — Interactive browser for jobs, skills, and career paths
16
16
  - **CLI tools** — Command-line access to all functionality
17
- - **Agent teams** — Generate VS Code Custom Agent teams (`.agent.md`) and skills
18
- (`SKILL.md`)
17
+ - **Agent teams** — Generate Claude Code agent teams (`.claude/agents/`) and
18
+ skills (`.claude/skills/`)
19
19
  - **Interview prep** — Build interview question sets by role
20
20
  - **Static site** — Export everything as a static site
21
21
 
@@ -30,7 +30,7 @@ npx fit-pathway skill --list
30
30
  npx fit-pathway job software_engineering senior --track=platform
31
31
 
32
32
  # Generate agent teams and skills
33
- npx fit-pathway agent software_engineering --track=platform --output=./.github/agents
33
+ npx fit-pathway agent software_engineering --track=platform --output=.
34
34
  ```
35
35
 
36
36
  ## CLI Commands
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forwardimpact/pathway",
3
- "version": "0.24.0",
3
+ "version": "0.25.0",
4
4
  "description": "Career progression web app and CLI for exploring roles and generating agent teams",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -41,7 +41,7 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "@forwardimpact/map": "^0.14.0",
44
- "@forwardimpact/libskill": "^3.0.0",
44
+ "@forwardimpact/libskill": "^4.0.0",
45
45
  "@forwardimpact/libtemplate": "^0.2.0",
46
46
  "@forwardimpact/libui": "^1.0.0",
47
47
  "mustache": "^4.2.0",
@@ -2,7 +2,8 @@
2
2
  * Agent Command
3
3
  *
4
4
  * CLI command for generating AI coding agent configurations
5
- * from Engineering Pathway data.
5
+ * from Engineering Pathway data. Outputs follow the Claude Code
6
+ * agent specification.
6
7
  *
7
8
  * All agents are stage-specific. Use --stage for a single stage
8
9
  * or --all-stages (default) for all stages.
@@ -26,7 +27,6 @@
26
27
  import { writeFile, mkdir, readFile } from "fs/promises";
27
28
  import { join, dirname } from "path";
28
29
  import { existsSync } from "fs";
29
- import { stringify as stringifyYaml } from "yaml";
30
30
  import { createDataLoader } from "@forwardimpact/map/loader";
31
31
  import {
32
32
  generateStageAgentProfile,
@@ -36,7 +36,6 @@ import {
36
36
  deriveAgentSkills,
37
37
  generateSkillMarkdown,
38
38
  deriveToolkit,
39
- buildAgentIndex,
40
39
  getDisciplineAbbreviation,
41
40
  toKebabCase,
42
41
  } from "@forwardimpact/libskill";
@@ -58,13 +57,13 @@ async function ensureDir(filePath) {
58
57
  }
59
58
 
60
59
  /**
61
- * Generate VS Code settings file with required settings
60
+ * Generate Claude Code settings file
62
61
  * Merges with existing settings if file exists
63
62
  * @param {string} baseDir - Base output directory
64
- * @param {Object} vscodeSettings - Settings loaded from data
63
+ * @param {Object} claudeCodeSettings - Settings loaded from data
65
64
  */
66
- async function generateVSCodeSettings(baseDir, vscodeSettings) {
67
- const settingsPath = join(baseDir, ".vscode", "settings.json");
65
+ async function generateClaudeCodeSettings(baseDir, claudeCodeSettings) {
66
+ const settingsPath = join(baseDir, ".claude", "settings.json");
68
67
 
69
68
  let settings = {};
70
69
  if (existsSync(settingsPath)) {
@@ -72,7 +71,7 @@ async function generateVSCodeSettings(baseDir, vscodeSettings) {
72
71
  settings = JSON.parse(content);
73
72
  }
74
73
 
75
- const merged = { ...settings, ...vscodeSettings };
74
+ const merged = { ...settings, ...claudeCodeSettings };
76
75
 
77
76
  await ensureDir(settingsPath);
78
77
  await writeFile(
@@ -83,64 +82,6 @@ async function generateVSCodeSettings(baseDir, vscodeSettings) {
83
82
  console.log(formatSuccess(`Updated: ${settingsPath}`));
84
83
  }
85
84
 
86
- /**
87
- * Generate devcontainer.json from template with VS Code settings embedded
88
- * @param {string} baseDir - Base output directory
89
- * @param {Object} devcontainerConfig - Devcontainer config loaded from data
90
- * @param {Object} vscodeSettings - VS Code settings to embed in customizations
91
- */
92
- async function generateDevcontainer(
93
- baseDir,
94
- devcontainerConfig,
95
- vscodeSettings,
96
- ) {
97
- if (!devcontainerConfig || Object.keys(devcontainerConfig).length === 0) {
98
- return;
99
- }
100
-
101
- const devcontainerPath = join(baseDir, ".devcontainer", "devcontainer.json");
102
-
103
- // Build devcontainer.json with VS Code settings embedded
104
- const devcontainer = {
105
- ...devcontainerConfig,
106
- customizations: {
107
- vscode: {
108
- settings: vscodeSettings,
109
- },
110
- },
111
- };
112
-
113
- await ensureDir(devcontainerPath);
114
- await writeFile(
115
- devcontainerPath,
116
- JSON.stringify(devcontainer, null, 2) + "\n",
117
- "utf-8",
118
- );
119
- console.log(formatSuccess(`Created: ${devcontainerPath}`));
120
- }
121
-
122
- /**
123
- * Generate GitHub Actions workflow for Copilot Coding Agent setup steps
124
- * @param {string} baseDir - Base output directory
125
- * @param {Object|null} copilotSetupSteps - Workflow config loaded from data
126
- */
127
- async function generateCopilotSetupSteps(baseDir, copilotSetupSteps) {
128
- if (!copilotSetupSteps) {
129
- return;
130
- }
131
-
132
- const workflowPath = join(
133
- baseDir,
134
- ".github",
135
- "workflows",
136
- "copilot-setup-steps.yml",
137
- );
138
-
139
- await ensureDir(workflowPath);
140
- await writeFile(workflowPath, stringifyYaml(copilotSetupSteps), "utf-8");
141
- console.log(formatSuccess(`Created: ${workflowPath}`));
142
- }
143
-
144
85
  /**
145
86
  * Show agent summary with stats
146
87
  * @param {Object} data - Pathway data
@@ -258,7 +199,7 @@ function listAgentCombinations(data, agentData, verbose = false) {
258
199
  * @param {string} template - Mustache template for agent profile
259
200
  */
260
201
  async function writeProfile(profile, baseDir, template) {
261
- const profilePath = join(baseDir, ".github", "agents", profile.filename);
202
+ const profilePath = join(baseDir, ".claude", "agents", profile.filename);
262
203
  const profileContent = formatAgentProfile(profile, template);
263
204
  await ensureDir(profilePath);
264
205
  await writeFile(profilePath, profileContent, "utf-8");
@@ -451,15 +392,6 @@ export async function runAgentCommand({
451
392
 
452
393
  const baseDir = options.output || ".";
453
394
 
454
- // Build agent index for all valid combinations
455
- const agentIndex = buildAgentIndex({
456
- disciplines: data.disciplines,
457
- tracks: data.tracks,
458
- stages: data.stages,
459
- agentDisciplines: agentData.disciplines,
460
- agentTracks: agentData.tracks,
461
- });
462
-
463
395
  // Common params for stage-based generation
464
396
  const stageParams = {
465
397
  discipline: humanDiscipline,
@@ -471,7 +403,6 @@ export async function runAgentCommand({
471
403
  agentDiscipline,
472
404
  agentTrack,
473
405
  stages: data.stages,
474
- agentIndex,
475
406
  };
476
407
 
477
408
  // Handle --stage flag for single stage agent
@@ -508,13 +439,7 @@ export async function runAgentCommand({
508
439
  }
509
440
 
510
441
  await writeProfile(profile, baseDir, agentTemplate);
511
- await generateVSCodeSettings(baseDir, agentData.vscodeSettings);
512
- await generateDevcontainer(
513
- baseDir,
514
- agentData.devcontainer,
515
- agentData.vscodeSettings,
516
- );
517
- await generateCopilotSetupSteps(baseDir, agentData.copilotSetupSteps);
442
+ await generateClaudeCodeSettings(baseDir, agentData.claudeCodeSettings);
518
443
  console.log("");
519
444
  console.log(
520
445
  formatSuccess(`Generated stage agent: ${profile.frontmatter.name}`),
@@ -522,8 +447,7 @@ export async function runAgentCommand({
522
447
  return;
523
448
  }
524
449
 
525
- // Default behavior: generate all stage agents (or single stage if --stage specified)
526
- // No generic agents - all agents are stage-specific
450
+ // Default behavior: generate all stage agents
527
451
  const profiles = [];
528
452
 
529
453
  // Generate all stage agents
@@ -603,13 +527,7 @@ export async function runAgentCommand({
603
527
  await writeProfile(profile, baseDir, agentTemplate);
604
528
  }
605
529
  const fileCount = await writeSkills(skillFiles, baseDir, skillTemplates);
606
- await generateVSCodeSettings(baseDir, agentData.vscodeSettings);
607
- await generateDevcontainer(
608
- baseDir,
609
- agentData.devcontainer,
610
- agentData.vscodeSettings,
611
- );
612
- await generateCopilotSetupSteps(baseDir, agentData.copilotSetupSteps);
530
+ await generateClaudeCodeSettings(baseDir, agentData.claudeCodeSettings);
613
531
 
614
532
  console.log("");
615
533
  console.log(formatSuccess(`Generated ${profiles.length} agents:`));
@@ -160,6 +160,24 @@ ${framework.emojiIcon} Generating ${framework.title} static site...
160
160
  });
161
161
  console.log(` ✓ ui/lib + ui/css`);
162
162
 
163
+ // Copy vendor dependencies for offline usage
164
+ console.log("📦 Copying vendor dependencies...");
165
+ const vendorDir = join(outputDir, "vendor");
166
+ await mkdir(vendorDir, { recursive: true });
167
+
168
+ // mustache (ESM module)
169
+ const mustacheSrc = fileURLToPath(import.meta.resolve("mustache"));
170
+ const mustacheMjs = join(dirname(mustacheSrc), "mustache.mjs");
171
+ await cp(mustacheMjs, join(vendorDir, "mustache.mjs"));
172
+ console.log(" ✓ vendor/mustache.mjs");
173
+
174
+ // yaml (browser ESM build — not in package exports, resolve via filesystem)
175
+ // import.meta.resolve("yaml") → .../yaml/dist/index.js, go up two levels
176
+ const yamlPkg = dirname(dirname(fileURLToPath(import.meta.resolve("yaml"))));
177
+ const yamlBrowserDist = join(yamlPkg, "browser", "dist");
178
+ await cp(yamlBrowserDist, join(vendorDir, "yaml"), { recursive: true });
179
+ console.log(" ✓ vendor/yaml/");
180
+
163
181
  // Copy data directory (dereference symlinks to copy actual content)
164
182
  console.log("📁 Copying data files...");
165
183
  const dataOutputDir = join(outputDir, "data");
@@ -34,10 +34,19 @@ const mapLibDir = resolvePackageLib("@forwardimpact/map");
34
34
  const modelLibDir = resolvePackageLib("@forwardimpact/libskill");
35
35
  const uiLibDir = resolvePackageLib("@forwardimpact/libui");
36
36
 
37
+ // Vendor dependencies — mirror the paths that build.js copies to vendor/
38
+ const mustacheDir = dirname(fileURLToPath(import.meta.resolve("mustache")));
39
+ const yamlBrowserDir = join(
40
+ dirname(dirname(fileURLToPath(import.meta.resolve("yaml")))),
41
+ "browser",
42
+ "dist",
43
+ );
44
+
37
45
  const MIME_TYPES = {
38
46
  ".html": "text/html; charset=utf-8",
39
47
  ".css": "text/css; charset=utf-8",
40
48
  ".js": "application/javascript; charset=utf-8",
49
+ ".mjs": "application/javascript; charset=utf-8",
41
50
  ".yaml": "text/yaml; charset=utf-8",
42
51
  ".yml": "text/yaml; charset=utf-8",
43
52
  ".json": "application/json; charset=utf-8",
@@ -149,6 +158,12 @@ export async function runDevCommand({ dataDir, options }) {
149
158
  } else if (pathname.startsWith("/ui/css/")) {
150
159
  // Serve @forwardimpact/libui package CSS files
151
160
  filePath = join(uiLibDir, "css", pathname.slice(8));
161
+ } else if (pathname === "/vendor/mustache.mjs") {
162
+ // Serve vendored mustache ESM module
163
+ filePath = join(mustacheDir, "mustache.mjs");
164
+ } else if (pathname.startsWith("/vendor/yaml/")) {
165
+ // Serve vendored yaml browser ESM build
166
+ filePath = join(yamlBrowserDir, pathname.slice(13));
152
167
  } else if (pathname === "/" || pathname === "") {
153
168
  // Serve index.html for root
154
169
  filePath = join(publicDir, "index.html");
@@ -22,17 +22,20 @@ import { SKILL_PROFICIENCY_ORDER } from "@forwardimpact/map/levels";
22
22
  import { truncate } from "../formatters/shared.js";
23
23
 
24
24
  /**
25
- * Sort skills by level descending (expert first), then alphabetically
25
+ * Sort skills by capability group order, then by level descending within each group
26
26
  * @param {SkillMatrixItem[]} skills
27
+ * @param {string[]} capabilityOrder - Ordered capability IDs
27
28
  * @returns {SkillMatrixItem[]}
28
29
  */
29
- function sortByLevelDescending(skills) {
30
+ function sortByCapabilityThenLevel(skills, capabilityOrder) {
31
+ const orderMap = new Map(capabilityOrder.map((id, i) => [id, i]));
30
32
  return [...skills].sort((a, b) => {
31
- const levelA = SKILL_PROFICIENCY_ORDER.indexOf(a.level);
32
- const levelB = SKILL_PROFICIENCY_ORDER.indexOf(b.level);
33
- if (levelB !== levelA) {
34
- return levelB - levelA;
35
- }
33
+ const capA = orderMap.has(a.capability) ? orderMap.get(a.capability) : capabilityOrder.length;
34
+ const capB = orderMap.has(b.capability) ? orderMap.get(b.capability) : capabilityOrder.length;
35
+ if (capA !== capB) return capA - capB;
36
+ const levelA = SKILL_PROFICIENCY_ORDER.indexOf(a.proficiency);
37
+ const levelB = SKILL_PROFICIENCY_ORDER.indexOf(b.proficiency);
38
+ if (levelB !== levelA) return levelB - levelA;
36
39
  return a.skillName.localeCompare(b.skillName);
37
40
  });
38
41
  }
@@ -40,14 +43,19 @@ function sortByLevelDescending(skills) {
40
43
  /**
41
44
  * Create a skill matrix table
42
45
  * @param {SkillMatrixItem[]} skillMatrix - Skill matrix entries
46
+ * @param {Object} [options]
47
+ * @param {string[]} [options.capabilityOrder] - Capability IDs in desired display order
43
48
  * @returns {HTMLElement}
44
49
  */
45
- export function createSkillMatrix(skillMatrix) {
50
+ export function createSkillMatrix(skillMatrix, options = {}) {
46
51
  if (!skillMatrix || skillMatrix.length === 0) {
47
52
  return div({ className: "empty-state" }, "No skills in matrix");
48
53
  }
49
54
 
50
- const sortedSkills = sortByLevelDescending(skillMatrix);
55
+ const { capabilityOrder } = options;
56
+ const sortedSkills = capabilityOrder
57
+ ? sortByCapabilityThenLevel(skillMatrix, capabilityOrder)
58
+ : [...skillMatrix];
51
59
 
52
60
  const rows = sortedSkills.map((skill) => {
53
61
  const levelIndex = getSkillProficiencyIndex(skill.proficiency);
@@ -17,14 +17,14 @@ import { getStageEmoji } from "../stage/shared.js";
17
17
  * @param {Object} deployment.profile - Agent profile
18
18
  * @param {Array} deployment.skills - Agent skills
19
19
  * @param {Array} [deployment.roleAgents] - Role variant agents (plan, review)
20
- * @param {Object} [deployment.vscodeSettings] - VS Code settings to include in download
20
+ * @param {Object} [deployment.claudeCodeSettings] - Claude Code settings to include in download
21
21
  * @returns {HTMLElement}
22
22
  */
23
23
  export function agentDeploymentToDOM({
24
24
  profile,
25
25
  skills,
26
26
  roleAgents = [],
27
- vscodeSettings = {},
27
+ claudeCodeSettings = {},
28
28
  }) {
29
29
  const profileContent = formatAgentProfile(profile);
30
30
  const agentName = profile.frontmatter.name;
@@ -37,7 +37,7 @@ export function agentDeploymentToDOM({
37
37
  profile,
38
38
  skills,
39
39
  roleAgents,
40
- vscodeSettings,
40
+ claudeCodeSettings,
41
41
  agentName,
42
42
  ),
43
43
 
@@ -90,7 +90,7 @@ export function agentDeploymentToDOM({
90
90
  * @param {Object} profile - Agent profile
91
91
  * @param {Array} skills - Agent skills
92
92
  * @param {Array} roleAgents - Role variant agents
93
- * @param {Object} vscodeSettings - VS Code settings to include
93
+ * @param {Object} claudeCodeSettings - Claude Code settings to include
94
94
  * @param {string} agentName - Agent name for zip filename
95
95
  * @returns {HTMLElement}
96
96
  */
@@ -98,7 +98,7 @@ function createDownloadButton(
98
98
  profile,
99
99
  skills,
100
100
  roleAgents,
101
- vscodeSettings,
101
+ claudeCodeSettings,
102
102
  agentName,
103
103
  ) {
104
104
  const btn = button(
@@ -115,7 +115,7 @@ function createDownloadButton(
115
115
  profile,
116
116
  skills,
117
117
  roleAgents,
118
- vscodeSettings,
118
+ claudeCodeSettings,
119
119
  agentName,
120
120
  );
121
121
  } finally {
@@ -167,28 +167,28 @@ function createRoleAgentCard(agent) {
167
167
  * @param {Object} profile - Agent profile
168
168
  * @param {Array} skills - Agent skills
169
169
  * @param {Array} roleAgents - Role variant agents
170
- * @param {Object} vscodeSettings - VS Code settings to include
170
+ * @param {Object} claudeCodeSettings - Claude Code settings to include
171
171
  * @param {string} agentName - Agent name for zip filename
172
172
  */
173
173
  async function downloadAllAsZip(
174
174
  profile,
175
175
  skills,
176
176
  roleAgents,
177
- vscodeSettings,
177
+ claudeCodeSettings,
178
178
  agentName,
179
179
  ) {
180
180
  // Dynamically import JSZip
181
181
  const JSZip = await importJSZip();
182
182
  const zip = new JSZip();
183
183
 
184
- // Add main profile to .github/agents/ folder
184
+ // Add main profile to .claude/agents/ folder
185
185
  const profileContent = formatAgentProfile(profile);
186
- zip.file(`.github/agents/${profile.filename}`, profileContent);
186
+ zip.file(`.claude/agents/${profile.filename}`, profileContent);
187
187
 
188
- // Add role agent profiles to .github/agents/ folder
188
+ // Add role agent profiles to .claude/agents/ folder
189
189
  for (const roleAgent of roleAgents) {
190
190
  const roleContent = formatAgentProfile(roleAgent);
191
- zip.file(`.github/agents/${roleAgent.filename}`, roleContent);
191
+ zip.file(`.claude/agents/${roleAgent.filename}`, roleContent);
192
192
  }
193
193
 
194
194
  // Add skills to .claude/skills/ folder
@@ -197,11 +197,11 @@ async function downloadAllAsZip(
197
197
  zip.file(`.claude/skills/${skill.dirname}/SKILL.md`, skillContent);
198
198
  }
199
199
 
200
- // Add VS Code settings for multi-agent features
201
- if (Object.keys(vscodeSettings).length > 0) {
200
+ // Add Claude Code settings
201
+ if (Object.keys(claudeCodeSettings).length > 0) {
202
202
  zip.file(
203
- ".vscode/settings.json",
204
- JSON.stringify(vscodeSettings, null, 2) + "\n",
203
+ ".claude/settings.json",
204
+ JSON.stringify(claudeCodeSettings, null, 2) + "\n",
205
205
  );
206
206
  }
207
207
 
@@ -235,13 +235,12 @@ async function importJSZip() {
235
235
  * @param {Object} profile - Generated profile with frontmatter and body
236
236
  * @param {Object} options - Options
237
237
  * @param {Array} [options.stages] - All stages for emoji lookup
238
- * @param {Object} [options.vscodeSettings] - VS Code settings for download
238
+ * @param {Object} [options.claudeCodeSettings] - Claude Code settings for download
239
239
  * @returns {HTMLElement}
240
240
  */
241
241
  export function stageAgentToDOM(stageAgent, profile, options = {}) {
242
- const { vscodeSettings = {}, stages = [] } = options;
243
- const { stage, tools, handoffs, constraints, checklist, derivedSkills } =
244
- stageAgent;
242
+ const { claudeCodeSettings = {}, stages = [] } = options;
243
+ const { stage, tools, constraints, checklist, derivedSkills } = stageAgent;
245
244
  const stageEmoji = getStageEmoji(stages, stage.id);
246
245
  const profileContent = formatAgentProfile(profile);
247
246
 
@@ -287,25 +286,6 @@ export function stageAgentToDOM(stageAgent, profile, options = {}) {
287
286
  )
288
287
  : null,
289
288
 
290
- // Handoffs section
291
- handoffs.length > 0
292
- ? section(
293
- { className: "agent-section" },
294
- h3({}, "Handoffs"),
295
- div(
296
- { className: "handoff-buttons" },
297
- ...handoffs.map((h) => {
298
- const targetEmoji = getStageEmoji(stages, h.target);
299
- return div(
300
- { className: "handoff-button-preview" },
301
- span({ className: "handoff-icon" }, targetEmoji),
302
- span({ className: "handoff-label" }, h.label),
303
- );
304
- }),
305
- ),
306
- )
307
- : null,
308
-
309
289
  // Checklist section
310
290
  checklist.length > 0
311
291
  ? section(
@@ -353,7 +333,7 @@ export function stageAgentToDOM(stageAgent, profile, options = {}) {
353
333
  ),
354
334
 
355
335
  // Download button
356
- createStageAgentDownloadButton(profile, vscodeSettings),
336
+ createStageAgentDownloadButton(profile, claudeCodeSettings),
357
337
  );
358
338
  }
359
339
 
@@ -387,10 +367,10 @@ function createChecklistPreview(checklist) {
387
367
  /**
388
368
  * Create download button for stage agent
389
369
  * @param {Object} profile - Agent profile
390
- * @param {Object} vscodeSettings - VS Code settings
370
+ * @param {Object} claudeCodeSettings - Claude Code settings
391
371
  * @returns {HTMLElement}
392
372
  */
393
- function createStageAgentDownloadButton(profile, vscodeSettings) {
373
+ function createStageAgentDownloadButton(profile, claudeCodeSettings) {
394
374
  const btn = button(
395
375
  { className: "btn btn-primary download-all-btn" },
396
376
  "📥 Download Agent Profile",
@@ -406,13 +386,13 @@ function createStageAgentDownloadButton(profile, vscodeSettings) {
406
386
 
407
387
  // Add profile
408
388
  const profileContent = formatAgentProfile(profile);
409
- zip.file(`.github/agents/${profile.filename}`, profileContent);
389
+ zip.file(`.claude/agents/${profile.filename}`, profileContent);
410
390
 
411
- // Add VS Code settings
412
- if (Object.keys(vscodeSettings).length > 0) {
391
+ // Add Claude Code settings
392
+ if (Object.keys(claudeCodeSettings).length > 0) {
413
393
  zip.file(
414
- ".vscode/settings.json",
415
- JSON.stringify(vscodeSettings, null, 2) + "\n",
394
+ ".claude/settings.json",
395
+ JSON.stringify(claudeCodeSettings, null, 2) + "\n",
416
396
  );
417
397
  }
418
398
 
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Agent Profile Formatter
3
3
  *
4
- * Formats agent profile data into .agent.md file content
5
- * following the GitHub Copilot Custom Agents specification.
4
+ * Formats agent profile data into .md file content
5
+ * following the Claude Code agent specification.
6
6
  *
7
7
  * Uses Mustache templates for flexible output formatting.
8
8
  * Templates are loaded from data/ directory with fallback to templates/ directory.
@@ -11,10 +11,7 @@
11
11
  import Mustache from "mustache";
12
12
 
13
13
  import { trimValue, trimRequired, trimFields } from "../shared.js";
14
- import {
15
- flattenToLine,
16
- preprocessArrayFrontmatter,
17
- } from "../template-preprocess.js";
14
+ import { flattenToLine } from "../template-preprocess.js";
18
15
 
19
16
  /**
20
17
  * @typedef {Object} WorkingStyleEntry
@@ -29,8 +26,8 @@ import {
29
26
  * @param {Object} params.frontmatter - YAML frontmatter data
30
27
  * @param {string} params.frontmatter.name - Agent name
31
28
  * @param {string} params.frontmatter.description - Agent description
32
- * @param {boolean} params.frontmatter.infer - Whether to auto-select
33
- * @param {Array} [params.frontmatter.handoffs] - Handoff definitions
29
+ * @param {string} params.frontmatter.model - Claude Code model (sonnet, opus, haiku)
30
+ * @param {string[]} params.frontmatter.skills - Skill dirnames for auto-loading
34
31
  * @param {Object} params.bodyData - Structured body data
35
32
  * @param {string} params.bodyData.title - Agent title
36
33
  * @param {string} params.bodyData.stageDescription - Stage description text
@@ -43,41 +40,51 @@ import {
43
40
  * @param {string} params.bodyData.roleContext - Role context text
44
41
  * @param {WorkingStyleEntry[]} params.bodyData.workingStyles - Working style entries
45
42
  * @param {string[]} params.bodyData.constraints - List of constraints
46
- * @param {Array<{id: string, name: string, description: string}>} [params.bodyData.agentIndex] - List of all available agents
47
- * @param {boolean} [params.bodyData.hasAgentIndex] - Whether agent index is available
43
+ * @param {Array<{targetStageName: string, summaryInstruction: string, entryCriteria: string[]}>} params.bodyData.stageTransitions - Stage transition definitions
48
44
  * @returns {Object} Data object ready for Mustache template
49
45
  */
50
46
  function prepareAgentProfileData({ frontmatter, bodyData }) {
51
- // Preprocess handoffs - flatten prompt field for front matter compatibility
52
- const preprocessedHandoffs = preprocessArrayFrontmatter(
53
- frontmatter.handoffs,
54
- ["prompt"],
47
+ const stageConstraints = (bodyData.stageConstraints || []).map((c) =>
48
+ trimRequired(c),
49
+ );
50
+ const disciplineConstraints = (bodyData.disciplineConstraints || []).map(
51
+ (c) => trimRequired(c),
52
+ );
53
+ const trackConstraints = (bodyData.trackConstraints || []).map((c) =>
54
+ trimRequired(c),
55
+ );
56
+ const returnFormat = (bodyData.returnFormat || []).map((r) =>
57
+ trimRequired(r),
55
58
  );
56
- // Then trim as before
57
- const handoffs = trimFields(preprocessedHandoffs, { prompt: "required" });
58
-
59
- const constraints = (bodyData.constraints || []).map((c) => trimRequired(c));
60
59
  const skillIndex = trimFields(bodyData.skillIndex, {
61
60
  name: "required",
62
61
  dirname: "required",
63
62
  useWhen: "required",
64
63
  });
65
- const agentIndex = trimFields(bodyData.agentIndex, {
66
- id: "required",
67
- name: "required",
68
- description: "required",
69
- });
70
64
  const workingStyles = trimFields(bodyData.workingStyles, {
71
65
  title: "required",
72
66
  content: "required",
73
67
  });
74
68
 
69
+ // Prepare stage transitions for body rendering
70
+ const stageTransitions = (bodyData.stageTransitions || []).map((t) => ({
71
+ targetStageName: t.targetStageName,
72
+ summaryInstruction: trimValue(t.summaryInstruction),
73
+ entryCriteria: (t.entryCriteria || []).map((c) => trimRequired(c)),
74
+ hasEntryCriteria: (t.entryCriteria || []).length > 0,
75
+ }));
76
+
77
+ const hasConstraints =
78
+ stageConstraints.length > 0 ||
79
+ disciplineConstraints.length > 0 ||
80
+ trackConstraints.length > 0;
81
+
75
82
  return {
76
- // Frontmatter - flatten description for single-line front matter
83
+ // Frontmatter
77
84
  name: frontmatter.name,
78
85
  description: flattenToLine(frontmatter.description),
79
- infer: frontmatter.infer,
80
- handoffs,
86
+ model: frontmatter.model,
87
+ skills: frontmatter.skills,
81
88
 
82
89
  // Body data - trim all string fields
83
90
  title: bodyData.title,
@@ -92,22 +99,28 @@ function prepareAgentProfileData({ frontmatter, bodyData }) {
92
99
  roleContext: trimValue(bodyData.roleContext),
93
100
  workingStyles,
94
101
  hasWorkingStyles: workingStyles.length > 0,
95
- constraints,
96
- hasConstraints: constraints.length > 0,
97
- agentIndex,
98
- hasAgentIndex: agentIndex.length > 0,
102
+ stageConstraints,
103
+ disciplineConstraints,
104
+ trackConstraints,
105
+ hasStageConstraints: stageConstraints.length > 0,
106
+ hasDisciplineOrTrackConstraints:
107
+ disciplineConstraints.length > 0 || trackConstraints.length > 0,
108
+ hasConstraints,
109
+ returnFormat,
110
+ hasReturnFormat: returnFormat.length > 0,
111
+ stageTransitions,
112
+ hasStageTransitions: stageTransitions.length > 0,
99
113
  };
100
114
  }
101
115
 
102
116
  /**
103
- * Format agent profile as .agent.md file content using Mustache template
117
+ * Format agent profile as .md file content using Mustache template
104
118
  * @param {Object} profile - Profile with frontmatter and bodyData
105
119
  * @param {Object} profile.frontmatter - YAML frontmatter data
106
120
  * @param {string} profile.frontmatter.name - Agent name
107
121
  * @param {string} profile.frontmatter.description - Agent description
108
- * @param {string[]} profile.frontmatter.tools - Available tools
109
- * @param {boolean} profile.frontmatter.infer - Whether to auto-select
110
- * @param {Array} [profile.frontmatter.handoffs] - Handoff definitions
122
+ * @param {string} profile.frontmatter.model - Claude Code model
123
+ * @param {string[]} profile.frontmatter.skills - Skill dirnames
111
124
  * @param {Object} profile.bodyData - Structured body data
112
125
  * @param {string} profile.bodyData.title - Agent title (e.g. "Software Engineering - Platform - Plan Agent")
113
126
  * @param {string} profile.bodyData.stageDescription - Stage description text
@@ -117,8 +130,9 @@ function prepareAgentProfileData({ frontmatter, bodyData }) {
117
130
  * @param {string} profile.bodyData.roleContext - Role context text
118
131
  * @param {WorkingStyleEntry[]} profile.bodyData.workingStyles - Working style entries
119
132
  * @param {string[]} profile.bodyData.constraints - List of constraints
133
+ * @param {Array} profile.bodyData.stageTransitions - Stage transitions for body section
120
134
  * @param {string} template - Mustache template string
121
- * @returns {string} Complete .agent.md file content
135
+ * @returns {string} Complete .md file content
122
136
  */
123
137
  export function formatAgentProfile({ frontmatter, bodyData }, template) {
124
138
  const data = prepareAgentProfileData({ frontmatter, bodyData });
@@ -2,7 +2,7 @@
2
2
  * Job formatting for DOM/web output
3
3
  */
4
4
 
5
- import { div, h1, h2, p, a, span, section } from "../../lib/render.js";
5
+ import { div, h1, h2, a, section } from "../../lib/render.js";
6
6
  import { createBackLink } from "../../components/nav.js";
7
7
  import {
8
8
  createDetailSection,
@@ -117,7 +117,9 @@ export function jobToDOM(view, options = {}) {
117
117
 
118
118
  createDetailSection({
119
119
  title: "Skill Matrix",
120
- content: createSkillMatrix(view.skillMatrix),
120
+ content: createSkillMatrix(view.skillMatrix, {
121
+ capabilityOrder: view.capabilityOrder,
122
+ }),
121
123
  }),
122
124
 
123
125
  // Toolkit (after skill matrix)
@@ -127,21 +129,6 @@ export function jobToDOM(view, options = {}) {
127
129
  content: createToolkitTable(view.toolkit),
128
130
  })
129
131
  : null,
130
-
131
- // Driver coverage
132
- view.driverCoverage.length > 0
133
- ? createDetailSection({
134
- title: "Driver Coverage",
135
- content: div(
136
- {},
137
- p(
138
- { className: "text-muted", style: "margin-bottom: 1rem" },
139
- "How well this job aligns with organizational outcome drivers.",
140
- ),
141
- createDriverCoverageDisplay(view.driverCoverage),
142
- ),
143
- })
144
- : null,
145
132
  )
146
133
  : null,
147
134
 
@@ -164,48 +151,6 @@ export function jobToDOM(view, options = {}) {
164
151
  );
165
152
  }
166
153
 
167
- /**
168
- * Create driver coverage display
169
- */
170
- function createDriverCoverageDisplay(coverage) {
171
- const items = coverage.map((c) => {
172
- const percentage = Math.round(c.coverage * 100);
173
-
174
- return div(
175
- { className: "driver-coverage-item" },
176
- div(
177
- { className: "driver-coverage-header" },
178
- a(
179
- {
180
- href: `#/driver/${c.id}`,
181
- className: "driver-coverage-name",
182
- },
183
- c.name,
184
- ),
185
- span({ className: "driver-coverage-score" }, `${percentage}%`),
186
- ),
187
- div(
188
- { className: "progress-bar" },
189
- div({
190
- className: "progress-bar-fill",
191
- style: `width: ${percentage}%; background: ${getScoreColor(c.coverage)}`,
192
- }),
193
- ),
194
- );
195
- });
196
-
197
- return div({ className: "driver-coverage" }, ...items);
198
- }
199
-
200
- /**
201
- * Get color based on score
202
- */
203
- function getScoreColor(score) {
204
- if (score >= 0.8) return "#10b981"; // Green
205
- if (score >= 0.5) return "#f59e0b"; // Yellow
206
- return "#ef4444"; // Red
207
- }
208
-
209
154
  /**
210
155
  * Create the job description section with copy button
211
156
  * @param {Object} params
@@ -243,6 +188,7 @@ export function createJobDescriptionSection({
243
188
  "Copy this markdown-formatted job description for use in job postings, documentation, or sharing.",
244
189
  toHtml: markdownToHtml,
245
190
  minHeight: 450,
191
+ open: true,
246
192
  }),
247
193
  );
248
194
  }
package/src/handout.html CHANGED
@@ -8,7 +8,7 @@
8
8
  <script type="importmap">
9
9
  {
10
10
  "imports": {
11
- "mustache": "https://esm.sh/mustache@4.2.0",
11
+ "mustache": "/vendor/mustache.mjs",
12
12
  "@forwardimpact/map": "/map/lib/index.js",
13
13
  "@forwardimpact/map/levels": "/map/lib/levels.js",
14
14
  "@forwardimpact/map/loader": "/map/lib/loader.js",
package/src/index.html CHANGED
@@ -15,12 +15,18 @@
15
15
  href="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism.min.css"
16
16
  />
17
17
  <link rel="stylesheet" href="css/bundles/app.css" />
18
- <script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/prism.min.js"></script>
19
- <script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-markdown.min.js"></script>
18
+ <script
19
+ src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/prism.min.js"
20
+ defer
21
+ ></script>
22
+ <script
23
+ src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-markdown.min.js"
24
+ defer
25
+ ></script>
20
26
  <script type="importmap">
21
27
  {
22
28
  "imports": {
23
- "mustache": "https://esm.sh/mustache@4.2.0",
29
+ "mustache": "/vendor/mustache.mjs",
24
30
  "@forwardimpact/map": "/map/lib/index.js",
25
31
  "@forwardimpact/map/levels": "/map/lib/levels.js",
26
32
  "@forwardimpact/map/loader": "/map/lib/loader.js",
@@ -287,27 +287,15 @@ export async function loadAllData(dataDir = "./data") {
287
287
  * @returns {Promise<Object>}
288
288
  */
289
289
  export async function loadAgentDataBrowser(dataDir = "./data") {
290
- const [
291
- disciplines,
292
- tracks,
293
- behaviours,
294
- vscodeSettings,
295
- devcontainer,
296
- copilotSetupSteps,
297
- ] = await Promise.all([
298
- loadDisciplinesFromDir(`${dataDir}/disciplines`),
299
- loadTracksFromDir(`${dataDir}/tracks`),
300
- loadBehavioursFromDir(`${dataDir}/behaviours`),
301
- tryLoadYamlFile(`${dataDir}/repository/vscode-settings.yaml`).then(
302
- (r) => r ?? tryLoadYamlFile(`${dataDir}/vscode-settings.yaml`),
303
- ),
304
- tryLoadYamlFile(`${dataDir}/repository/devcontainer.yaml`).then(
305
- (r) => r ?? tryLoadYamlFile(`${dataDir}/devcontainer.yaml`),
306
- ),
307
- tryLoadYamlFile(`${dataDir}/repository/copilot-setup-steps.yaml`).then(
308
- (r) => r ?? tryLoadYamlFile(`${dataDir}/copilot-setup-steps.yaml`),
309
- ),
310
- ]);
290
+ const [disciplines, tracks, behaviours, claudeCodeSettings] =
291
+ await Promise.all([
292
+ loadDisciplinesFromDir(`${dataDir}/disciplines`),
293
+ loadTracksFromDir(`${dataDir}/tracks`),
294
+ loadBehavioursFromDir(`${dataDir}/behaviours`),
295
+ tryLoadYamlFile(`${dataDir}/repository/claude-code-settings.yaml`).then(
296
+ (r) => r ?? tryLoadYamlFile(`${dataDir}/claude-code-settings.yaml`),
297
+ ),
298
+ ]);
311
299
 
312
300
  return {
313
301
  disciplines: disciplines
@@ -319,8 +307,6 @@ export async function loadAgentDataBrowser(dataDir = "./data") {
319
307
  behaviours: behaviours
320
308
  .filter((b) => b.agent)
321
309
  .map((b) => ({ id: b.id, ...b.agent })),
322
- vscodeSettings: vscodeSettings || {},
323
- devcontainer: devcontainer || {},
324
- copilotSetupSteps: copilotSetupSteps || null,
310
+ claudeCodeSettings: claudeCodeSettings || {},
325
311
  };
326
312
  }
@@ -28,7 +28,6 @@ import {
28
28
  deriveAgentSkills,
29
29
  deriveReferenceLevel,
30
30
  deriveToolkit,
31
- buildAgentIndex,
32
31
  } from "@forwardimpact/libskill";
33
32
  import {
34
33
  createSelectWithValue,
@@ -259,15 +258,6 @@ export async function renderAgentBuilder() {
259
258
  // Get reference level for derivation
260
259
  const level = deriveReferenceLevel(data.levels);
261
260
 
262
- // Build agent index for all valid combinations
263
- const agentIndex = buildAgentIndex({
264
- disciplines: data.disciplines,
265
- tracks: data.tracks,
266
- stages,
267
- agentDisciplines: agentData.disciplines,
268
- agentTracks: agentData.tracks,
269
- });
270
-
271
261
  // Build context for generation
272
262
  const context = {
273
263
  humanDiscipline,
@@ -279,10 +269,8 @@ export async function renderAgentBuilder() {
279
269
  skills: data.skills,
280
270
  behaviours: data.behaviours,
281
271
  agentBehaviours: agentData.behaviours,
282
- vscodeSettings: agentData.vscodeSettings,
283
- devcontainer: agentData.devcontainer,
272
+ claudeCodeSettings: agentData.claudeCodeSettings,
284
273
  templates,
285
- agentIndex,
286
274
  };
287
275
 
288
276
  // Generate preview based on stage selection
@@ -316,7 +304,7 @@ export async function renderAgentBuilder() {
316
304
  p(
317
305
  { className: "page-description" },
318
306
  "Generate coding agent teams from discipline × track × stage combinations. " +
319
- "Export complete agent profiles and skill files for GitHub Copilot.",
307
+ "Export complete agent profiles and skill files for Claude Code.",
320
308
  ),
321
309
  ),
322
310
 
@@ -456,10 +444,8 @@ function createAllStagesPreview(context) {
456
444
  skills,
457
445
  behaviours,
458
446
  agentBehaviours,
459
- vscodeSettings,
460
- devcontainer,
447
+ claudeCodeSettings,
461
448
  templates,
462
- agentIndex,
463
449
  } = context;
464
450
 
465
451
  // Generate all stage agents
@@ -474,7 +460,6 @@ function createAllStagesPreview(context) {
474
460
  agentBehaviours,
475
461
  agentDiscipline,
476
462
  agentTrack,
477
- stages,
478
463
  });
479
464
 
480
465
  const profile = generateStageAgentProfile({
@@ -488,7 +473,6 @@ function createAllStagesPreview(context) {
488
473
  agentDiscipline,
489
474
  agentTrack,
490
475
  stages,
491
- agentIndex,
492
476
  });
493
477
 
494
478
  return { stage, derived, profile };
@@ -521,8 +505,7 @@ function createAllStagesPreview(context) {
521
505
  createDownloadAllButton(
522
506
  stageAgents,
523
507
  skillFiles,
524
- vscodeSettings,
525
- devcontainer,
508
+ claudeCodeSettings,
526
509
  context,
527
510
  ),
528
511
 
@@ -532,7 +515,7 @@ function createAllStagesPreview(context) {
532
515
  h2({}, `Agents (${stageAgents.length})`),
533
516
  p(
534
517
  { className: "text-muted" },
535
- "Stage-specific agents with appropriate tools, constraints, and handoffs.",
518
+ "Stage-specific agents with skills, constraints, and stage transitions.",
536
519
  ),
537
520
  div(
538
521
  { className: "agent-cards-grid" },
@@ -598,11 +581,9 @@ function createSingleStagePreview(context, stage) {
598
581
  skills,
599
582
  behaviours,
600
583
  agentBehaviours,
601
- vscodeSettings,
602
- devcontainer,
584
+ claudeCodeSettings,
603
585
  stages,
604
586
  templates,
605
- agentIndex,
606
587
  } = context;
607
588
 
608
589
  const profile = generateStageAgentProfile({
@@ -616,7 +597,6 @@ function createSingleStagePreview(context, stage) {
616
597
  agentDiscipline,
617
598
  agentTrack,
618
599
  stages,
619
- agentIndex,
620
600
  });
621
601
 
622
602
  // Get skills for this stage (using full derived skills)
@@ -645,8 +625,7 @@ function createSingleStagePreview(context, stage) {
645
625
  createDownloadSingleButton(
646
626
  profile,
647
627
  skillFiles,
648
- vscodeSettings,
649
- devcontainer,
628
+ claudeCodeSettings,
650
629
  templates,
651
630
  ),
652
631
 
@@ -756,16 +735,14 @@ function buildSkillFileCard(skill, templates) {
756
735
  * Create download all button for all stages
757
736
  * @param {Array} stageAgents - Array of {stage, derived, profile}
758
737
  * @param {Array} skillFiles - Array of skill file objects
759
- * @param {Object} vscodeSettings - VS Code settings
760
- * @param {Object} devcontainer - Devcontainer config
738
+ * @param {Object} claudeCodeSettings - Claude Code settings
761
739
  * @param {Object} context - Context with discipline/track info and templates
762
740
  * @returns {HTMLElement}
763
741
  */
764
742
  function createDownloadAllButton(
765
743
  stageAgents,
766
744
  skillFiles,
767
- vscodeSettings,
768
- devcontainer,
745
+ claudeCodeSettings,
769
746
  context,
770
747
  ) {
771
748
  const { humanDiscipline, humanTrack, templates } = context;
@@ -783,10 +760,10 @@ function createDownloadAllButton(
783
760
  const JSZip = await importJSZip();
784
761
  const zip = new JSZip();
785
762
 
786
- // Add all stage agent profiles
763
+ // Add all stage agent profiles to .claude/agents/
787
764
  for (const { profile } of stageAgents) {
788
765
  const content = formatAgentProfile(profile, templates.agent);
789
- zip.file(`.github/agents/${profile.filename}`, content);
766
+ zip.file(`.claude/agents/${profile.filename}`, content);
790
767
  }
791
768
 
792
769
  // Add skills (SKILL.md + optional install script + optional reference)
@@ -812,27 +789,11 @@ function createDownloadAllButton(
812
789
  }
813
790
  }
814
791
 
815
- // Add VS Code settings
816
- if (Object.keys(vscodeSettings).length > 0) {
817
- zip.file(
818
- ".vscode/settings.json",
819
- JSON.stringify(vscodeSettings, null, 2) + "\n",
820
- );
821
- }
822
-
823
- // Add devcontainer.json with VS Code settings embedded
824
- if (devcontainer && Object.keys(devcontainer).length > 0) {
825
- const devcontainerJson = {
826
- ...devcontainer,
827
- customizations: {
828
- vscode: {
829
- settings: vscodeSettings,
830
- },
831
- },
832
- };
792
+ // Add Claude Code settings
793
+ if (Object.keys(claudeCodeSettings).length > 0) {
833
794
  zip.file(
834
- ".devcontainer/devcontainer.json",
835
- JSON.stringify(devcontainerJson, null, 2) + "\n",
795
+ ".claude/settings.json",
796
+ JSON.stringify(claudeCodeSettings, null, 2) + "\n",
836
797
  );
837
798
  }
838
799
 
@@ -861,16 +822,14 @@ function createDownloadAllButton(
861
822
  * Create download button for single stage
862
823
  * @param {Object} profile - Agent profile
863
824
  * @param {Array} skillFiles - Skill files
864
- * @param {Object} vscodeSettings - VS Code settings
865
- * @param {Object} devcontainer - Devcontainer config
825
+ * @param {Object} claudeCodeSettings - Claude Code settings
866
826
  * @param {{agent: string, skill: string}} templates - Mustache templates
867
827
  * @returns {HTMLElement}
868
828
  */
869
829
  function createDownloadSingleButton(
870
830
  profile,
871
831
  skillFiles,
872
- vscodeSettings,
873
- devcontainer,
832
+ claudeCodeSettings,
874
833
  templates,
875
834
  ) {
876
835
  const btn = document.createElement("button");
@@ -885,9 +844,9 @@ function createDownloadSingleButton(
885
844
  const JSZip = await importJSZip();
886
845
  const zip = new JSZip();
887
846
 
888
- // Add profile
847
+ // Add profile to .claude/agents/
889
848
  const content = formatAgentProfile(profile, templates.agent);
890
- zip.file(`.github/agents/${profile.filename}`, content);
849
+ zip.file(`.claude/agents/${profile.filename}`, content);
891
850
 
892
851
  // Add skills (SKILL.md + optional install script + optional reference)
893
852
  for (const skill of skillFiles) {
@@ -912,27 +871,11 @@ function createDownloadSingleButton(
912
871
  }
913
872
  }
914
873
 
915
- // Add VS Code settings
916
- if (Object.keys(vscodeSettings).length > 0) {
917
- zip.file(
918
- ".vscode/settings.json",
919
- JSON.stringify(vscodeSettings, null, 2) + "\n",
920
- );
921
- }
922
-
923
- // Add devcontainer.json with VS Code settings embedded
924
- if (devcontainer && Object.keys(devcontainer).length > 0) {
925
- const devcontainerJson = {
926
- ...devcontainer,
927
- customizations: {
928
- vscode: {
929
- settings: vscodeSettings,
930
- },
931
- },
932
- };
874
+ // Add Claude Code settings
875
+ if (Object.keys(claudeCodeSettings).length > 0) {
933
876
  zip.file(
934
- ".devcontainer/devcontainer.json",
935
- JSON.stringify(devcontainerJson, null, 2) + "\n",
877
+ ".claude/settings.json",
878
+ JSON.stringify(claudeCodeSettings, null, 2) + "\n",
936
879
  );
937
880
  }
938
881
 
@@ -982,7 +925,7 @@ function createHelpSection() {
982
925
  p(
983
926
  {},
984
927
  "Agents are generated for each stage: Plan (research), Code (implement), and Review (verify). " +
985
- "Each stage has specific tools, constraints, and handoffs.",
928
+ "Each stage has specific skills, constraints, and stage transitions.",
986
929
  ),
987
930
  ),
988
931
  div(
@@ -990,8 +933,8 @@ function createHelpSection() {
990
933
  div({ className: "detail-item-label" }, "Agent Profiles"),
991
934
  p(
992
935
  {},
993
- "The .agent.md files contain the agent's identity, capabilities, and constraints. " +
994
- "Place them in .github/agents/ to register with GitHub Copilot.",
936
+ "The .md files contain the agent's identity, skills, and constraints. " +
937
+ "Place them in .claude/agents/ for Claude Code to discover.",
995
938
  ),
996
939
  ),
997
940
  div(
@@ -299,7 +299,7 @@ export function renderLanding() {
299
299
  p(
300
300
  {},
301
301
  "Generate coding agent team configurations from discipline × track combinations " +
302
- "for GitHub Copilot custom agents.",
302
+ "for Claude Code agents.",
303
303
  ),
304
304
  div(
305
305
  { className: "page-actions", style: "justify-content: center" },
package/src/slides.html CHANGED
@@ -8,7 +8,7 @@
8
8
  <script type="importmap">
9
9
  {
10
10
  "imports": {
11
- "mustache": "https://esm.sh/mustache@4.2.0",
11
+ "mustache": "/vendor/mustache.mjs",
12
12
  "@forwardimpact/map": "/map/lib/index.js",
13
13
  "@forwardimpact/map/levels": "/map/lib/levels.js",
14
14
  "@forwardimpact/map/loader": "/map/lib/loader.js",
@@ -3,22 +3,13 @@
3
3
  name: {{name}}
4
4
  {{/name}}
5
5
  description: {{{description}}}
6
- {{#infer}}
7
- infer: {{infer}}
8
- {{/infer}}
9
- {{#handoffs.length}}
10
- handoffs:
11
- {{#handoffs}}
12
- - label: {{label}}
13
- {{#agent}}
14
- agent: {{agent}}
15
- {{/agent}}
16
- prompt: "{{{prompt}}}"
17
- {{#send}}
18
- send: {{send}}
19
- {{/send}}
20
- {{/handoffs}}
21
- {{/handoffs.length}}
6
+ model: sonnet
7
+ {{#skills.length}}
8
+ skills:
9
+ {{#skills}}
10
+ - {{.}}
11
+ {{/skills}}
12
+ {{/skills.length}}
22
13
  ---
23
14
 
24
15
  # {{title}}
@@ -41,89 +32,77 @@ handoffs:
41
32
  {{#hasWorkingStyles}}
42
33
 
43
34
  ## Working style
44
- {{#workingStyles}}
45
35
 
46
- ### {{title}}
36
+ {{#workingStyles}}
37
+ **{{title}}**
47
38
 
48
39
  {{{content}}}
40
+
49
41
  {{/workingStyles}}
50
42
  {{/hasWorkingStyles}}
51
43
  {{#hasSkills}}
52
44
 
53
45
  ## Required skills
54
46
 
55
- **MANDATORY:** Before starting work, you MUST read ALL listed skill files below,
56
- not just the most relevant one. Every skill contributes project-specific
57
- guidance, required tools, and technology standards. Pre-training knowledge alone
58
- is insufficient—skills contain organizational standards that override general
59
- knowledge.
60
-
61
- **FIRST ACTION:** Read every skill file listed below. For each skill, note
62
- its `<read_then_do_{{stageId}}>` and `<do_then_confirm_{{stageId}}>` checklist
63
- items. List all checklist items from all skills before beginning any work. Do
64
- not start implementation until every skill has been read and its checklists
65
- acknowledged.
66
-
67
- Each skill contains marked-up sections and references for precise navigation:
68
-
69
- - `<read_then_do_{{stageId}}>` — Read-Then-Do checklist for the
70
- {{stageName}} stage. Read and understand these items BEFORE starting work.
71
- These are prerequisites and context you must absorb first.
72
- - `<do_then_confirm_{{stageId}}>` — Do-Then-Confirm checklist for the
73
- {{stageName}} stage. Complete your work, then verify each item. These are
74
- quality gates to check AFTER implementation.
75
- - `<required_tools>` — Mandatory tools for this skill. You MUST use these
76
- organizational standards that override general knowledge or personal
77
- preferences.
78
- {{#isOnboard}}
79
- - `scripts/install.sh` — Self-contained install script for environment setup.
80
- **Step 1 of onboarding — run FIRST:** Execute
81
- `bash .claude/skills/<skill-name>/scripts/install.sh` for each skill before
82
- doing any manual setup. Only install manually if the script is missing or
83
- fails. Do not skip this step even if you can install the same tools manually.
84
- - `references/REFERENCE.md` — Detailed code examples and reference material.
85
- Consult this for implementation patterns, common pitfalls, and verification
86
- steps.
87
- {{/isOnboard}}
88
-
89
- | Skill | Location | Use when |
90
- | ----- | -------- | -------- |
47
+ | Skill | Use when |
48
+ | ----- | -------- |
91
49
  {{#skillIndex}}
92
- | {{{name}}} | `.claude/skills/{{dirname}}/SKILL.md` | {{{useWhen}}} |
50
+ | {{{name}}} | {{{useWhen}}} |
93
51
  {{/skillIndex}}
52
+ {{#isOnboard}}
53
+
54
+ For each skill, run `bash .claude/skills/<skill-name>/scripts/install.sh`
55
+ BEFORE any manual setup. Consult `references/REFERENCE.md` for implementation
56
+ patterns.
57
+ {{/isOnboard}}
94
58
  {{/hasSkills}}
95
- {{#hasAgentIndex}}
59
+ {{#hasStageTransitions}}
60
+
61
+ ## Stage transitions
62
+ {{#stageTransitions}}
63
+
64
+ When your work is complete, the next stage is **{{targetStageName}}**.
96
65
 
97
- ## Required subagent delegations
66
+ {{{summaryInstruction}}}
67
+ {{#hasEntryCriteria}}
98
68
 
99
- **MANDATORY:** You MUST delegate work outside your speciality using the
100
- `runSubagent` tool. Do not attempt work that another agent is better suited for.
69
+ The {{targetStageName}} stage requires the following entry criteria:
70
+ {{#entryCriteria}}
71
+ - [ ] {{{.}}}
72
+ {{/entryCriteria}}
101
73
 
102
- You are part of an agentic team with specialized roles. Attempting work outside
103
- your speciality produces inferior results and violates team structure. If you
104
- cannot delegate due to a blocking constraint, document in your output: (1) the
105
- specialized work required, (2) the specific constraint preventing delegation,
106
- and (3) the compromised approach with acknowledged limitations.
74
+ If critical items are missing, continue working in the current stage.
75
+ {{/hasEntryCriteria}}
76
+ {{/stageTransitions}}
77
+ {{/hasStageTransitions}}
107
78
 
108
- | Agent name | Speciality | Description |
109
- | ---------- | ---------- | ----------- |
110
- {{#agentIndex}}
111
- | `{{id}}` | {{{name}}} | {{{description}}} |
112
- {{/agentIndex}}
113
- {{/hasAgentIndex}}
79
+ {{#hasReturnFormat}}
114
80
 
115
81
  ## Return format
116
82
 
117
- When completing work (for handoff or as a subagent), provide:
83
+ When completing work, provide:
118
84
 
119
- 1. **Work completed**: What was accomplished
120
- 2. **Checklist status**: Items verified from skill Do-Then-Confirm checklists
121
- 3. **Recommendation**: Ready for next stage, or needs more work
85
+ {{#returnFormat}}
86
+ 1. {{{.}}}
87
+ {{/returnFormat}}
88
+ {{/hasReturnFormat}}
122
89
 
123
90
  {{#hasConstraints}}
124
91
  ## Constraints
125
92
 
126
- {{#constraints}}
93
+ {{#hasStageConstraints}}
94
+ {{#stageConstraints}}
95
+ - {{{.}}}
96
+ {{/stageConstraints}}
97
+ {{/hasStageConstraints}}
98
+ {{#hasDisciplineOrTrackConstraints}}
99
+
100
+ **General:**
101
+ {{#disciplineConstraints}}
102
+ - {{{.}}}
103
+ {{/disciplineConstraints}}
104
+ {{#trackConstraints}}
127
105
  - {{{.}}}
128
- {{/constraints}}
106
+ {{/trackConstraints}}
107
+ {{/hasDisciplineOrTrackConstraints}}
129
108
  {{/hasConstraints}}