@forwardimpact/pathway 0.23.2 → 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 +3 -3
- package/package.json +3 -3
- package/src/commands/agent.js +11 -93
- package/src/commands/build.js +18 -0
- package/src/commands/dev.js +15 -0
- package/src/components/skill-matrix.js +17 -9
- package/src/formatters/agent/dom.js +27 -47
- package/src/formatters/agent/profile.js +49 -35
- package/src/formatters/job/dom.js +5 -59
- package/src/handout.html +1 -1
- package/src/index.html +9 -3
- package/src/lib/yaml-loader.js +10 -24
- package/src/main.js +1 -0
- package/src/pages/agent-builder.js +26 -83
- package/src/pages/landing.js +1 -1
- package/src/slides.html +1 -1
- package/templates/agent.template.md +55 -76
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
|
|
18
|
-
(
|
|
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
|
|
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.
|
|
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": {
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"./commands": "./src/commands/index.js"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@forwardimpact/map": "^0.
|
|
44
|
-
"@forwardimpact/libskill": "^
|
|
43
|
+
"@forwardimpact/map": "^0.14.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",
|
package/src/commands/agent.js
CHANGED
|
@@ -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
|
|
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}
|
|
63
|
+
* @param {Object} claudeCodeSettings - Settings loaded from data
|
|
65
64
|
*/
|
|
66
|
-
async function
|
|
67
|
-
const settingsPath = join(baseDir, ".
|
|
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, ...
|
|
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, ".
|
|
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
|
|
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
|
|
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
|
|
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:`));
|
package/src/commands/build.js
CHANGED
|
@@ -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");
|
package/src/commands/dev.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
32
|
-
const
|
|
33
|
-
if (
|
|
34
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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}
|
|
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
|
-
|
|
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
|
-
|
|
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}
|
|
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
|
-
|
|
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 .
|
|
184
|
+
// Add main profile to .claude/agents/ folder
|
|
185
185
|
const profileContent = formatAgentProfile(profile);
|
|
186
|
-
zip.file(`.
|
|
186
|
+
zip.file(`.claude/agents/${profile.filename}`, profileContent);
|
|
187
187
|
|
|
188
|
-
// Add role agent profiles to .
|
|
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(`.
|
|
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
|
|
201
|
-
if (Object.keys(
|
|
200
|
+
// Add Claude Code settings
|
|
201
|
+
if (Object.keys(claudeCodeSettings).length > 0) {
|
|
202
202
|
zip.file(
|
|
203
|
-
".
|
|
204
|
-
JSON.stringify(
|
|
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.
|
|
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 {
|
|
243
|
-
const { stage, tools,
|
|
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,
|
|
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}
|
|
370
|
+
* @param {Object} claudeCodeSettings - Claude Code settings
|
|
391
371
|
* @returns {HTMLElement}
|
|
392
372
|
*/
|
|
393
|
-
function createStageAgentDownloadButton(profile,
|
|
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(`.
|
|
389
|
+
zip.file(`.claude/agents/${profile.filename}`, profileContent);
|
|
410
390
|
|
|
411
|
-
// Add
|
|
412
|
-
if (Object.keys(
|
|
391
|
+
// Add Claude Code settings
|
|
392
|
+
if (Object.keys(claudeCodeSettings).length > 0) {
|
|
413
393
|
zip.file(
|
|
414
|
-
".
|
|
415
|
-
JSON.stringify(
|
|
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 .
|
|
5
|
-
* following the
|
|
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 {
|
|
33
|
-
* @param {
|
|
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<{
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
|
83
|
+
// Frontmatter
|
|
77
84
|
name: frontmatter.name,
|
|
78
85
|
description: flattenToLine(frontmatter.description),
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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 .
|
|
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
|
|
109
|
-
* @param {
|
|
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 .
|
|
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,
|
|
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": "
|
|
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
|
|
19
|
-
|
|
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": "
|
|
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",
|
package/src/lib/yaml-loader.js
CHANGED
|
@@ -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
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
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
|
-
|
|
323
|
-
devcontainer: devcontainer || {},
|
|
324
|
-
copilotSetupSteps: copilotSetupSteps || null,
|
|
310
|
+
claudeCodeSettings: claudeCodeSettings || {},
|
|
325
311
|
};
|
|
326
312
|
}
|
package/src/main.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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}
|
|
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
|
-
|
|
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(`.
|
|
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
|
|
816
|
-
if (Object.keys(
|
|
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
|
-
".
|
|
835
|
-
JSON.stringify(
|
|
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}
|
|
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
|
-
|
|
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(`.
|
|
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
|
|
916
|
-
if (Object.keys(
|
|
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
|
-
".
|
|
935
|
-
JSON.stringify(
|
|
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
|
|
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 .
|
|
994
|
-
"Place them in .
|
|
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(
|
package/src/pages/landing.js
CHANGED
|
@@ -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
|
|
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": "
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
{{#
|
|
10
|
-
|
|
11
|
-
{{
|
|
12
|
-
|
|
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
|
-
|
|
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
|
-
|
|
56
|
-
|
|
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}}} |
|
|
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
|
-
{{#
|
|
59
|
+
{{#hasStageTransitions}}
|
|
60
|
+
|
|
61
|
+
## Stage transitions
|
|
62
|
+
{{#stageTransitions}}
|
|
63
|
+
|
|
64
|
+
When your work is complete, the next stage is **{{targetStageName}}**.
|
|
96
65
|
|
|
97
|
-
|
|
66
|
+
{{{summaryInstruction}}}
|
|
67
|
+
{{#hasEntryCriteria}}
|
|
98
68
|
|
|
99
|
-
|
|
100
|
-
|
|
69
|
+
The {{targetStageName}} stage requires the following entry criteria:
|
|
70
|
+
{{#entryCriteria}}
|
|
71
|
+
- [ ] {{{.}}}
|
|
72
|
+
{{/entryCriteria}}
|
|
101
73
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
|
|
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
|
|
83
|
+
When completing work, provide:
|
|
118
84
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
85
|
+
{{#returnFormat}}
|
|
86
|
+
1. {{{.}}}
|
|
87
|
+
{{/returnFormat}}
|
|
88
|
+
{{/hasReturnFormat}}
|
|
122
89
|
|
|
123
90
|
{{#hasConstraints}}
|
|
124
91
|
## Constraints
|
|
125
92
|
|
|
126
|
-
{{#
|
|
93
|
+
{{#hasStageConstraints}}
|
|
94
|
+
{{#stageConstraints}}
|
|
95
|
+
- {{{.}}}
|
|
96
|
+
{{/stageConstraints}}
|
|
97
|
+
{{/hasStageConstraints}}
|
|
98
|
+
{{#hasDisciplineOrTrackConstraints}}
|
|
99
|
+
|
|
100
|
+
**General:**
|
|
101
|
+
{{#disciplineConstraints}}
|
|
102
|
+
- {{{.}}}
|
|
103
|
+
{{/disciplineConstraints}}
|
|
104
|
+
{{#trackConstraints}}
|
|
127
105
|
- {{{.}}}
|
|
128
|
-
{{/
|
|
106
|
+
{{/trackConstraints}}
|
|
107
|
+
{{/hasDisciplineOrTrackConstraints}}
|
|
129
108
|
{{/hasConstraints}}
|