@forwardimpact/pathway 0.7.0 → 0.8.1
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/bin/fit-pathway.js +126 -58
- package/package.json +3 -3
- package/src/commands/agent.js +46 -1
- package/src/commands/job.js +32 -1
- package/src/commands/site.js +24 -1
- package/src/components/card.js +1 -1
- package/src/components/detail.js +1 -1
- package/src/css/components/badges.css +3 -3
- package/src/css/views/print.css +2 -2
- package/src/formatters/agent/profile.js +33 -25
- package/src/formatters/agent/skill.js +14 -4
- package/src/formatters/job/description.js +22 -9
- package/src/formatters/job/dom.js +19 -10
- package/src/formatters/job/markdown.js +16 -8
- package/src/formatters/toolkit/dom.js +75 -0
- package/src/formatters/toolkit/markdown.js +38 -0
- package/src/pages/agent-builder.js +47 -1
- package/templates/agent.template.md +47 -27
- package/templates/job.template.md +24 -15
- package/templates/skill.template.md +27 -18
package/bin/fit-pathway.js
CHANGED
|
@@ -27,10 +27,6 @@
|
|
|
27
27
|
* --list Output IDs only (for piping)
|
|
28
28
|
* --json Output as JSON
|
|
29
29
|
* --help Show help
|
|
30
|
-
*
|
|
31
|
-
* Validation (moved to fit-schema):
|
|
32
|
-
* npx fit-schema validate
|
|
33
|
-
* npx fit-schema generate-index
|
|
34
30
|
*/
|
|
35
31
|
|
|
36
32
|
import { join, resolve } from "path";
|
|
@@ -78,76 +74,142 @@ Engineering Pathway CLI
|
|
|
78
74
|
Usage:
|
|
79
75
|
npx fit-pathway <command> [options]
|
|
80
76
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
77
|
+
Global Options:
|
|
78
|
+
--list Output IDs only (for piping to other commands)
|
|
79
|
+
--json Output as JSON
|
|
80
|
+
--data=PATH Path to data directory (default: ./data or examples/)
|
|
81
|
+
--help Show this help message
|
|
82
|
+
|
|
83
|
+
────────────────────────────────────────────────────────────────────────────────
|
|
84
|
+
GETTING STARTED
|
|
85
|
+
────────────────────────────────────────────────────────────────────────────────
|
|
84
86
|
|
|
85
|
-
Getting Started:
|
|
86
87
|
init Create ./data/ with example data
|
|
87
88
|
serve [--port=PORT] Serve web app at http://localhost:3000
|
|
88
89
|
site [--output=PATH] Generate static site to ./site/
|
|
89
90
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
track [<id>] Browse tracks
|
|
94
|
-
behaviour [<id>] Browse behaviours
|
|
95
|
-
skill [<id>] Browse skills
|
|
96
|
-
--agent Output as agent SKILL.md format
|
|
97
|
-
driver [<id>] Browse drivers
|
|
98
|
-
stage [<id>] Browse lifecycle stages
|
|
99
|
-
tool [<name>] Browse recommended tools
|
|
100
|
-
|
|
101
|
-
Composite Commands:
|
|
102
|
-
job [<discipline> <grade>] [--track=TRACK] Generate job definition
|
|
103
|
-
interview <discipline> <grade> [--track=TRACK] [--type=TYPE]
|
|
104
|
-
Generate interview questions
|
|
105
|
-
progress <discipline> <grade> [--track=TRACK] [--compare=GRADE]
|
|
106
|
-
Show career progression
|
|
107
|
-
questions [filters] Browse interview questions
|
|
108
|
-
agent <discipline> [--track=<track>] Generate AI coding agent
|
|
91
|
+
────────────────────────────────────────────────────────────────────────────────
|
|
92
|
+
ENTITY COMMANDS
|
|
93
|
+
────────────────────────────────────────────────────────────────────────────────
|
|
109
94
|
|
|
110
|
-
|
|
111
|
-
--list Output IDs only (for piping to other commands)
|
|
112
|
-
--json Output as JSON
|
|
113
|
-
--data=PATH Path to data directory (default: ./data or examples/)
|
|
114
|
-
--help Show this help message
|
|
95
|
+
All entity commands support: summary (default), --list (IDs for piping), <id> (detail)
|
|
115
96
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
--stats Show detailed statistics
|
|
123
|
-
--format=FORMAT Output format: table, yaml, json
|
|
124
|
-
|
|
125
|
-
Agent Options:
|
|
126
|
-
--track=TRACK Track for the agent (e.g., platform, forward_deployed)
|
|
127
|
-
--output=PATH Write files to directory (without this, outputs to console)
|
|
128
|
-
--stage=STAGE Generate specific stage agent (plan, code, review)
|
|
129
|
-
--all-stages Generate all stage agents (default)
|
|
97
|
+
discipline [<id>] Browse engineering disciplines
|
|
98
|
+
grade [<id>] Browse career grades/levels
|
|
99
|
+
track [<id>] Browse track specializations
|
|
100
|
+
behaviour [<id>] Browse professional behaviours
|
|
101
|
+
driver [<id>] Browse outcome drivers
|
|
102
|
+
stage [<id>] Browse lifecycle stages
|
|
130
103
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
104
|
+
skill [<id>] Browse skills
|
|
105
|
+
--agent Output as agent SKILL.md format
|
|
106
|
+
|
|
107
|
+
tool [<name>] Browse recommended tools (aggregated from skills)
|
|
108
|
+
|
|
109
|
+
────────────────────────────────────────────────────────────────────────────────
|
|
110
|
+
JOB COMMAND
|
|
111
|
+
────────────────────────────────────────────────────────────────────────────────
|
|
136
112
|
|
|
137
|
-
|
|
138
|
-
npx fit-pathway tool --list # Tool names for piping
|
|
139
|
-
npx fit-pathway tool DuckDB # Tool detail with skill usages
|
|
113
|
+
Generate job definitions from discipline × grade × track combinations.
|
|
140
114
|
|
|
141
|
-
|
|
142
|
-
npx fit-pathway job
|
|
115
|
+
Usage:
|
|
116
|
+
npx fit-pathway job Summary with stats
|
|
117
|
+
npx fit-pathway job --list All valid combinations
|
|
118
|
+
npx fit-pathway job <discipline> <grade> Detail view (trackless)
|
|
119
|
+
npx fit-pathway job <d> <g> --track=<track> Detail view (with track)
|
|
120
|
+
npx fit-pathway job <d> <g> --skills Plain list of skill IDs
|
|
121
|
+
npx fit-pathway job <d> <g> --tools Plain list of tool names
|
|
122
|
+
npx fit-pathway job <d> <g> --checklist=<stage> Show handoff checklist
|
|
123
|
+
|
|
124
|
+
Options:
|
|
125
|
+
--track=TRACK Track specialization (e.g., platform, forward_deployed)
|
|
126
|
+
--skills Output plain list of skill IDs (for piping)
|
|
127
|
+
--tools Output plain list of tool names (for piping)
|
|
128
|
+
--checklist=STAGE Show checklist for stage handoff (plan, code)
|
|
129
|
+
|
|
130
|
+
Examples:
|
|
143
131
|
npx fit-pathway job software_engineering L4
|
|
144
132
|
npx fit-pathway job software_engineering L4 --track=platform
|
|
145
|
-
npx fit-pathway job se L3 --track=platform --
|
|
133
|
+
npx fit-pathway job se L3 --track=platform --skills
|
|
134
|
+
npx fit-pathway job se L3 --track=platform --tools
|
|
135
|
+
|
|
136
|
+
────────────────────────────────────────────────────────────────────────────────
|
|
137
|
+
AGENT COMMAND
|
|
138
|
+
────────────────────────────────────────────────────────────────────────────────
|
|
139
|
+
|
|
140
|
+
Generate AI coding agent configurations from discipline × track × stage.
|
|
146
141
|
|
|
142
|
+
Usage:
|
|
143
|
+
npx fit-pathway agent Summary with stats
|
|
144
|
+
npx fit-pathway agent --list All valid combinations
|
|
145
|
+
npx fit-pathway agent <discipline> --track=<track> Generate all stage agents
|
|
146
|
+
npx fit-pathway agent <d> --track=<t> --stage=<s> Generate single stage agent
|
|
147
|
+
npx fit-pathway agent <d> --track=<t> --skills Plain list of skill IDs
|
|
148
|
+
npx fit-pathway agent <d> --track=<t> --tools Plain list of tool names
|
|
149
|
+
|
|
150
|
+
Options:
|
|
151
|
+
--track=TRACK Track for the agent (required for generation)
|
|
152
|
+
--stage=STAGE Generate specific stage agent (plan, code, review)
|
|
153
|
+
--output=PATH Write files to directory (without this, outputs to console)
|
|
154
|
+
--skills Output plain list of skill IDs (for piping)
|
|
155
|
+
--tools Output plain list of tool names (for piping)
|
|
156
|
+
|
|
157
|
+
Examples:
|
|
158
|
+
npx fit-pathway agent software_engineering --track=platform
|
|
159
|
+
npx fit-pathway agent software_engineering --track=platform --stage=plan
|
|
160
|
+
npx fit-pathway agent software_engineering --track=platform --output=./agents
|
|
161
|
+
npx fit-pathway agent software_engineering --track=platform --skills
|
|
162
|
+
|
|
163
|
+
────────────────────────────────────────────────────────────────────────────────
|
|
164
|
+
INTERVIEW COMMAND
|
|
165
|
+
────────────────────────────────────────────────────────────────────────────────
|
|
166
|
+
|
|
167
|
+
Generate interview question sets based on job requirements.
|
|
168
|
+
|
|
169
|
+
Usage:
|
|
170
|
+
npx fit-pathway interview <discipline> <grade>
|
|
171
|
+
npx fit-pathway interview <d> <g> --track=<track>
|
|
172
|
+
npx fit-pathway interview <d> <g> --type=<type>
|
|
173
|
+
|
|
174
|
+
Options:
|
|
175
|
+
--track=TRACK Track specialization
|
|
176
|
+
--type=TYPE Interview type: full (default), short
|
|
177
|
+
|
|
178
|
+
────────────────────────────────────────────────────────────────────────────────
|
|
179
|
+
PROGRESS COMMAND
|
|
180
|
+
────────────────────────────────────────────────────────────────────────────────
|
|
181
|
+
|
|
182
|
+
Analyze career progression between grades.
|
|
183
|
+
|
|
184
|
+
Usage:
|
|
185
|
+
npx fit-pathway progress <discipline> <grade>
|
|
186
|
+
npx fit-pathway progress <d> <g> --track=<track>
|
|
187
|
+
npx fit-pathway progress <d> <g> --compare=<to_grade>
|
|
188
|
+
|
|
189
|
+
Options:
|
|
190
|
+
--track=TRACK Track specialization
|
|
191
|
+
--compare=GRADE Compare to specific grade
|
|
192
|
+
|
|
193
|
+
────────────────────────────────────────────────────────────────────────────────
|
|
194
|
+
QUESTIONS COMMAND
|
|
195
|
+
────────────────────────────────────────────────────────────────────────────────
|
|
196
|
+
|
|
197
|
+
Browse and filter interview questions.
|
|
198
|
+
|
|
199
|
+
Usage:
|
|
200
|
+
npx fit-pathway questions
|
|
147
201
|
npx fit-pathway questions --level=practitioner
|
|
202
|
+
npx fit-pathway questions --skill=architecture_design
|
|
148
203
|
npx fit-pathway questions --stats
|
|
149
204
|
|
|
150
|
-
|
|
205
|
+
Options:
|
|
206
|
+
--level=LEVEL Filter by skill level
|
|
207
|
+
--maturity=MATURITY Filter by behaviour maturity
|
|
208
|
+
--skill=ID Filter to specific skill
|
|
209
|
+
--behaviour=ID Filter to specific behaviour
|
|
210
|
+
--capability=CAP Filter by capability
|
|
211
|
+
--stats Show detailed statistics
|
|
212
|
+
--format=FORMAT Output format: table, yaml, json
|
|
151
213
|
`;
|
|
152
214
|
|
|
153
215
|
/**
|
|
@@ -177,6 +239,8 @@ function parseArgs(args) {
|
|
|
177
239
|
stats: false,
|
|
178
240
|
// Job command options
|
|
179
241
|
checklist: null,
|
|
242
|
+
skills: false,
|
|
243
|
+
tools: false,
|
|
180
244
|
// Agent command options
|
|
181
245
|
output: null,
|
|
182
246
|
stage: null,
|
|
@@ -233,6 +297,10 @@ function parseArgs(args) {
|
|
|
233
297
|
result.agent = true;
|
|
234
298
|
} else if (arg.startsWith("--checklist=")) {
|
|
235
299
|
result.checklist = arg.slice(12);
|
|
300
|
+
} else if (arg === "--skills") {
|
|
301
|
+
result.skills = true;
|
|
302
|
+
} else if (arg === "--tools") {
|
|
303
|
+
result.tools = true;
|
|
236
304
|
} else if (arg.startsWith("--port=")) {
|
|
237
305
|
result.port = parseInt(arg.slice(7), 10);
|
|
238
306
|
} else if (arg.startsWith("--path=")) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forwardimpact/pathway",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "Career progression web app and CLI for exploring roles and generating agents",
|
|
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/schema": "^0.
|
|
44
|
-
"@forwardimpact/model": "^0.
|
|
43
|
+
"@forwardimpact/schema": "^0.3.0",
|
|
44
|
+
"@forwardimpact/model": "^0.3.0",
|
|
45
45
|
"mustache": "^4.2.0",
|
|
46
46
|
"simple-icons": "^16.7.0",
|
|
47
47
|
"yaml": "^2.3.4"
|
package/src/commands/agent.js
CHANGED
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
* npx pathway agent <discipline> [--track=<track>]
|
|
14
14
|
* npx pathway agent <discipline> --track=<track> --stage=plan
|
|
15
15
|
* npx pathway agent <discipline> --track=<track> --output=./agents
|
|
16
|
+
* npx pathway agent <discipline> [--track=<track>] --skills # Plain list of skill IDs
|
|
17
|
+
* npx pathway agent <discipline> [--track=<track>] --tools # Plain list of tool names
|
|
16
18
|
* npx pathway agent --list
|
|
17
19
|
*
|
|
18
20
|
* Examples:
|
|
@@ -36,7 +38,9 @@ import {
|
|
|
36
38
|
deriveReferenceGrade,
|
|
37
39
|
deriveAgentSkills,
|
|
38
40
|
generateSkillMd,
|
|
39
|
-
|
|
41
|
+
deriveToolkit,
|
|
42
|
+
buildAgentIndex,
|
|
43
|
+
} from "@forwardimpact/model";
|
|
40
44
|
import { formatAgentProfile } from "../formatters/agent/profile.js";
|
|
41
45
|
import { formatAgentSkill } from "../formatters/agent/skill.js";
|
|
42
46
|
import { formatError, formatSuccess } from "../lib/cli-output.js";
|
|
@@ -44,6 +48,7 @@ import {
|
|
|
44
48
|
loadAgentTemplate,
|
|
45
49
|
loadSkillTemplate,
|
|
46
50
|
} from "../lib/template-loader.js";
|
|
51
|
+
import { toolkitToPlainList } from "../formatters/toolkit/markdown.js";
|
|
47
52
|
|
|
48
53
|
/**
|
|
49
54
|
* Ensure directory exists for a file path
|
|
@@ -372,8 +377,47 @@ export async function runAgentCommand({ data, args, options, dataDir }) {
|
|
|
372
377
|
// Get reference grade for derivation
|
|
373
378
|
const grade = deriveReferenceGrade(data.grades);
|
|
374
379
|
|
|
380
|
+
// --skills: Output plain list of skill IDs (for piping)
|
|
381
|
+
if (options.skills) {
|
|
382
|
+
const derivedSkills = deriveAgentSkills({
|
|
383
|
+
discipline: humanDiscipline,
|
|
384
|
+
track: humanTrack,
|
|
385
|
+
grade,
|
|
386
|
+
skills: skillsWithAgent,
|
|
387
|
+
});
|
|
388
|
+
for (const skill of derivedSkills) {
|
|
389
|
+
console.log(skill.skillId);
|
|
390
|
+
}
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// --tools: Output plain list of tool names (for piping)
|
|
395
|
+
if (options.tools) {
|
|
396
|
+
const derivedSkills = deriveAgentSkills({
|
|
397
|
+
discipline: humanDiscipline,
|
|
398
|
+
track: humanTrack,
|
|
399
|
+
grade,
|
|
400
|
+
skills: skillsWithAgent,
|
|
401
|
+
});
|
|
402
|
+
const toolkit = deriveToolkit({
|
|
403
|
+
skillMatrix: derivedSkills,
|
|
404
|
+
skills: skillsWithAgent,
|
|
405
|
+
});
|
|
406
|
+
console.log(toolkitToPlainList(toolkit));
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
|
|
375
410
|
const baseDir = options.output || ".";
|
|
376
411
|
|
|
412
|
+
// Build agent index for all valid combinations
|
|
413
|
+
const agentIndex = buildAgentIndex({
|
|
414
|
+
disciplines: data.disciplines,
|
|
415
|
+
tracks: data.tracks,
|
|
416
|
+
stages: data.stages,
|
|
417
|
+
agentDisciplines: agentData.disciplines,
|
|
418
|
+
agentTracks: agentData.tracks,
|
|
419
|
+
});
|
|
420
|
+
|
|
377
421
|
// Common params for stage-based generation
|
|
378
422
|
const stageParams = {
|
|
379
423
|
discipline: humanDiscipline,
|
|
@@ -386,6 +430,7 @@ export async function runAgentCommand({ data, args, options, dataDir }) {
|
|
|
386
430
|
agentTrack,
|
|
387
431
|
capabilities: data.capabilities,
|
|
388
432
|
stages: data.stages,
|
|
433
|
+
agentIndex,
|
|
389
434
|
};
|
|
390
435
|
|
|
391
436
|
// Handle --stage flag for single stage agent
|
package/src/commands/job.js
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
* npx pathway job --list # All valid combinations (for piping)
|
|
9
9
|
* npx pathway job <discipline> <grade> # Detail view (trackless)
|
|
10
10
|
* npx pathway job <discipline> <grade> --track=<track> # Detail view (with track)
|
|
11
|
+
* npx pathway job <d> <g> [--track=<t>] --skills # Plain list of skill IDs
|
|
12
|
+
* npx pathway job <d> <g> [--track=<t>] --tools # Plain list of tool names
|
|
11
13
|
* npx pathway job se L3 --track=platform --checklist=code # Show checklist for handoff
|
|
12
14
|
* npx pathway job --validate # Validation checks
|
|
13
15
|
*/
|
|
@@ -21,6 +23,7 @@ import {
|
|
|
21
23
|
formatChecklistMarkdown,
|
|
22
24
|
} from "@forwardimpact/model/checklist";
|
|
23
25
|
import { loadJobTemplate } from "../lib/template-loader.js";
|
|
26
|
+
import { toolkitToPlainList } from "../formatters/toolkit/markdown.js";
|
|
24
27
|
|
|
25
28
|
/**
|
|
26
29
|
* Format job output
|
|
@@ -132,10 +135,38 @@ export async function runJobCommand({ data, args, options, dataDir }) {
|
|
|
132
135
|
});
|
|
133
136
|
|
|
134
137
|
if (!view) {
|
|
135
|
-
|
|
138
|
+
const combo = track
|
|
139
|
+
? `${discipline.id} × ${grade.id} × ${track.id}`
|
|
140
|
+
: `${discipline.id} × ${grade.id}`;
|
|
141
|
+
console.error(`Invalid combination: ${combo}`);
|
|
142
|
+
if (track) {
|
|
143
|
+
const validTracks =
|
|
144
|
+
discipline.validTracks?.filter((t) => t !== null) || [];
|
|
145
|
+
if (validTracks.length > 0) {
|
|
146
|
+
console.error(
|
|
147
|
+
`Valid tracks for ${discipline.id}: ${validTracks.join(", ")}`,
|
|
148
|
+
);
|
|
149
|
+
} else {
|
|
150
|
+
console.error(`${discipline.id} does not support tracks`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
136
153
|
process.exit(1);
|
|
137
154
|
}
|
|
138
155
|
|
|
156
|
+
// --skills: Output plain list of skill IDs (for piping)
|
|
157
|
+
if (options.skills) {
|
|
158
|
+
for (const skill of view.skillMatrix) {
|
|
159
|
+
console.log(skill.skillId);
|
|
160
|
+
}
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// --tools: Output plain list of tool names (for piping)
|
|
165
|
+
if (options.tools) {
|
|
166
|
+
console.log(toolkitToPlainList(view.toolkit));
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
139
170
|
if (options.json) {
|
|
140
171
|
console.log(JSON.stringify(view, null, 2));
|
|
141
172
|
return;
|
package/src/commands/site.js
CHANGED
|
@@ -15,6 +15,22 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
15
15
|
const __dirname = dirname(__filename);
|
|
16
16
|
const appDir = join(__dirname, "..");
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Resolve package directory using Node's module resolution.
|
|
20
|
+
* Works in both monorepo (development) and installed (production) contexts.
|
|
21
|
+
* @param {string} packageName - Package specifier (e.g., '@forwardimpact/schema')
|
|
22
|
+
* @returns {string} Absolute path to package lib directory
|
|
23
|
+
*/
|
|
24
|
+
function resolvePackageLib(packageName) {
|
|
25
|
+
// import.meta.resolve returns file:// URL to package's main entry (lib/index.js)
|
|
26
|
+
const mainUrl = import.meta.resolve(packageName);
|
|
27
|
+
// Convert to path and get lib directory
|
|
28
|
+
return dirname(fileURLToPath(mainUrl));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const schemaLibDir = resolvePackageLib("@forwardimpact/schema");
|
|
32
|
+
const modelLibDir = resolvePackageLib("@forwardimpact/model");
|
|
33
|
+
|
|
18
34
|
/**
|
|
19
35
|
* Files and directories to copy from app/
|
|
20
36
|
*/
|
|
@@ -31,7 +47,6 @@ const PUBLIC_ASSETS = [
|
|
|
31
47
|
// Directories
|
|
32
48
|
"css",
|
|
33
49
|
"lib",
|
|
34
|
-
"model",
|
|
35
50
|
"pages",
|
|
36
51
|
"slides",
|
|
37
52
|
"components",
|
|
@@ -113,6 +128,14 @@ ${framework.emojiIcon} Generating ${framework.title} static site...
|
|
|
113
128
|
}
|
|
114
129
|
}
|
|
115
130
|
|
|
131
|
+
// Copy @forwardimpact/schema and @forwardimpact/model packages
|
|
132
|
+
// These are needed by the browser's import map
|
|
133
|
+
console.log("📚 Copying package dependencies...");
|
|
134
|
+
await cp(schemaLibDir, join(outputDir, "schema/lib"), { recursive: true });
|
|
135
|
+
console.log(` ✓ schema/lib`);
|
|
136
|
+
await cp(modelLibDir, join(outputDir, "model/lib"), { recursive: true });
|
|
137
|
+
console.log(` ✓ model/lib`);
|
|
138
|
+
|
|
116
139
|
// Copy data directory (dereference symlinks to copy actual content)
|
|
117
140
|
console.log("📁 Copying data files...");
|
|
118
141
|
const dataOutputDir = join(outputDir, "data");
|
package/src/components/card.js
CHANGED
package/src/components/detail.js
CHANGED
|
@@ -188,7 +188,7 @@ export function createTagsList(tags, emptyMessage = "None") {
|
|
|
188
188
|
return p({ className: "text-muted" }, emptyMessage);
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
-
return div({ className: "tags" }, ...tags.map((tag) => createTag(tag)));
|
|
191
|
+
return div({ className: "info-tags" }, ...tags.map((tag) => createTag(tag)));
|
|
192
192
|
}
|
|
193
193
|
|
|
194
194
|
/**
|
|
@@ -176,14 +176,14 @@
|
|
|
176
176
|
border: 1px solid var(--color-border);
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
-
/*
|
|
180
|
-
.tags {
|
|
179
|
+
/* Info tags */
|
|
180
|
+
.info-tags {
|
|
181
181
|
display: flex;
|
|
182
182
|
gap: var(--space-sm);
|
|
183
183
|
flex-wrap: wrap;
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
-
.tag {
|
|
186
|
+
.info-tag {
|
|
187
187
|
display: inline-block;
|
|
188
188
|
padding: var(--space-xs) var(--space-sm);
|
|
189
189
|
background: var(--color-bg);
|
package/src/css/views/print.css
CHANGED
|
@@ -194,10 +194,10 @@
|
|
|
194
194
|
/* Preserve badge colors */
|
|
195
195
|
.slide-view .badge,
|
|
196
196
|
.slide-view .level-badge,
|
|
197
|
-
.slide-view .tag,
|
|
197
|
+
.slide-view .info-tag,
|
|
198
198
|
.badge,
|
|
199
199
|
.level-badge,
|
|
200
|
-
.tag {
|
|
200
|
+
.info-tag {
|
|
201
201
|
-webkit-print-color-adjust: exact;
|
|
202
202
|
print-color-adjust: exact;
|
|
203
203
|
background-color: inherit !important;
|
|
@@ -12,6 +12,12 @@ import Mustache from "mustache";
|
|
|
12
12
|
|
|
13
13
|
import { trimValue, trimRequired, trimFields } from "../shared.js";
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {Object} WorkingStyleEntry
|
|
17
|
+
* @property {string} title - Section title
|
|
18
|
+
* @property {string} content - Working style content (markdown)
|
|
19
|
+
*/
|
|
20
|
+
|
|
15
21
|
/**
|
|
16
22
|
* Prepare agent profile data for template rendering
|
|
17
23
|
* Normalizes string values by trimming trailing newlines for consistent template output.
|
|
@@ -27,30 +33,32 @@ import { trimValue, trimRequired, trimFields } from "../shared.js";
|
|
|
27
33
|
* @param {string} params.bodyData.identity - Core identity text
|
|
28
34
|
* @param {string} [params.bodyData.priority] - Priority/philosophy statement
|
|
29
35
|
* @param {Array<{name: string, dirname: string, useWhen: string}>} params.bodyData.skillIndex - Skill index entries
|
|
30
|
-
* @param {
|
|
31
|
-
* @param {
|
|
32
|
-
* @param {string} params.bodyData.operationalContext - Operational context text
|
|
33
|
-
* @param {string} params.bodyData.workingStyle - Working style markdown section
|
|
36
|
+
* @param {string} params.bodyData.roleContext - Role context text
|
|
37
|
+
* @param {WorkingStyleEntry[]} params.bodyData.workingStyles - Working style entries
|
|
34
38
|
* @param {string} [params.bodyData.beforeHandoff] - Before handoff checklist markdown
|
|
35
39
|
* @param {string[]} params.bodyData.constraints - List of constraints
|
|
40
|
+
* @param {Array<{id: string, name: string, description: string}>} [params.bodyData.agentIndex] - List of all available agents
|
|
41
|
+
* @param {boolean} [params.bodyData.hasAgentIndex] - Whether agent index is available
|
|
36
42
|
* @returns {Object} Data object ready for Mustache template
|
|
37
43
|
*/
|
|
38
44
|
function prepareAgentProfileData({ frontmatter, bodyData }) {
|
|
39
|
-
// Trim array fields using helpers
|
|
45
|
+
// Trim array fields using shared helpers
|
|
40
46
|
const handoffs = trimFields(frontmatter.handoffs, { prompt: "required" });
|
|
41
|
-
const beforeMakingChanges = trimFields(bodyData.beforeMakingChanges, {
|
|
42
|
-
text: "required",
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
// Trim simple string arrays
|
|
46
47
|
const constraints = (bodyData.constraints || []).map((c) => trimRequired(c));
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
const skillIndex = trimFields(bodyData.skillIndex, {
|
|
49
|
+
name: "required",
|
|
50
|
+
dirname: "required",
|
|
51
|
+
useWhen: "required",
|
|
52
|
+
});
|
|
53
|
+
const agentIndex = trimFields(bodyData.agentIndex, {
|
|
54
|
+
id: "required",
|
|
55
|
+
name: "required",
|
|
56
|
+
description: "required",
|
|
57
|
+
});
|
|
58
|
+
const workingStyles = trimFields(bodyData.workingStyles, {
|
|
59
|
+
title: "required",
|
|
60
|
+
content: "required",
|
|
61
|
+
});
|
|
54
62
|
|
|
55
63
|
return {
|
|
56
64
|
// Frontmatter
|
|
@@ -66,12 +74,14 @@ function prepareAgentProfileData({ frontmatter, bodyData }) {
|
|
|
66
74
|
priority: trimValue(bodyData.priority),
|
|
67
75
|
skillIndex,
|
|
68
76
|
hasSkills: skillIndex.length > 0,
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
workingStyle: trimValue(bodyData.workingStyle),
|
|
77
|
+
roleContext: trimValue(bodyData.roleContext),
|
|
78
|
+
workingStyles,
|
|
79
|
+
hasWorkingStyles: workingStyles.length > 0,
|
|
73
80
|
beforeHandoff: trimValue(bodyData.beforeHandoff),
|
|
74
81
|
constraints,
|
|
82
|
+
hasConstraints: constraints.length > 0,
|
|
83
|
+
agentIndex,
|
|
84
|
+
hasAgentIndex: agentIndex.length > 0,
|
|
75
85
|
};
|
|
76
86
|
}
|
|
77
87
|
|
|
@@ -90,10 +100,8 @@ function prepareAgentProfileData({ frontmatter, bodyData }) {
|
|
|
90
100
|
* @param {string} profile.bodyData.identity - Core identity text
|
|
91
101
|
* @param {string} [profile.bodyData.priority] - Priority/philosophy statement (optional)
|
|
92
102
|
* @param {Array<{name: string, dirname: string, useWhen: string}>} profile.bodyData.skillIndex - Skill index entries
|
|
93
|
-
* @param {
|
|
94
|
-
* @param {
|
|
95
|
-
* @param {string} profile.bodyData.operationalContext - Operational context text
|
|
96
|
-
* @param {string} profile.bodyData.workingStyle - Working style markdown section
|
|
103
|
+
* @param {string} profile.bodyData.roleContext - Role context text
|
|
104
|
+
* @param {WorkingStyleEntry[]} profile.bodyData.workingStyles - Working style entries
|
|
97
105
|
* @param {string} [profile.bodyData.beforeHandoff] - Before handoff checklist markdown (optional)
|
|
98
106
|
* @param {string[]} profile.bodyData.constraints - List of constraints
|
|
99
107
|
* @param {string} template - Mustache template string
|
|
@@ -40,14 +40,24 @@ function prepareAgentSkillData({
|
|
|
40
40
|
ready: "array",
|
|
41
41
|
});
|
|
42
42
|
|
|
43
|
+
const descriptionLines = splitLines(frontmatter.description);
|
|
44
|
+
const useWhenLines = splitLines(frontmatter.useWhen);
|
|
45
|
+
const trimmedReference = trimValue(reference) || "";
|
|
46
|
+
const tools = toolReferences || [];
|
|
47
|
+
|
|
43
48
|
return {
|
|
44
49
|
name: frontmatter.name,
|
|
45
|
-
descriptionLines
|
|
46
|
-
|
|
50
|
+
descriptionLines,
|
|
51
|
+
hasDescription: descriptionLines.length > 0,
|
|
52
|
+
useWhenLines,
|
|
53
|
+
hasUseWhen: useWhenLines.length > 0,
|
|
47
54
|
title,
|
|
48
55
|
stages: processedStages,
|
|
49
|
-
|
|
50
|
-
|
|
56
|
+
hasStages: processedStages.length > 0,
|
|
57
|
+
reference: trimmedReference,
|
|
58
|
+
hasReference: !!trimmedReference,
|
|
59
|
+
toolReferences: tools,
|
|
60
|
+
hasToolReferences: tools.length > 0,
|
|
51
61
|
};
|
|
52
62
|
}
|
|
53
63
|
|
|
@@ -129,25 +129,38 @@ function prepareJobDescriptionData({ job, discipline, grade, track }) {
|
|
|
129
129
|
grade.typicalExperienceRange || "",
|
|
130
130
|
) || null;
|
|
131
131
|
|
|
132
|
+
const responsibilities = trimFields(job.derivedResponsibilities, {
|
|
133
|
+
responsibility: "required",
|
|
134
|
+
});
|
|
135
|
+
const behaviours = trimFields(sortedBehaviours, {
|
|
136
|
+
maturityDescription: "optional",
|
|
137
|
+
});
|
|
138
|
+
const trimmedTrackRoleContext = trimValue(track?.roleContext);
|
|
139
|
+
const trimmedExpectationsParagraph = trimValue(expectationsParagraph);
|
|
140
|
+
const trimmedQualificationSummary = trimValue(qualificationSummary);
|
|
141
|
+
|
|
132
142
|
return {
|
|
133
143
|
title: job.title,
|
|
134
144
|
gradeId: grade.id,
|
|
135
145
|
typicalExperienceRange: grade.typicalExperienceRange,
|
|
136
146
|
trackName: track?.name || null,
|
|
147
|
+
hasTrack: !!track,
|
|
137
148
|
roleSummary: trimValue(roleSummary),
|
|
138
|
-
trackRoleContext:
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
149
|
+
trackRoleContext: trimmedTrackRoleContext,
|
|
150
|
+
hasTrackRoleContext: !!trimmedTrackRoleContext,
|
|
151
|
+
expectationsParagraph: trimmedExpectationsParagraph,
|
|
152
|
+
hasExpectationsParagraph: !!trimmedExpectationsParagraph,
|
|
153
|
+
responsibilities,
|
|
154
|
+
hasResponsibilities: responsibilities.length > 0,
|
|
155
|
+
behaviours,
|
|
156
|
+
hasBehaviours: behaviours.length > 0,
|
|
146
157
|
skillLevels: skillLevels.map((level) => ({
|
|
147
158
|
...level,
|
|
148
159
|
skills: trimFields(level.skills, { levelDescription: "optional" }),
|
|
149
160
|
})),
|
|
150
|
-
|
|
161
|
+
hasSkillLevels: skillLevels.length > 0,
|
|
162
|
+
qualificationSummary: trimmedQualificationSummary,
|
|
163
|
+
hasQualificationSummary: !!trimmedQualificationSummary,
|
|
151
164
|
};
|
|
152
165
|
}
|
|
153
166
|
|
|
@@ -17,6 +17,7 @@ import { createBehaviourProfile } from "../../components/behaviour-profile.js";
|
|
|
17
17
|
import { createCodeDisplay } from "../../components/code-display.js";
|
|
18
18
|
import { markdownToHtml } from "../../lib/markdown.js";
|
|
19
19
|
import { formatJobDescription } from "./description.js";
|
|
20
|
+
import { createToolkitTable } from "../toolkit/dom.js";
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
23
|
* Format job detail as DOM elements
|
|
@@ -77,14 +78,14 @@ export function jobToDOM(view, options = {}) {
|
|
|
77
78
|
// Radar charts
|
|
78
79
|
div(
|
|
79
80
|
{ className: "section auto-grid-lg" },
|
|
80
|
-
createSkillRadar(view.skillMatrix, {
|
|
81
|
-
title: "Skills Radar",
|
|
82
|
-
size: 420,
|
|
83
|
-
}),
|
|
84
81
|
createBehaviourRadar(view.behaviourProfile, {
|
|
85
82
|
title: "Behaviours Radar",
|
|
86
83
|
size: 420,
|
|
87
84
|
}),
|
|
85
|
+
createSkillRadar(view.skillMatrix, {
|
|
86
|
+
title: "Skills Radar",
|
|
87
|
+
size: 420,
|
|
88
|
+
}),
|
|
88
89
|
),
|
|
89
90
|
|
|
90
91
|
// Job Description HTML (for print view)
|
|
@@ -104,21 +105,29 @@ export function jobToDOM(view, options = {}) {
|
|
|
104
105
|
})
|
|
105
106
|
: null,
|
|
106
107
|
|
|
107
|
-
// Skill matrix,
|
|
108
|
+
// Behaviour profile, Skill matrix, Toolkit, Driver coverage tables
|
|
108
109
|
showTables
|
|
109
110
|
? div(
|
|
110
111
|
{ className: "job-tables-section" },
|
|
111
|
-
createDetailSection({
|
|
112
|
-
title: "Skill Matrix",
|
|
113
|
-
content: createSkillMatrix(view.skillMatrix),
|
|
114
|
-
}),
|
|
115
|
-
|
|
116
112
|
// Behaviour profile table
|
|
117
113
|
createDetailSection({
|
|
118
114
|
title: "Behaviour Profile",
|
|
119
115
|
content: createBehaviourProfile(view.behaviourProfile),
|
|
120
116
|
}),
|
|
121
117
|
|
|
118
|
+
createDetailSection({
|
|
119
|
+
title: "Skill Matrix",
|
|
120
|
+
content: createSkillMatrix(view.skillMatrix),
|
|
121
|
+
}),
|
|
122
|
+
|
|
123
|
+
// Toolkit (after skill matrix)
|
|
124
|
+
view.toolkit && view.toolkit.length > 0
|
|
125
|
+
? createDetailSection({
|
|
126
|
+
title: "Tool Kit",
|
|
127
|
+
content: createToolkitTable(view.toolkit),
|
|
128
|
+
})
|
|
129
|
+
: null,
|
|
130
|
+
|
|
122
131
|
// Driver coverage
|
|
123
132
|
view.driverCoverage.length > 0
|
|
124
133
|
? createDetailSection({
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
import { formatLevel } from "../../lib/render.js";
|
|
11
11
|
import { formatJobDescription } from "./description.js";
|
|
12
12
|
import { SKILL_LEVEL_ORDER } from "@forwardimpact/schema/levels";
|
|
13
|
+
import { toolkitToMarkdown } from "../toolkit/markdown.js";
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Format job detail as markdown
|
|
@@ -33,6 +34,15 @@ export function jobToMarkdown(view, entities = {}, jobTemplate) {
|
|
|
33
34
|
lines.push("");
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
// Behaviour Profile
|
|
38
|
+
lines.push("## Behaviour Profile", "");
|
|
39
|
+
const behaviourRows = view.behaviourProfile.map((b) => [
|
|
40
|
+
b.behaviourName,
|
|
41
|
+
formatLevel(b.maturity),
|
|
42
|
+
]);
|
|
43
|
+
lines.push(tableToMarkdown(["Behaviour", "Maturity"], behaviourRows));
|
|
44
|
+
lines.push("");
|
|
45
|
+
|
|
36
46
|
// Skill Matrix - sorted by level descending
|
|
37
47
|
lines.push("## Skill Matrix", "");
|
|
38
48
|
const sortedSkills = [...view.skillMatrix].sort((a, b) => {
|
|
@@ -50,14 +60,12 @@ export function jobToMarkdown(view, entities = {}, jobTemplate) {
|
|
|
50
60
|
lines.push(tableToMarkdown(["Skill", "Level"], skillRows));
|
|
51
61
|
lines.push("");
|
|
52
62
|
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
lines.push(tableToMarkdown(["Behaviour", "Maturity"], behaviourRows));
|
|
60
|
-
lines.push("");
|
|
63
|
+
// Toolkit
|
|
64
|
+
if (view.toolkit && view.toolkit.length > 0) {
|
|
65
|
+
lines.push("## Tool Kit", "");
|
|
66
|
+
lines.push(toolkitToMarkdown(view.toolkit));
|
|
67
|
+
lines.push("");
|
|
68
|
+
}
|
|
61
69
|
|
|
62
70
|
// Driver Coverage
|
|
63
71
|
if (view.driverCoverage.length > 0) {
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Toolkit formatting for DOM/web output
|
|
3
|
+
*
|
|
4
|
+
* Displays a compact toolkit table showing tools with icons and descriptions.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
div,
|
|
9
|
+
table,
|
|
10
|
+
thead,
|
|
11
|
+
tbody,
|
|
12
|
+
tr,
|
|
13
|
+
th,
|
|
14
|
+
td,
|
|
15
|
+
a,
|
|
16
|
+
span,
|
|
17
|
+
} from "../../lib/render.js";
|
|
18
|
+
import { createToolIcon } from "../../lib/card-mappers.js";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Create a toolkit table for display in job/agent detail pages
|
|
22
|
+
* @param {Array<{name: string, description: string, url?: string, simpleIcon?: string, skillIds: string[]}>} toolkit - Derived toolkit entries
|
|
23
|
+
* @returns {HTMLElement}
|
|
24
|
+
*/
|
|
25
|
+
export function createToolkitTable(toolkit) {
|
|
26
|
+
if (!toolkit || toolkit.length === 0) {
|
|
27
|
+
return div({ className: "empty-state" }, "No tools in toolkit");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const rows = toolkit.map((tool) => {
|
|
31
|
+
const iconCell = tool.simpleIcon
|
|
32
|
+
? td(
|
|
33
|
+
{ className: "tool-icon-cell" },
|
|
34
|
+
createToolIcon(tool.simpleIcon, tool.name),
|
|
35
|
+
)
|
|
36
|
+
: td({ className: "tool-icon-cell" });
|
|
37
|
+
|
|
38
|
+
const nameContent = tool.url
|
|
39
|
+
? a(
|
|
40
|
+
{
|
|
41
|
+
href: tool.url,
|
|
42
|
+
target: "_blank",
|
|
43
|
+
rel: "noopener noreferrer",
|
|
44
|
+
className: "tool-link",
|
|
45
|
+
},
|
|
46
|
+
tool.name,
|
|
47
|
+
span({ className: "external-icon" }, " ↗"),
|
|
48
|
+
)
|
|
49
|
+
: span({}, tool.name);
|
|
50
|
+
|
|
51
|
+
return tr(
|
|
52
|
+
{},
|
|
53
|
+
iconCell,
|
|
54
|
+
td({ className: "tool-name-cell" }, nameContent),
|
|
55
|
+
td({ className: "tool-description-cell" }, tool.description),
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
return div(
|
|
60
|
+
{ className: "table-container" },
|
|
61
|
+
table(
|
|
62
|
+
{ className: "table toolkit-table" },
|
|
63
|
+
thead(
|
|
64
|
+
{},
|
|
65
|
+
tr(
|
|
66
|
+
{},
|
|
67
|
+
th({ style: "width: 40px" }, ""),
|
|
68
|
+
th({}, "Tool"),
|
|
69
|
+
th({}, "Description"),
|
|
70
|
+
),
|
|
71
|
+
),
|
|
72
|
+
tbody({}, ...rows),
|
|
73
|
+
),
|
|
74
|
+
);
|
|
75
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Toolkit formatting for markdown/CLI output
|
|
3
|
+
*
|
|
4
|
+
* Displays toolkit as a markdown table with tools, icons, and descriptions.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { tableToMarkdown } from "../shared.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Format toolkit as markdown table
|
|
11
|
+
* @param {Array<{name: string, description: string, url?: string, simpleIcon?: string, skillIds: string[]}>} toolkit - Derived toolkit entries
|
|
12
|
+
* @returns {string}
|
|
13
|
+
*/
|
|
14
|
+
export function toolkitToMarkdown(toolkit) {
|
|
15
|
+
if (!toolkit || toolkit.length === 0) {
|
|
16
|
+
return "";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const rows = toolkit.map((tool) => {
|
|
20
|
+
const name = tool.url ? `[${tool.name}](${tool.url})` : tool.name;
|
|
21
|
+
return [name, tool.description];
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
return tableToMarkdown(["Tool", "Description"], rows);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Format toolkit as a plain list of tool names (for --tools flag)
|
|
29
|
+
* @param {Array<{name: string}>} toolkit - Derived toolkit entries
|
|
30
|
+
* @returns {string}
|
|
31
|
+
*/
|
|
32
|
+
export function toolkitToPlainList(toolkit) {
|
|
33
|
+
if (!toolkit || toolkit.length === 0) {
|
|
34
|
+
return "";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return toolkit.map((tool) => tool.name).join("\n");
|
|
38
|
+
}
|
|
@@ -27,7 +27,9 @@ import {
|
|
|
27
27
|
generateSkillMd,
|
|
28
28
|
deriveAgentSkills,
|
|
29
29
|
deriveReferenceGrade,
|
|
30
|
-
|
|
30
|
+
deriveToolkit,
|
|
31
|
+
buildAgentIndex,
|
|
32
|
+
} from "@forwardimpact/model";
|
|
31
33
|
import {
|
|
32
34
|
createSelectWithValue,
|
|
33
35
|
createDisciplineSelect,
|
|
@@ -37,6 +39,8 @@ import { getStageEmoji } from "../formatters/stage/shared.js";
|
|
|
37
39
|
import { formatAgentProfile } from "../formatters/agent/profile.js";
|
|
38
40
|
import { formatAgentSkill } from "../formatters/agent/skill.js";
|
|
39
41
|
import { createCodeDisplay } from "../components/code-display.js";
|
|
42
|
+
import { createToolkitTable } from "../formatters/toolkit/dom.js";
|
|
43
|
+
import { createDetailSection } from "../components/detail.js";
|
|
40
44
|
|
|
41
45
|
/** All stages option value */
|
|
42
46
|
const ALL_STAGES_VALUE = "all";
|
|
@@ -247,6 +251,15 @@ export async function renderAgentBuilder() {
|
|
|
247
251
|
// Get reference grade for derivation
|
|
248
252
|
const grade = deriveReferenceGrade(data.grades);
|
|
249
253
|
|
|
254
|
+
// Build agent index for all valid combinations
|
|
255
|
+
const agentIndex = buildAgentIndex({
|
|
256
|
+
disciplines: data.disciplines,
|
|
257
|
+
tracks: data.tracks,
|
|
258
|
+
stages,
|
|
259
|
+
agentDisciplines: agentData.disciplines,
|
|
260
|
+
agentTracks: agentData.tracks,
|
|
261
|
+
});
|
|
262
|
+
|
|
250
263
|
// Build context for generation
|
|
251
264
|
const context = {
|
|
252
265
|
humanDiscipline,
|
|
@@ -262,6 +275,7 @@ export async function renderAgentBuilder() {
|
|
|
262
275
|
vscodeSettings: agentData.vscodeSettings,
|
|
263
276
|
devcontainer: agentData.devcontainer,
|
|
264
277
|
templates,
|
|
278
|
+
agentIndex,
|
|
265
279
|
};
|
|
266
280
|
|
|
267
281
|
// Generate preview based on stage selection
|
|
@@ -439,6 +453,7 @@ function createAllStagesPreview(context) {
|
|
|
439
453
|
vscodeSettings,
|
|
440
454
|
devcontainer,
|
|
441
455
|
templates,
|
|
456
|
+
agentIndex,
|
|
442
457
|
} = context;
|
|
443
458
|
|
|
444
459
|
// Generate all stage agents
|
|
@@ -469,6 +484,7 @@ function createAllStagesPreview(context) {
|
|
|
469
484
|
agentTrack,
|
|
470
485
|
capabilities,
|
|
471
486
|
stages,
|
|
487
|
+
agentIndex,
|
|
472
488
|
});
|
|
473
489
|
|
|
474
490
|
return { stage, derived, profile };
|
|
@@ -488,6 +504,12 @@ function createAllStagesPreview(context) {
|
|
|
488
504
|
.filter((skill) => skill?.agent)
|
|
489
505
|
.map((skill) => generateSkillMd(skill, stages));
|
|
490
506
|
|
|
507
|
+
// Derive toolkit from agent skills
|
|
508
|
+
const toolkit = deriveToolkit({
|
|
509
|
+
skillMatrix: derivedSkills,
|
|
510
|
+
skills,
|
|
511
|
+
});
|
|
512
|
+
|
|
491
513
|
return div(
|
|
492
514
|
{ className: "agent-deployment" },
|
|
493
515
|
|
|
@@ -533,6 +555,14 @@ function createAllStagesPreview(context) {
|
|
|
533
555
|
),
|
|
534
556
|
),
|
|
535
557
|
|
|
558
|
+
// Tool Kit section
|
|
559
|
+
toolkit.length > 0
|
|
560
|
+
? createDetailSection({
|
|
561
|
+
title: `Tool Kit (${toolkit.length})`,
|
|
562
|
+
content: createToolkitTable(toolkit),
|
|
563
|
+
})
|
|
564
|
+
: null,
|
|
565
|
+
|
|
536
566
|
// CLI hint
|
|
537
567
|
createCliHint(humanDiscipline.id, humanTrack.id),
|
|
538
568
|
);
|
|
@@ -559,6 +589,7 @@ function createSingleStagePreview(context, stage) {
|
|
|
559
589
|
devcontainer,
|
|
560
590
|
stages,
|
|
561
591
|
templates,
|
|
592
|
+
agentIndex,
|
|
562
593
|
} = context;
|
|
563
594
|
|
|
564
595
|
// Derive stage agent
|
|
@@ -588,6 +619,7 @@ function createSingleStagePreview(context, stage) {
|
|
|
588
619
|
agentTrack,
|
|
589
620
|
capabilities,
|
|
590
621
|
stages,
|
|
622
|
+
agentIndex,
|
|
591
623
|
});
|
|
592
624
|
|
|
593
625
|
// Get skills for this stage (using full derived skills)
|
|
@@ -603,6 +635,12 @@ function createSingleStagePreview(context, stage) {
|
|
|
603
635
|
.filter((skill) => skill?.agent)
|
|
604
636
|
.map((skill) => generateSkillMd(skill, stages));
|
|
605
637
|
|
|
638
|
+
// Derive toolkit from agent skills
|
|
639
|
+
const toolkit = deriveToolkit({
|
|
640
|
+
skillMatrix: derivedSkills,
|
|
641
|
+
skills,
|
|
642
|
+
});
|
|
643
|
+
|
|
606
644
|
return div(
|
|
607
645
|
{ className: "agent-deployment" },
|
|
608
646
|
|
|
@@ -642,6 +680,14 @@ function createSingleStagePreview(context, stage) {
|
|
|
642
680
|
),
|
|
643
681
|
),
|
|
644
682
|
|
|
683
|
+
// Tool Kit section
|
|
684
|
+
toolkit.length > 0
|
|
685
|
+
? createDetailSection({
|
|
686
|
+
title: `Tool Kit (${toolkit.length})`,
|
|
687
|
+
content: createToolkitTable(toolkit),
|
|
688
|
+
})
|
|
689
|
+
: null,
|
|
690
|
+
|
|
645
691
|
// CLI hint
|
|
646
692
|
createCliHint(humanDiscipline.id, humanTrack.id, stage.id),
|
|
647
693
|
);
|
|
@@ -28,10 +28,27 @@ handoffs:
|
|
|
28
28
|
## Core Identity
|
|
29
29
|
|
|
30
30
|
{{{identity}}}
|
|
31
|
+
{{#priority}}
|
|
31
32
|
|
|
32
|
-
{{
|
|
33
|
+
{{{priority}}}
|
|
34
|
+
{{/priority}}
|
|
35
|
+
{{#roleContext}}
|
|
33
36
|
|
|
34
|
-
|
|
37
|
+
## Role Context
|
|
38
|
+
|
|
39
|
+
{{{roleContext}}}
|
|
40
|
+
{{/roleContext}}
|
|
41
|
+
{{#hasWorkingStyles}}
|
|
42
|
+
|
|
43
|
+
## Working Style
|
|
44
|
+
{{#workingStyles}}
|
|
45
|
+
|
|
46
|
+
### {{title}}
|
|
47
|
+
|
|
48
|
+
{{{content}}}
|
|
49
|
+
{{/workingStyles}}
|
|
50
|
+
{{/hasWorkingStyles}}
|
|
51
|
+
{{#hasSkills}}
|
|
35
52
|
|
|
36
53
|
## Available Skills and Tools
|
|
37
54
|
|
|
@@ -47,26 +64,30 @@ and (3) trade-offs of the alternative.
|
|
|
47
64
|
|
|
48
65
|
| Skill | Location | Use When |
|
|
49
66
|
| ----- | -------- | -------- |
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
{{
|
|
53
|
-
|
|
54
|
-
{{
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
{{
|
|
67
|
+
{{#skillIndex}}
|
|
68
|
+
| {{{name}}} | `.claude/skills/{{dirname}}/SKILL.md` | {{{useWhen}}} |
|
|
69
|
+
{{/skillIndex}}
|
|
70
|
+
{{/hasSkills}}
|
|
71
|
+
{{#hasAgentIndex}}
|
|
72
|
+
|
|
73
|
+
## Available Sub-Agents for Delegation
|
|
74
|
+
|
|
75
|
+
**IMPORTANT:** If you come across work that is not strictly within your
|
|
76
|
+
speciality, then you must delegate the task using the `runSubagent` tool.
|
|
77
|
+
|
|
78
|
+
You are part of a team of agents and should not carry out all work yourself.
|
|
79
|
+
Rely on other agents around you that have a speciality better suited for
|
|
80
|
+
individual tasks. If you choose to not delegate specialised work to a sub-agent,
|
|
81
|
+
document in your output: (1) what the specialised work is, (2) the constraint
|
|
82
|
+
preventing delegation and (3) trade-offs of the alternative.
|
|
83
|
+
|
|
84
|
+
| Agent Name | Speciality | Description |
|
|
85
|
+
| ---------- | ---------- | ----------- |
|
|
86
|
+
{{#agentIndex}}
|
|
87
|
+
| `{{id}}` | {{{name}}} | {{{description}}} |
|
|
88
|
+
{{/agentIndex}}
|
|
89
|
+
{{/hasAgentIndex}}
|
|
90
|
+
{{#beforeHandoff}}
|
|
70
91
|
|
|
71
92
|
## Before Handoff
|
|
72
93
|
|
|
@@ -76,7 +97,6 @@ Before offering a handoff, verify and summarize completion of these items:
|
|
|
76
97
|
|
|
77
98
|
When verified, summarize what was accomplished then offer the handoff. If items
|
|
78
99
|
are incomplete, explain what remains.
|
|
79
|
-
|
|
80
100
|
{{/beforeHandoff}}
|
|
81
101
|
|
|
82
102
|
## Return Format
|
|
@@ -87,10 +107,10 @@ When completing work (for handoff or as a subagent), provide:
|
|
|
87
107
|
2. **Checklist status**: Items verified from Before Handoff section
|
|
88
108
|
3. **Recommendation**: Ready for next stage, or needs more work
|
|
89
109
|
|
|
90
|
-
{{#
|
|
91
|
-
|
|
110
|
+
{{#hasConstraints}}
|
|
92
111
|
## Constraints
|
|
93
112
|
|
|
94
113
|
{{#constraints}}
|
|
95
|
-
|
|
96
|
-
|
|
114
|
+
- {{{.}}}
|
|
115
|
+
{{/constraints}}
|
|
116
|
+
{{/hasConstraints}}
|
|
@@ -1,41 +1,50 @@
|
|
|
1
1
|
# {{{title}}}
|
|
2
2
|
|
|
3
3
|
- **Level:** {{{gradeId}}}
|
|
4
|
-
- **Experience:** {{{typicalExperienceRange}}}
|
|
5
|
-
|
|
4
|
+
- **Experience:** {{{typicalExperienceRange}}}
|
|
5
|
+
{{#hasTrack}}- **Track:** {{{trackName}}}
|
|
6
|
+
{{/hasTrack}}
|
|
6
7
|
|
|
7
8
|
## ROLE SUMMARY
|
|
8
9
|
|
|
9
10
|
{{{roleSummary}}}
|
|
11
|
+
{{#hasTrackRoleContext}}
|
|
10
12
|
|
|
11
|
-
{{
|
|
13
|
+
{{{trackRoleContext}}}
|
|
14
|
+
{{/hasTrackRoleContext}}
|
|
15
|
+
{{#hasExpectationsParagraph}}
|
|
12
16
|
|
|
13
|
-
{{
|
|
14
|
-
|
|
15
|
-
{{
|
|
17
|
+
{{{expectationsParagraph}}}
|
|
18
|
+
{{/hasExpectationsParagraph}}
|
|
19
|
+
{{#hasResponsibilities}}
|
|
16
20
|
|
|
17
21
|
## ROLE RESPONSIBILITIES
|
|
18
22
|
|
|
19
23
|
{{#responsibilities}}
|
|
20
|
-
|
|
21
|
-
|
|
24
|
+
- **{{{capabilityName}}}:** {{{responsibility}}}
|
|
25
|
+
{{/responsibilities}}
|
|
26
|
+
{{/hasResponsibilities}}
|
|
27
|
+
{{#hasBehaviours}}
|
|
22
28
|
|
|
23
29
|
## ROLE BEHAVIOURS
|
|
24
30
|
|
|
25
31
|
{{#behaviours}}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
- **{{{behaviourName}}}:** {{{maturityDescription}}}
|
|
33
|
+
{{/behaviours}}
|
|
34
|
+
{{/hasBehaviours}}
|
|
35
|
+
{{#hasSkillLevels}}
|
|
29
36
|
{{#skillLevels}}
|
|
30
37
|
|
|
31
38
|
## {{{levelHeading}}}
|
|
32
39
|
|
|
33
40
|
{{#skills}}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
41
|
+
- **{{{skillName}}}:** {{{levelDescription}}}
|
|
42
|
+
{{/skills}}
|
|
37
43
|
{{/skillLevels}}
|
|
44
|
+
{{/hasSkillLevels}}
|
|
45
|
+
{{#hasQualificationSummary}}
|
|
38
46
|
|
|
39
47
|
## QUALIFICATIONS
|
|
40
48
|
|
|
41
|
-
{{
|
|
49
|
+
{{{qualificationSummary}}}
|
|
50
|
+
{{/hasQualificationSummary}}
|
|
@@ -4,41 +4,50 @@ description: |
|
|
|
4
4
|
{{#descriptionLines}}
|
|
5
5
|
{{{.}}}
|
|
6
6
|
{{/descriptionLines}}
|
|
7
|
-
{{#
|
|
7
|
+
{{#hasUseWhen}}
|
|
8
8
|
**Use When:** {{#useWhenLines}}{{{.}}}{{/useWhenLines}}
|
|
9
|
-
{{/
|
|
9
|
+
{{/hasUseWhen}}
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
# {{{title}}}
|
|
13
|
+
{{#hasUseWhen}}
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
{{#useWhenLines}}{{{.}}}{{/useWhenLines}}
|
|
15
|
+
**Use This Skill When:**
|
|
16
|
+
{{#useWhenLines}}{{{.}}}{{/useWhenLines}}
|
|
17
|
+
{{/hasUseWhen}}
|
|
18
|
+
{{#hasStages}}
|
|
16
19
|
|
|
17
20
|
## Stage Guidance
|
|
18
|
-
|
|
19
21
|
{{#stages}}
|
|
20
22
|
|
|
21
23
|
### {{stageName}} Stage
|
|
22
24
|
|
|
23
25
|
**Focus:** {{{focus}}}
|
|
24
26
|
|
|
25
|
-
**Activities:**
|
|
26
|
-
|
|
27
|
-
- {{{.}}}
|
|
27
|
+
**Activities:**
|
|
28
|
+
{{#activities}}
|
|
29
|
+
- {{{.}}}
|
|
30
|
+
{{/activities}}
|
|
28
31
|
|
|
29
|
-
**Ready for {{nextStageName}} when:**
|
|
32
|
+
**Ready for {{nextStageName}} when:**
|
|
33
|
+
{{#ready}}
|
|
34
|
+
- [ ] {{{.}}}
|
|
35
|
+
{{/ready}}
|
|
36
|
+
{{/stages}}
|
|
37
|
+
{{/hasStages}}
|
|
38
|
+
{{#hasToolReferences}}
|
|
30
39
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
## Recommended Tools
|
|
40
|
+
# Recommended Tools
|
|
34
41
|
|
|
35
42
|
| Tool | Use When |
|
|
36
43
|
| ---- | -------- |
|
|
44
|
+
{{#toolReferences}}
|
|
45
|
+
| {{#url}}[{{{name}}}]({{{url}}}){{/url}}{{^url}}{{{name}}}{{/url}} | {{{useWhen}}} |
|
|
46
|
+
{{/toolReferences}}
|
|
47
|
+
{{/hasToolReferences}}
|
|
48
|
+
{{#hasReference}}
|
|
37
49
|
|
|
38
|
-
|
|
39
|
-
{{#url}}[{{{name}}}]({{{url}}}){{/url}}{{^url}}{{{name}}}{{/url}} |
|
|
40
|
-
{{{useWhen}}} | {{/toolReferences}} {{/toolReferences.length}} {{#reference}}
|
|
41
|
-
|
|
42
|
-
## Reference
|
|
50
|
+
# Reference
|
|
43
51
|
|
|
44
|
-
{{{reference}}}
|
|
52
|
+
{{{reference}}}
|
|
53
|
+
{{/hasReference}}
|