@forwardimpact/pathway 0.21.0 → 0.23.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/bin/fit-pathway.js +22 -22
- package/package.json +4 -3
- package/src/commands/agent.js +14 -10
- package/src/commands/behaviour.js +11 -1
- package/src/commands/build.js +11 -2
- package/src/commands/command-factory.js +4 -2
- package/src/commands/dev.js +9 -2
- package/src/commands/discipline.js +19 -2
- package/src/commands/driver.js +11 -1
- package/src/commands/index.js +1 -1
- package/src/commands/init.js +1 -1
- package/src/commands/interview.js +8 -8
- package/src/commands/job.js +41 -28
- package/src/commands/level.js +76 -0
- package/src/commands/progress.js +20 -20
- package/src/commands/questions.js +3 -3
- package/src/commands/skill.js +11 -1
- package/src/commands/stage.js +11 -1
- package/src/commands/tool.js +4 -3
- package/src/commands/track.js +11 -1
- package/src/components/action-buttons.js +3 -3
- package/src/components/builder.js +25 -25
- package/src/components/card.js +8 -104
- package/src/components/comparison-radar.js +4 -4
- package/src/components/detail.js +18 -120
- package/src/components/error-page.js +8 -68
- package/src/components/grid.js +12 -106
- package/src/components/list.js +7 -116
- package/src/components/nav.js +7 -60
- package/src/components/radar-chart.js +3 -3
- package/src/components/skill-matrix.js +7 -7
- package/src/css/bundles/app.css +25 -21
- package/src/css/bundles/handout.css +33 -33
- package/src/css/bundles/slides.css +25 -25
- package/src/css/pages/landing.css +5 -5
- package/src/formatters/index.js +5 -5
- package/src/formatters/interview/shared.js +23 -23
- package/src/formatters/job/description.js +18 -18
- package/src/formatters/job/dom.js +12 -12
- package/src/formatters/job/markdown.js +7 -7
- package/src/formatters/json-ld.js +24 -24
- package/src/formatters/{grade → level}/dom.js +31 -27
- package/src/formatters/{grade → level}/markdown.js +19 -28
- package/src/formatters/{grade → level}/microdata.js +28 -38
- package/src/formatters/level/shared.js +86 -0
- package/src/formatters/progress/markdown.js +2 -2
- package/src/formatters/progress/shared.js +51 -51
- package/src/formatters/questions/markdown.js +8 -6
- package/src/formatters/questions/shared.js +7 -7
- package/src/formatters/skill/dom.js +4 -4
- package/src/formatters/skill/markdown.js +1 -1
- package/src/formatters/skill/microdata.js +3 -3
- package/src/formatters/skill/shared.js +3 -3
- package/src/formatters/track/shared.js +1 -1
- package/src/handout-main.js +12 -12
- package/src/handout.html +32 -13
- package/src/index.html +33 -14
- package/src/lib/card-mappers.js +16 -16
- package/src/lib/cli-command.js +3 -3
- package/src/lib/cli-output.js +2 -2
- package/src/lib/error-boundary.js +3 -66
- package/src/lib/errors.js +7 -45
- package/src/lib/job-cache.js +12 -12
- package/src/lib/markdown.js +2 -109
- package/src/lib/reactive.js +7 -73
- package/src/lib/render.js +53 -201
- package/src/lib/router-core.js +2 -156
- package/src/lib/router-pages.js +2 -11
- package/src/lib/router-slides.js +2 -197
- package/src/lib/state.js +16 -65
- package/src/lib/utils.js +3 -10
- package/src/lib/yaml-loader.js +22 -80
- package/src/main.js +10 -10
- package/src/pages/agent-builder.js +12 -12
- package/src/pages/assessment-results.js +28 -24
- package/src/pages/interview-builder.js +6 -6
- package/src/pages/interview.js +8 -8
- package/src/pages/job-builder.js +7 -7
- package/src/pages/job.js +8 -8
- package/src/pages/landing.js +8 -8
- package/src/pages/level.js +122 -0
- package/src/pages/progress-builder.js +8 -8
- package/src/pages/progress.js +74 -74
- package/src/pages/self-assessment.js +7 -7
- package/src/pages/skill.js +1 -1
- package/src/slide-main.js +23 -23
- package/src/slides/chapter.js +4 -4
- package/src/slides/index.js +11 -11
- package/src/slides/interview.js +2 -2
- package/src/slides/job.js +4 -4
- package/src/slides/level.js +32 -0
- package/src/slides/overview.js +10 -10
- package/src/slides/progress.js +13 -13
- package/src/slides.html +32 -13
- package/src/types.js +1 -1
- package/templates/job.template.md +2 -2
- package/src/commands/grade.js +0 -60
- package/src/css/base.css +0 -56
- package/src/css/components/badges.css +0 -232
- package/src/css/components/buttons.css +0 -101
- package/src/css/components/forms.css +0 -191
- package/src/css/components/layout.css +0 -218
- package/src/css/components/nav.css +0 -206
- package/src/css/components/progress.css +0 -166
- package/src/css/components/states.css +0 -82
- package/src/css/components/surfaces.css +0 -347
- package/src/css/components/tables.css +0 -362
- package/src/css/components/top-bar.css +0 -180
- package/src/css/components/typography.css +0 -121
- package/src/css/components/utilities.css +0 -41
- package/src/css/pages/detail.css +0 -119
- package/src/css/reset.css +0 -50
- package/src/css/tokens.css +0 -162
- package/src/css/views/handout.css +0 -30
- package/src/css/views/print.css +0 -634
- package/src/css/views/slide-animations.css +0 -113
- package/src/css/views/slide-base.css +0 -331
- package/src/css/views/slide-sections.css +0 -597
- package/src/css/views/slide-tables.css +0 -275
- package/src/formatters/grade/shared.js +0 -86
- package/src/pages/grade.js +0 -122
- package/src/slides/grade.js +0 -32
package/README.md
CHANGED
|
@@ -52,9 +52,9 @@ Use `--help` with any command for full options.
|
|
|
52
52
|
|
|
53
53
|
## Web App Features
|
|
54
54
|
|
|
55
|
-
- **Job Builder** — Select discipline, track, and
|
|
56
|
-
- **Skill Browser** — View all skills with
|
|
57
|
-
- **Career Progression** — Compare
|
|
55
|
+
- **Job Builder** — Select discipline, track, and level to explore roles
|
|
56
|
+
- **Skill Browser** — View all skills with proficiency descriptions
|
|
57
|
+
- **Career Progression** — Compare levels and identify growth areas
|
|
58
58
|
- **Interview Prep** — Generate role-specific question sets
|
|
59
59
|
- **Agent Preview** — Preview generated agent profiles
|
|
60
60
|
|
package/bin/fit-pathway.js
CHANGED
|
@@ -10,16 +10,16 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Commands:
|
|
12
12
|
* discipline [<id>] Show disciplines
|
|
13
|
-
*
|
|
13
|
+
* level [<id>] Show levels
|
|
14
14
|
* track [<id>] Show tracks
|
|
15
15
|
* behaviour [<id>] Show behaviours
|
|
16
16
|
* skill [<id>] Show skills (summary, --list, or detail)
|
|
17
17
|
* driver [<id>] Show drivers
|
|
18
18
|
* stage [<id>] Show stages
|
|
19
19
|
* tool [<name>] Show tools
|
|
20
|
-
* job [<discipline> <
|
|
21
|
-
* interview <discipline> <
|
|
22
|
-
* progress <discipline> <
|
|
20
|
+
* job [<discipline> <level>] [--track=TRACK] Generate job definition
|
|
21
|
+
* interview <discipline> <level> [--track=TRACK] [--type=mission|decomposition|stakeholder] Generate interview
|
|
22
|
+
* progress <discipline> <level> [--track=TRACK] [--compare=LEVEL] Career progression
|
|
23
23
|
* questions [options] Browse interview questions
|
|
24
24
|
* agent [<discipline> <track>] [--output=PATH] Generate AI agent
|
|
25
25
|
*
|
|
@@ -37,7 +37,7 @@ import { formatError } from "../src/lib/cli-output.js";
|
|
|
37
37
|
|
|
38
38
|
// Import command handlers
|
|
39
39
|
import { runDisciplineCommand } from "../src/commands/discipline.js";
|
|
40
|
-
import {
|
|
40
|
+
import { runLevelCommand } from "../src/commands/level.js";
|
|
41
41
|
import { runTrackCommand } from "../src/commands/track.js";
|
|
42
42
|
import { runBehaviourCommand } from "../src/commands/behaviour.js";
|
|
43
43
|
import { runSkillCommand } from "../src/commands/skill.js";
|
|
@@ -56,7 +56,7 @@ import { runUpdateCommand } from "../src/commands/update.js";
|
|
|
56
56
|
|
|
57
57
|
const COMMANDS = {
|
|
58
58
|
discipline: runDisciplineCommand,
|
|
59
|
-
|
|
59
|
+
level: runLevelCommand,
|
|
60
60
|
track: runTrackCommand,
|
|
61
61
|
behaviour: runBehaviourCommand,
|
|
62
62
|
skill: runSkillCommand,
|
|
@@ -98,7 +98,7 @@ ENTITY COMMANDS
|
|
|
98
98
|
All entity commands support: summary (default), --list (IDs for piping), <id> (detail)
|
|
99
99
|
|
|
100
100
|
discipline [<id>] Browse engineering disciplines
|
|
101
|
-
|
|
101
|
+
level [<id>] Browse career levels
|
|
102
102
|
track [<id>] Browse track specializations
|
|
103
103
|
behaviour [<id>] Browse professional behaviours
|
|
104
104
|
driver [<id>] Browse outcome drivers
|
|
@@ -113,16 +113,16 @@ All entity commands support: summary (default), --list (IDs for piping), <id> (d
|
|
|
113
113
|
JOB COMMAND
|
|
114
114
|
────────────────────────────────────────────────────────────────────────────────
|
|
115
115
|
|
|
116
|
-
Generate job definitions from discipline ×
|
|
116
|
+
Generate job definitions from discipline × level × track combinations.
|
|
117
117
|
|
|
118
118
|
Usage:
|
|
119
119
|
npx fit-pathway job Summary with stats
|
|
120
120
|
npx fit-pathway job --list All valid combinations
|
|
121
|
-
npx fit-pathway job <discipline> <
|
|
122
|
-
npx fit-pathway job <d> <
|
|
123
|
-
npx fit-pathway job <d> <
|
|
124
|
-
npx fit-pathway job <d> <
|
|
125
|
-
npx fit-pathway job <d> <
|
|
121
|
+
npx fit-pathway job <discipline> <level> Detail view (trackless)
|
|
122
|
+
npx fit-pathway job <d> <l> --track=<track> Detail view (with track)
|
|
123
|
+
npx fit-pathway job <d> <l> --skills Plain list of skill IDs
|
|
124
|
+
npx fit-pathway job <d> <l> --tools Plain list of tool names
|
|
125
|
+
npx fit-pathway job <d> <l> --checklist=<stage> Show handoff checklist
|
|
126
126
|
|
|
127
127
|
Options:
|
|
128
128
|
--track=TRACK Track specialization (e.g., platform, forward_deployed)
|
|
@@ -170,9 +170,9 @@ INTERVIEW COMMAND
|
|
|
170
170
|
Generate interview question sets based on job requirements.
|
|
171
171
|
|
|
172
172
|
Usage:
|
|
173
|
-
npx fit-pathway interview <discipline> <
|
|
174
|
-
npx fit-pathway interview <d> <
|
|
175
|
-
npx fit-pathway interview <d> <
|
|
173
|
+
npx fit-pathway interview <discipline> <level> All types
|
|
174
|
+
npx fit-pathway interview <d> <l> --track=<track> With track
|
|
175
|
+
npx fit-pathway interview <d> <l> --track=<t> --type=<type> Single type
|
|
176
176
|
|
|
177
177
|
Options:
|
|
178
178
|
--track=TRACK Track specialization
|
|
@@ -183,16 +183,16 @@ Options:
|
|
|
183
183
|
PROGRESS COMMAND
|
|
184
184
|
────────────────────────────────────────────────────────────────────────────────
|
|
185
185
|
|
|
186
|
-
Analyze career progression between
|
|
186
|
+
Analyze career progression between levels.
|
|
187
187
|
|
|
188
188
|
Usage:
|
|
189
|
-
npx fit-pathway progress <discipline> <
|
|
190
|
-
npx fit-pathway progress <d> <
|
|
191
|
-
npx fit-pathway progress <d> <
|
|
189
|
+
npx fit-pathway progress <discipline> <level>
|
|
190
|
+
npx fit-pathway progress <d> <l> --track=<track>
|
|
191
|
+
npx fit-pathway progress <d> <l> --compare=<to_level>
|
|
192
192
|
|
|
193
193
|
Options:
|
|
194
194
|
--track=TRACK Track specialization
|
|
195
|
-
--compare=
|
|
195
|
+
--compare=LEVEL Compare to specific level
|
|
196
196
|
|
|
197
197
|
────────────────────────────────────────────────────────────────────────────────
|
|
198
198
|
QUESTIONS COMMAND
|
|
@@ -207,7 +207,7 @@ Usage:
|
|
|
207
207
|
npx fit-pathway questions --stats
|
|
208
208
|
|
|
209
209
|
Options:
|
|
210
|
-
--level=LEVEL Filter by skill
|
|
210
|
+
--level=LEVEL Filter by skill proficiency
|
|
211
211
|
--maturity=MATURITY Filter by behaviour maturity
|
|
212
212
|
--skill=ID Filter to specific skill
|
|
213
213
|
--behaviour=ID Filter to specific behaviour
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forwardimpact/pathway",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.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,9 @@
|
|
|
40
40
|
"./commands": "./src/commands/index.js"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@forwardimpact/map": "^0.
|
|
44
|
-
"@forwardimpact/
|
|
43
|
+
"@forwardimpact/map": "^0.11.0",
|
|
44
|
+
"@forwardimpact/libskill": "^3.0.0",
|
|
45
|
+
"@forwardimpact/libui": "^1.0.0",
|
|
45
46
|
"mustache": "^4.2.0",
|
|
46
47
|
"simple-icons": "^16.7.0",
|
|
47
48
|
"yaml": "^2.3.4"
|
package/src/commands/agent.js
CHANGED
|
@@ -35,14 +35,14 @@ import {
|
|
|
35
35
|
generateStageAgentProfile,
|
|
36
36
|
validateAgentProfile,
|
|
37
37
|
validateAgentSkill,
|
|
38
|
-
|
|
38
|
+
deriveReferenceLevel,
|
|
39
39
|
deriveAgentSkills,
|
|
40
40
|
generateSkillMarkdown,
|
|
41
41
|
deriveToolkit,
|
|
42
42
|
buildAgentIndex,
|
|
43
43
|
getDisciplineAbbreviation,
|
|
44
44
|
toKebabCase,
|
|
45
|
-
} from "@forwardimpact/
|
|
45
|
+
} from "@forwardimpact/libskill";
|
|
46
46
|
import { formatAgentProfile } from "../formatters/agent/profile.js";
|
|
47
47
|
import {
|
|
48
48
|
formatAgentSkill,
|
|
@@ -199,7 +199,7 @@ function showAgentSummary(data, agentData, skillsWithAgent) {
|
|
|
199
199
|
*/
|
|
200
200
|
function listAgentCombinations(data, agentData, verbose = false) {
|
|
201
201
|
if (!verbose) {
|
|
202
|
-
//
|
|
202
|
+
// Descriptive output for piping and AI agent discovery
|
|
203
203
|
for (const discipline of agentData.disciplines) {
|
|
204
204
|
for (const track of agentData.tracks) {
|
|
205
205
|
const humanDiscipline = data.disciplines.find(
|
|
@@ -209,7 +209,11 @@ function listAgentCombinations(data, agentData, verbose = false) {
|
|
|
209
209
|
if (humanDiscipline && humanTrack) {
|
|
210
210
|
const abbrev = getDisciplineAbbreviation(discipline.id);
|
|
211
211
|
const agentName = `${abbrev}-${toKebabCase(track.id)}`;
|
|
212
|
-
|
|
212
|
+
const specName =
|
|
213
|
+
humanDiscipline.specialization || humanDiscipline.id;
|
|
214
|
+
console.log(
|
|
215
|
+
`${agentName} ${discipline.id} ${track.id}, ${specName} (${humanTrack.name})`,
|
|
216
|
+
);
|
|
213
217
|
}
|
|
214
218
|
}
|
|
215
219
|
}
|
|
@@ -414,15 +418,15 @@ export async function runAgentCommand({ data, args, options, dataDir }) {
|
|
|
414
418
|
process.exit(1);
|
|
415
419
|
}
|
|
416
420
|
|
|
417
|
-
// Get reference
|
|
418
|
-
const
|
|
421
|
+
// Get reference level for derivation
|
|
422
|
+
const level = deriveReferenceLevel(data.levels);
|
|
419
423
|
|
|
420
424
|
// --skills: Output plain list of skill IDs (for piping)
|
|
421
425
|
if (options.skills) {
|
|
422
426
|
const derivedSkills = deriveAgentSkills({
|
|
423
427
|
discipline: humanDiscipline,
|
|
424
428
|
track: humanTrack,
|
|
425
|
-
|
|
429
|
+
level,
|
|
426
430
|
skills: skillsWithAgent,
|
|
427
431
|
});
|
|
428
432
|
for (const skill of derivedSkills) {
|
|
@@ -436,7 +440,7 @@ export async function runAgentCommand({ data, args, options, dataDir }) {
|
|
|
436
440
|
const derivedSkills = deriveAgentSkills({
|
|
437
441
|
discipline: humanDiscipline,
|
|
438
442
|
track: humanTrack,
|
|
439
|
-
|
|
443
|
+
level,
|
|
440
444
|
skills: skillsWithAgent,
|
|
441
445
|
});
|
|
442
446
|
const toolkit = deriveToolkit({
|
|
@@ -462,7 +466,7 @@ export async function runAgentCommand({ data, args, options, dataDir }) {
|
|
|
462
466
|
const stageParams = {
|
|
463
467
|
discipline: humanDiscipline,
|
|
464
468
|
track: humanTrack,
|
|
465
|
-
|
|
469
|
+
level,
|
|
466
470
|
skills: skillsWithAgent,
|
|
467
471
|
behaviours: data.behaviours,
|
|
468
472
|
agentBehaviours: agentData.behaviours,
|
|
@@ -534,7 +538,7 @@ export async function runAgentCommand({ data, args, options, dataDir }) {
|
|
|
534
538
|
const derivedSkills = deriveAgentSkills({
|
|
535
539
|
discipline: humanDiscipline,
|
|
536
540
|
track: humanTrack,
|
|
537
|
-
|
|
541
|
+
level,
|
|
538
542
|
skills: skillsWithAgent,
|
|
539
543
|
});
|
|
540
544
|
|
|
@@ -14,6 +14,15 @@ import { createEntityCommand } from "./command-factory.js";
|
|
|
14
14
|
import { behaviourToMarkdown } from "../formatters/behaviour/markdown.js";
|
|
15
15
|
import { formatTable } from "../lib/cli-output.js";
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Format behaviour list item for --list output
|
|
19
|
+
* @param {Object} behaviour - Behaviour entity
|
|
20
|
+
* @returns {string} Formatted list line
|
|
21
|
+
*/
|
|
22
|
+
function formatListItem(behaviour) {
|
|
23
|
+
return `${behaviour.id}, ${behaviour.name}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
17
26
|
/**
|
|
18
27
|
* Format behaviour summary output
|
|
19
28
|
* @param {Array} behaviours - Raw behaviour entities
|
|
@@ -34,7 +43,7 @@ function formatSummary(behaviours, data) {
|
|
|
34
43
|
|
|
35
44
|
console.log(formatTable(["ID", "Name", "Drivers"], rows));
|
|
36
45
|
console.log(`\nTotal: ${behaviours.length} behaviours`);
|
|
37
|
-
console.log(`\nRun 'npx pathway behaviour --list' for IDs`);
|
|
46
|
+
console.log(`\nRun 'npx pathway behaviour --list' for IDs and names`);
|
|
38
47
|
console.log(`Run 'npx pathway behaviour <id>' for details\n`);
|
|
39
48
|
}
|
|
40
49
|
|
|
@@ -57,5 +66,6 @@ export const runBehaviourCommand = createEntityCommand({
|
|
|
57
66
|
}),
|
|
58
67
|
formatSummary,
|
|
59
68
|
formatDetail,
|
|
69
|
+
formatListItem,
|
|
60
70
|
emojiIcon: "🧠",
|
|
61
71
|
});
|
package/src/commands/build.js
CHANGED
|
@@ -42,7 +42,8 @@ function resolvePackageLib(packageName) {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
const mapLibDir = resolvePackageLib("@forwardimpact/map");
|
|
45
|
-
const modelLibDir = resolvePackageLib("@forwardimpact/
|
|
45
|
+
const modelLibDir = resolvePackageLib("@forwardimpact/libskill");
|
|
46
|
+
const uiLibDir = resolvePackageLib("@forwardimpact/libui");
|
|
46
47
|
|
|
47
48
|
/**
|
|
48
49
|
* Files and directories to copy from app/
|
|
@@ -141,13 +142,21 @@ ${framework.emojiIcon} Generating ${framework.title} static site...
|
|
|
141
142
|
}
|
|
142
143
|
}
|
|
143
144
|
|
|
144
|
-
// Copy @forwardimpact/map and @forwardimpact/
|
|
145
|
+
// Copy @forwardimpact/map and @forwardimpact/libskill packages
|
|
145
146
|
// These are needed by the browser's import map
|
|
146
147
|
console.log("📚 Copying package dependencies...");
|
|
147
148
|
await cp(mapLibDir, join(outputDir, "map/lib"), { recursive: true });
|
|
148
149
|
console.log(` ✓ map/lib`);
|
|
149
150
|
await cp(modelLibDir, join(outputDir, "model/lib"), { recursive: true });
|
|
150
151
|
console.log(` ✓ model/lib`);
|
|
152
|
+
// Copy libui JS (src/) and CSS (src/css/)
|
|
153
|
+
await cp(uiLibDir, join(outputDir, "ui/lib"), { recursive: true });
|
|
154
|
+
// CSS is within uiLibDir/css/ so it's already copied as ui/lib/css/
|
|
155
|
+
// Create ui/css/ symlink-like copy for the CSS @import paths
|
|
156
|
+
await cp(join(uiLibDir, "css"), join(outputDir, "ui/css"), {
|
|
157
|
+
recursive: true,
|
|
158
|
+
});
|
|
159
|
+
console.log(` ✓ ui/lib + ui/css`);
|
|
151
160
|
|
|
152
161
|
// Copy data directory (dereference symlinks to copy actual content)
|
|
153
162
|
console.log("📁 Copying data files...");
|
|
@@ -22,6 +22,7 @@ import { capitalize } from "../formatters/shared.js";
|
|
|
22
22
|
* @param {Function} config.presentDetail - Function to present detail: (entity, data, options) => view
|
|
23
23
|
* @param {Function} config.formatSummary - Function to format summary output: (items, data) => void
|
|
24
24
|
* @param {Function} config.formatDetail - Function to format detail output: (view, framework) => void
|
|
25
|
+
* @param {Function} [config.formatListItem] - Optional function to format list item: (item) => string (defaults to item.id)
|
|
25
26
|
* @param {Function} [config.sortItems] - Optional function to sort items: (items) => sortedItems
|
|
26
27
|
* @param {Function} [config.validate] - Optional validation function: (data) => {errors: [], warnings: []}
|
|
27
28
|
* @param {string} [config.emojiIcon] - Optional emoji for the entity
|
|
@@ -34,6 +35,7 @@ export function createEntityCommand({
|
|
|
34
35
|
presentDetail,
|
|
35
36
|
formatSummary,
|
|
36
37
|
formatDetail,
|
|
38
|
+
formatListItem,
|
|
37
39
|
sortItems,
|
|
38
40
|
validate,
|
|
39
41
|
_emojiIcon = "",
|
|
@@ -48,10 +50,10 @@ export function createEntityCommand({
|
|
|
48
50
|
return handleValidate({ data, entityName, pluralName, validate });
|
|
49
51
|
}
|
|
50
52
|
|
|
51
|
-
// --list: Output
|
|
53
|
+
// --list: Output descriptive comma-separated lines for piping and AI agent discovery
|
|
52
54
|
if (options.list) {
|
|
53
55
|
for (const item of items) {
|
|
54
|
-
console.log(item.id);
|
|
56
|
+
console.log(formatListItem ? formatListItem(item) : item.id);
|
|
55
57
|
}
|
|
56
58
|
return;
|
|
57
59
|
}
|
package/src/commands/dev.js
CHANGED
|
@@ -31,7 +31,8 @@ function resolvePackageLib(packageName) {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
const mapLibDir = resolvePackageLib("@forwardimpact/map");
|
|
34
|
-
const modelLibDir = resolvePackageLib("@forwardimpact/
|
|
34
|
+
const modelLibDir = resolvePackageLib("@forwardimpact/libskill");
|
|
35
|
+
const uiLibDir = resolvePackageLib("@forwardimpact/libui");
|
|
35
36
|
|
|
36
37
|
const MIME_TYPES = {
|
|
37
38
|
".html": "text/html; charset=utf-8",
|
|
@@ -138,8 +139,14 @@ export async function runDevCommand({ dataDir, options }) {
|
|
|
138
139
|
// Serve @forwardimpact/map package files (resolved via Node module resolution)
|
|
139
140
|
filePath = join(mapLibDir, pathname.slice(9));
|
|
140
141
|
} else if (pathname.startsWith("/model/lib/")) {
|
|
141
|
-
// Serve @forwardimpact/
|
|
142
|
+
// Serve @forwardimpact/libskill package files (resolved via Node module resolution)
|
|
142
143
|
filePath = join(modelLibDir, pathname.slice(11));
|
|
144
|
+
} else if (pathname.startsWith("/ui/lib/")) {
|
|
145
|
+
// Serve @forwardimpact/libui package JS files
|
|
146
|
+
filePath = join(uiLibDir, pathname.slice(8));
|
|
147
|
+
} else if (pathname.startsWith("/ui/css/")) {
|
|
148
|
+
// Serve @forwardimpact/libui package CSS files
|
|
149
|
+
filePath = join(uiLibDir, "css", pathname.slice(8));
|
|
143
150
|
} else if (pathname === "/" || pathname === "") {
|
|
144
151
|
// Serve index.html for root
|
|
145
152
|
filePath = join(publicDir, "index.html");
|
|
@@ -14,6 +14,15 @@ import { createEntityCommand } from "./command-factory.js";
|
|
|
14
14
|
import { disciplineToMarkdown } from "../formatters/discipline/markdown.js";
|
|
15
15
|
import { formatTable } from "../lib/cli-output.js";
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Format discipline list item for --list output
|
|
19
|
+
* @param {Object} discipline - Discipline entity
|
|
20
|
+
* @returns {string} Formatted list line
|
|
21
|
+
*/
|
|
22
|
+
function formatListItem(discipline) {
|
|
23
|
+
return `${discipline.id}, ${discipline.specialization || discipline.id}, ${discipline.roleTitle || discipline.id}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
17
26
|
/**
|
|
18
27
|
* Format discipline summary output
|
|
19
28
|
* @param {Array} disciplines - Raw discipline entities
|
|
@@ -23,14 +32,21 @@ function formatSummary(disciplines) {
|
|
|
23
32
|
|
|
24
33
|
const rows = disciplines.map((d) => [
|
|
25
34
|
d.id,
|
|
35
|
+
d.specialization || d.id,
|
|
36
|
+
d.roleTitle || d.id,
|
|
26
37
|
d.coreSkills?.length || 0,
|
|
27
38
|
d.supportingSkills?.length || 0,
|
|
28
39
|
d.broadSkills?.length || 0,
|
|
29
40
|
]);
|
|
30
41
|
|
|
31
|
-
console.log(
|
|
42
|
+
console.log(
|
|
43
|
+
formatTable(
|
|
44
|
+
["ID", "Specialization", "Role Title", "Core", "Supporting", "Broad"],
|
|
45
|
+
rows,
|
|
46
|
+
),
|
|
47
|
+
);
|
|
32
48
|
console.log(`\nTotal: ${disciplines.length} disciplines`);
|
|
33
|
-
console.log(`\nRun 'npx pathway discipline --list' for IDs`);
|
|
49
|
+
console.log(`\nRun 'npx pathway discipline --list' for IDs and names`);
|
|
34
50
|
console.log(`Run 'npx pathway discipline <id>' for details\n`);
|
|
35
51
|
}
|
|
36
52
|
|
|
@@ -54,5 +70,6 @@ export const runDisciplineCommand = createEntityCommand({
|
|
|
54
70
|
}),
|
|
55
71
|
formatSummary,
|
|
56
72
|
formatDetail,
|
|
73
|
+
formatListItem,
|
|
57
74
|
emojiIcon: "📋",
|
|
58
75
|
});
|
package/src/commands/driver.js
CHANGED
|
@@ -20,6 +20,15 @@ import {
|
|
|
20
20
|
} from "../lib/cli-output.js";
|
|
21
21
|
import { getConceptEmoji } from "@forwardimpact/map/levels";
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Format driver list item for --list output
|
|
25
|
+
* @param {Object} driver - Driver entity
|
|
26
|
+
* @returns {string} Formatted list line
|
|
27
|
+
*/
|
|
28
|
+
function formatListItem(driver) {
|
|
29
|
+
return `${driver.id}, ${driver.name}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
23
32
|
/**
|
|
24
33
|
* Format driver summary output
|
|
25
34
|
* @param {Array} drivers - Raw driver entities
|
|
@@ -43,7 +52,7 @@ function formatSummary(drivers, data) {
|
|
|
43
52
|
|
|
44
53
|
console.log(formatTable(["ID", "Name", "Skills", "Behaviours"], rows));
|
|
45
54
|
console.log(`\nTotal: ${drivers.length} drivers`);
|
|
46
|
-
console.log(`\nRun 'npx pathway driver --list' for IDs`);
|
|
55
|
+
console.log(`\nRun 'npx pathway driver --list' for IDs and names`);
|
|
47
56
|
console.log(`Run 'npx pathway driver <id>' for details\n`);
|
|
48
57
|
}
|
|
49
58
|
|
|
@@ -90,5 +99,6 @@ export const runDriverCommand = createEntityCommand({
|
|
|
90
99
|
}),
|
|
91
100
|
formatSummary,
|
|
92
101
|
formatDetail,
|
|
102
|
+
formatListItem,
|
|
93
103
|
emojiIcon: "🎯",
|
|
94
104
|
});
|
package/src/commands/index.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
export { runDisciplineCommand } from "./discipline.js";
|
|
8
|
-
export {
|
|
8
|
+
export { runLevelCommand } from "./level.js";
|
|
9
9
|
export { runTrackCommand } from "./track.js";
|
|
10
10
|
export { runBehaviourCommand } from "./behaviour.js";
|
|
11
11
|
export { runSkillCommand } from "./skill.js";
|
package/src/commands/init.js
CHANGED
|
@@ -54,7 +54,7 @@ Next steps:
|
|
|
54
54
|
Data structure:
|
|
55
55
|
data/
|
|
56
56
|
├── framework.yaml # Framework metadata
|
|
57
|
-
├──
|
|
57
|
+
├── levels.yaml # Career levels
|
|
58
58
|
├── stages.yaml # Lifecycle stages
|
|
59
59
|
├── drivers.yaml # Business drivers
|
|
60
60
|
├── capabilities.yaml # Capability areas
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* Generates and displays interview questions in the terminal.
|
|
5
5
|
*
|
|
6
6
|
* Usage:
|
|
7
|
-
* npx fit-pathway interview <discipline> <
|
|
8
|
-
* npx fit-pathway interview <discipline> <
|
|
9
|
-
* npx fit-pathway interview <discipline> <
|
|
7
|
+
* npx fit-pathway interview <discipline> <level> # All interview types
|
|
8
|
+
* npx fit-pathway interview <discipline> <level> --track=<track> # With track
|
|
9
|
+
* npx fit-pathway interview <discipline> <level> --track=<track> --type=mission # Single type
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { createCompositeCommand } from "./command-factory.js";
|
|
@@ -45,7 +45,7 @@ function formatAllInterviews(views, options) {
|
|
|
45
45
|
|
|
46
46
|
export const runInterviewCommand = createCompositeCommand({
|
|
47
47
|
commandName: "interview",
|
|
48
|
-
requiredArgs: ["discipline_id", "
|
|
48
|
+
requiredArgs: ["discipline_id", "level_id"],
|
|
49
49
|
findEntities: (data, args, options) => {
|
|
50
50
|
const interviewType = options.type === "full" ? null : options.type;
|
|
51
51
|
|
|
@@ -57,7 +57,7 @@ export const runInterviewCommand = createCompositeCommand({
|
|
|
57
57
|
|
|
58
58
|
return {
|
|
59
59
|
discipline: data.disciplines.find((d) => d.id === args[0]),
|
|
60
|
-
|
|
60
|
+
level: data.levels.find((g) => g.id === args[1]),
|
|
61
61
|
track: options.track
|
|
62
62
|
? data.tracks.find((t) => t.id === options.track)
|
|
63
63
|
: null,
|
|
@@ -68,8 +68,8 @@ export const runInterviewCommand = createCompositeCommand({
|
|
|
68
68
|
if (!entities.discipline) {
|
|
69
69
|
return `Discipline not found: ${entities.discipline}`;
|
|
70
70
|
}
|
|
71
|
-
if (!entities.
|
|
72
|
-
return `
|
|
71
|
+
if (!entities.level) {
|
|
72
|
+
return `Level not found: ${entities.level}`;
|
|
73
73
|
}
|
|
74
74
|
if (options.track && !entities.track) {
|
|
75
75
|
return `Track not found: ${options.track}`;
|
|
@@ -79,7 +79,7 @@ export const runInterviewCommand = createCompositeCommand({
|
|
|
79
79
|
presenter: (entities, data, _options) => {
|
|
80
80
|
const params = {
|
|
81
81
|
discipline: entities.discipline,
|
|
82
|
-
|
|
82
|
+
level: entities.level,
|
|
83
83
|
track: entities.track,
|
|
84
84
|
skills: data.skills,
|
|
85
85
|
behaviours: data.behaviours,
|
package/src/commands/job.js
CHANGED
|
@@ -6,22 +6,22 @@
|
|
|
6
6
|
* Usage:
|
|
7
7
|
* npx pathway job # Summary with stats
|
|
8
8
|
* npx pathway job --list # All valid combinations (for piping)
|
|
9
|
-
* npx pathway job <discipline> <
|
|
10
|
-
* npx pathway job <discipline> <
|
|
11
|
-
* npx pathway job <d> <
|
|
12
|
-
* npx pathway job <d> <
|
|
9
|
+
* npx pathway job <discipline> <level> # Detail view (trackless)
|
|
10
|
+
* npx pathway job <discipline> <level> --track=<track> # Detail view (with track)
|
|
11
|
+
* npx pathway job <d> <l> [--track=<t>] --skills # Plain list of skill IDs
|
|
12
|
+
* npx pathway job <d> <l> [--track=<t>] --tools # Plain list of tool names
|
|
13
13
|
* npx pathway job se L3 --track=platform --checklist=code # Show checklist for handoff
|
|
14
14
|
* npx pathway job --validate # Validation checks
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import { prepareJobDetail } from "@forwardimpact/
|
|
17
|
+
import { prepareJobDetail } from "@forwardimpact/libskill/job";
|
|
18
18
|
import { jobToMarkdown } from "../formatters/job/markdown.js";
|
|
19
|
-
import { generateAllJobs } from "@forwardimpact/
|
|
19
|
+
import { generateJobTitle, generateAllJobs } from "@forwardimpact/libskill/derivation";
|
|
20
20
|
import { formatTable } from "../lib/cli-output.js";
|
|
21
21
|
import {
|
|
22
22
|
deriveChecklist,
|
|
23
23
|
formatChecklistMarkdown,
|
|
24
|
-
} from "@forwardimpact/
|
|
24
|
+
} from "@forwardimpact/libskill/checklist";
|
|
25
25
|
import { loadJobTemplate } from "../lib/template-loader.js";
|
|
26
26
|
import { toolkitToPlainList } from "../formatters/toolkit/markdown.js";
|
|
27
27
|
|
|
@@ -47,20 +47,23 @@ function formatJob(view, _options, entities, jobTemplate) {
|
|
|
47
47
|
export async function runJobCommand({ data, args, options, dataDir }) {
|
|
48
48
|
const jobs = generateAllJobs({
|
|
49
49
|
disciplines: data.disciplines,
|
|
50
|
-
|
|
50
|
+
levels: data.levels,
|
|
51
51
|
tracks: data.tracks,
|
|
52
52
|
skills: data.skills,
|
|
53
53
|
behaviours: data.behaviours,
|
|
54
54
|
validationRules: data.framework.validationRules,
|
|
55
55
|
});
|
|
56
56
|
|
|
57
|
-
// --list: Output
|
|
57
|
+
// --list: Output descriptive comma-separated lines for piping and AI agent discovery
|
|
58
58
|
if (options.list) {
|
|
59
59
|
for (const job of jobs) {
|
|
60
|
+
const title = generateJobTitle(job.discipline, job.level, job.track);
|
|
60
61
|
if (job.track) {
|
|
61
|
-
console.log(
|
|
62
|
+
console.log(
|
|
63
|
+
`${job.discipline.id} ${job.level.id} ${job.track.id}, ${title}`,
|
|
64
|
+
);
|
|
62
65
|
} else {
|
|
63
|
-
console.log(`${job.discipline.id} ${job.
|
|
66
|
+
console.log(`${job.discipline.id} ${job.level.id}, ${title}`);
|
|
64
67
|
}
|
|
65
68
|
}
|
|
66
69
|
return;
|
|
@@ -70,27 +73,37 @@ export async function runJobCommand({ data, args, options, dataDir }) {
|
|
|
70
73
|
if (args.length === 0) {
|
|
71
74
|
console.log(`\n💼 Jobs\n`);
|
|
72
75
|
|
|
73
|
-
// Count by discipline
|
|
76
|
+
// Count by discipline with name
|
|
74
77
|
const byDiscipline = {};
|
|
75
78
|
for (const job of jobs) {
|
|
76
|
-
|
|
77
|
-
|
|
79
|
+
const key = job.discipline.id;
|
|
80
|
+
if (!byDiscipline[key]) {
|
|
81
|
+
byDiscipline[key] = {
|
|
82
|
+
name: job.discipline.specialization || job.discipline.id,
|
|
83
|
+
count: 0,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
byDiscipline[key].count++;
|
|
78
87
|
}
|
|
79
88
|
|
|
80
|
-
const rows = Object.entries(byDiscipline).map(([id,
|
|
81
|
-
|
|
89
|
+
const rows = Object.entries(byDiscipline).map(([id, info]) => [
|
|
90
|
+
id,
|
|
91
|
+
info.name,
|
|
92
|
+
info.count,
|
|
93
|
+
]);
|
|
94
|
+
console.log(formatTable(["ID", "Specialization", "Combinations"], rows));
|
|
82
95
|
console.log(`\nTotal: ${jobs.length} valid job combinations`);
|
|
83
|
-
console.log(`\nRun 'npx pathway job --list' for all combinations`);
|
|
96
|
+
console.log(`\nRun 'npx pathway job --list' for all combinations with titles`);
|
|
84
97
|
console.log(
|
|
85
|
-
`Run 'npx pathway job <discipline> <
|
|
98
|
+
`Run 'npx pathway job <discipline> <level> [--track=<track>]' for details\n`,
|
|
86
99
|
);
|
|
87
100
|
return;
|
|
88
101
|
}
|
|
89
102
|
|
|
90
|
-
// Handle job detail view - requires discipline and
|
|
103
|
+
// Handle job detail view - requires discipline and level
|
|
91
104
|
if (args.length < 2) {
|
|
92
105
|
console.error(
|
|
93
|
-
"Usage: npx pathway job <discipline> <
|
|
106
|
+
"Usage: npx pathway job <discipline> <level> [--track=<track>]",
|
|
94
107
|
);
|
|
95
108
|
console.error(" npx pathway job --list");
|
|
96
109
|
console.error("Example: npx pathway job software_engineering L4");
|
|
@@ -101,7 +114,7 @@ export async function runJobCommand({ data, args, options, dataDir }) {
|
|
|
101
114
|
}
|
|
102
115
|
|
|
103
116
|
const discipline = data.disciplines.find((d) => d.id === args[0]);
|
|
104
|
-
const
|
|
117
|
+
const level = data.levels.find((g) => g.id === args[1]);
|
|
105
118
|
const track = options.track
|
|
106
119
|
? data.tracks.find((t) => t.id === options.track)
|
|
107
120
|
: null;
|
|
@@ -112,9 +125,9 @@ export async function runJobCommand({ data, args, options, dataDir }) {
|
|
|
112
125
|
process.exit(1);
|
|
113
126
|
}
|
|
114
127
|
|
|
115
|
-
if (!
|
|
116
|
-
console.error(`
|
|
117
|
-
console.error(`Available: ${data.
|
|
128
|
+
if (!level) {
|
|
129
|
+
console.error(`Level not found: ${args[1]}`);
|
|
130
|
+
console.error(`Available: ${data.levels.map((g) => g.id).join(", ")}`);
|
|
118
131
|
process.exit(1);
|
|
119
132
|
}
|
|
120
133
|
|
|
@@ -126,7 +139,7 @@ export async function runJobCommand({ data, args, options, dataDir }) {
|
|
|
126
139
|
|
|
127
140
|
const view = prepareJobDetail({
|
|
128
141
|
discipline,
|
|
129
|
-
|
|
142
|
+
level,
|
|
130
143
|
track,
|
|
131
144
|
skills: data.skills,
|
|
132
145
|
behaviours: data.behaviours,
|
|
@@ -137,8 +150,8 @@ export async function runJobCommand({ data, args, options, dataDir }) {
|
|
|
137
150
|
|
|
138
151
|
if (!view) {
|
|
139
152
|
const combo = track
|
|
140
|
-
? `${discipline.id} × ${
|
|
141
|
-
: `${discipline.id} × ${
|
|
153
|
+
? `${discipline.id} × ${level.id} × ${track.id}`
|
|
154
|
+
: `${discipline.id} × ${level.id}`;
|
|
142
155
|
console.error(`Invalid combination: ${combo}`);
|
|
143
156
|
if (track) {
|
|
144
157
|
const validTracks =
|
|
@@ -212,5 +225,5 @@ export async function runJobCommand({ data, args, options, dataDir }) {
|
|
|
212
225
|
|
|
213
226
|
// Load job template for description formatting
|
|
214
227
|
const jobTemplate = await loadJobTemplate(dataDir);
|
|
215
|
-
formatJob(view, options, { discipline,
|
|
228
|
+
formatJob(view, options, { discipline, level, track }, jobTemplate);
|
|
216
229
|
}
|