@forwardimpact/pathway 0.20.0 → 0.22.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 +28 -28
- package/package.json +4 -4
- package/src/commands/agent.js +8 -8
- package/src/commands/build.js +7 -7
- package/src/commands/dev.js +7 -7
- package/src/commands/driver.js +1 -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 +19 -19
- package/src/commands/level.js +60 -0
- package/src/commands/progress.js +20 -20
- package/src/commands/questions.js +3 -3
- package/src/commands/skill.js +1 -1
- package/src/commands/track.js +1 -1
- package/src/commands/update.js +12 -14
- package/src/components/action-buttons.js +3 -3
- package/src/components/builder.js +25 -25
- package/src/components/checklist.js +1 -1
- package/src/components/comparison-radar.js +3 -3
- package/src/components/detail.js +3 -3
- package/src/components/grid.js +1 -1
- package/src/components/radar-chart.js +3 -3
- package/src/components/skill-matrix.js +7 -7
- package/src/css/pages/landing.css +5 -5
- package/src/formatters/behaviour/dom.js +1 -1
- package/src/formatters/discipline/dom.js +1 -1
- package/src/formatters/driver/dom.js +1 -1
- package/src/formatters/index.js +5 -5
- package/src/formatters/interview/dom.js +1 -1
- package/src/formatters/interview/markdown.js +1 -1
- package/src/formatters/interview/shared.js +20 -20
- 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 +32 -28
- package/src/formatters/{grade → level}/markdown.js +20 -29
- 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 +48 -48
- 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 +2 -2
- package/src/formatters/skill/microdata.js +3 -3
- package/src/formatters/skill/shared.js +3 -3
- package/src/formatters/track/dom.js +1 -1
- package/src/formatters/track/markdown.js +1 -1
- package/src/handout-main.js +13 -16
- package/src/handout.html +4 -4
- package/src/index.html +5 -5
- package/src/lib/card-mappers.js +17 -17
- package/src/lib/cli-command.js +3 -3
- package/src/lib/cli-output.js +2 -2
- package/src/lib/job-cache.js +11 -11
- package/src/lib/render.js +6 -6
- package/src/lib/state.js +2 -2
- package/src/lib/yaml-loader.js +9 -9
- package/src/main.js +10 -10
- package/src/pages/agent-builder.js +11 -11
- package/src/pages/assessment-results.js +27 -23
- package/src/pages/behaviour.js +1 -1
- package/src/pages/discipline.js +1 -1
- package/src/pages/driver.js +1 -1
- package/src/pages/interview-builder.js +6 -6
- package/src/pages/interview.js +9 -9
- package/src/pages/job-builder.js +6 -6
- package/src/pages/job.js +7 -7
- package/src/pages/landing.js +9 -9
- 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 +8 -8
- package/src/pages/skill.js +1 -4
- package/src/pages/stage.js +1 -1
- package/src/pages/tool.js +1 -1
- package/src/pages/track.js +1 -1
- package/src/slide-main.js +22 -22
- 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 +3 -3
- package/src/slides/level.js +32 -0
- package/src/slides/overview.js +9 -9
- package/src/slides/progress.js +13 -13
- package/src/slides.html +4 -4
- package/src/types.js +1 -1
- package/templates/install.template.sh +11 -8
- package/templates/job.template.md +2 -2
- package/src/commands/grade.js +0 -60
- 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
|
*
|
|
@@ -32,12 +32,12 @@
|
|
|
32
32
|
import { join, resolve } from "path";
|
|
33
33
|
import { existsSync } from "fs";
|
|
34
34
|
import { homedir } from "os";
|
|
35
|
-
import { loadAllData } from "@forwardimpact/
|
|
35
|
+
import { loadAllData } from "@forwardimpact/map/loader";
|
|
36
36
|
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
|
|
@@ -341,7 +341,7 @@ function printHelp() {
|
|
|
341
341
|
* 3. ~/.fit/pathway/data/ (home directory install)
|
|
342
342
|
* 4. ./data/ relative to current working directory
|
|
343
343
|
* 5. ./examples/ relative to current working directory
|
|
344
|
-
* 6.
|
|
344
|
+
* 6. products/map/examples/ for monorepo development
|
|
345
345
|
*
|
|
346
346
|
* @param {Object} options - Parsed command options
|
|
347
347
|
* @returns {string} Resolved absolute path to data directory
|
|
@@ -375,10 +375,10 @@ function resolveDataPath(options) {
|
|
|
375
375
|
return cwdExamples;
|
|
376
376
|
}
|
|
377
377
|
|
|
378
|
-
// 6. Monorepo:
|
|
379
|
-
const
|
|
380
|
-
if (existsSync(
|
|
381
|
-
return
|
|
378
|
+
// 6. Monorepo: products/map/examples/
|
|
379
|
+
const mapExamples = join(process.cwd(), "products/map/examples");
|
|
380
|
+
if (existsSync(mapExamples)) {
|
|
381
|
+
return mapExamples;
|
|
382
382
|
}
|
|
383
383
|
|
|
384
384
|
throw new Error(
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forwardimpact/pathway",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.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": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "https://github.com/forwardimpact/monorepo",
|
|
9
|
-
"directory": "
|
|
9
|
+
"directory": "products/pathway"
|
|
10
10
|
},
|
|
11
11
|
"homepage": "https://www.forwardimpact.team/pathway",
|
|
12
12
|
"keywords": [
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"./commands": "./src/commands/index.js"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@forwardimpact/
|
|
44
|
-
"@forwardimpact/libpathway": "^
|
|
43
|
+
"@forwardimpact/map": "^0.11.0",
|
|
44
|
+
"@forwardimpact/libpathway": "^3.0.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
|
@@ -30,12 +30,12 @@ import { stringify as stringifyYaml } from "yaml";
|
|
|
30
30
|
import {
|
|
31
31
|
loadAgentData,
|
|
32
32
|
loadSkillsWithAgentData,
|
|
33
|
-
} from "@forwardimpact/
|
|
33
|
+
} from "@forwardimpact/map/loader";
|
|
34
34
|
import {
|
|
35
35
|
generateStageAgentProfile,
|
|
36
36
|
validateAgentProfile,
|
|
37
37
|
validateAgentSkill,
|
|
38
|
-
|
|
38
|
+
deriveReferenceLevel,
|
|
39
39
|
deriveAgentSkills,
|
|
40
40
|
generateSkillMarkdown,
|
|
41
41
|
deriveToolkit,
|
|
@@ -414,15 +414,15 @@ export async function runAgentCommand({ data, args, options, dataDir }) {
|
|
|
414
414
|
process.exit(1);
|
|
415
415
|
}
|
|
416
416
|
|
|
417
|
-
// Get reference
|
|
418
|
-
const
|
|
417
|
+
// Get reference level for derivation
|
|
418
|
+
const level = deriveReferenceLevel(data.levels);
|
|
419
419
|
|
|
420
420
|
// --skills: Output plain list of skill IDs (for piping)
|
|
421
421
|
if (options.skills) {
|
|
422
422
|
const derivedSkills = deriveAgentSkills({
|
|
423
423
|
discipline: humanDiscipline,
|
|
424
424
|
track: humanTrack,
|
|
425
|
-
|
|
425
|
+
level,
|
|
426
426
|
skills: skillsWithAgent,
|
|
427
427
|
});
|
|
428
428
|
for (const skill of derivedSkills) {
|
|
@@ -436,7 +436,7 @@ export async function runAgentCommand({ data, args, options, dataDir }) {
|
|
|
436
436
|
const derivedSkills = deriveAgentSkills({
|
|
437
437
|
discipline: humanDiscipline,
|
|
438
438
|
track: humanTrack,
|
|
439
|
-
|
|
439
|
+
level,
|
|
440
440
|
skills: skillsWithAgent,
|
|
441
441
|
});
|
|
442
442
|
const toolkit = deriveToolkit({
|
|
@@ -462,7 +462,7 @@ export async function runAgentCommand({ data, args, options, dataDir }) {
|
|
|
462
462
|
const stageParams = {
|
|
463
463
|
discipline: humanDiscipline,
|
|
464
464
|
track: humanTrack,
|
|
465
|
-
|
|
465
|
+
level,
|
|
466
466
|
skills: skillsWithAgent,
|
|
467
467
|
behaviours: data.behaviours,
|
|
468
468
|
agentBehaviours: agentData.behaviours,
|
|
@@ -534,7 +534,7 @@ export async function runAgentCommand({ data, args, options, dataDir }) {
|
|
|
534
534
|
const derivedSkills = deriveAgentSkills({
|
|
535
535
|
discipline: humanDiscipline,
|
|
536
536
|
track: humanTrack,
|
|
537
|
-
|
|
537
|
+
level,
|
|
538
538
|
skills: skillsWithAgent,
|
|
539
539
|
});
|
|
540
540
|
|
package/src/commands/build.js
CHANGED
|
@@ -21,8 +21,8 @@ import { join, dirname, relative, resolve } from "path";
|
|
|
21
21
|
import { fileURLToPath } from "url";
|
|
22
22
|
import { execFileSync } from "child_process";
|
|
23
23
|
import Mustache from "mustache";
|
|
24
|
-
import { generateAllIndexes } from "@forwardimpact/
|
|
25
|
-
import { loadFrameworkConfig } from "@forwardimpact/
|
|
24
|
+
import { generateAllIndexes } from "@forwardimpact/map/index-generator";
|
|
25
|
+
import { loadFrameworkConfig } from "@forwardimpact/map/loader";
|
|
26
26
|
|
|
27
27
|
const __filename = fileURLToPath(import.meta.url);
|
|
28
28
|
const __dirname = dirname(__filename);
|
|
@@ -31,7 +31,7 @@ const appDir = join(__dirname, "..");
|
|
|
31
31
|
/**
|
|
32
32
|
* Resolve package directory using Node's module resolution.
|
|
33
33
|
* Works in both monorepo (development) and installed (production) contexts.
|
|
34
|
-
* @param {string} packageName - Package specifier (e.g., '@forwardimpact/
|
|
34
|
+
* @param {string} packageName - Package specifier (e.g., '@forwardimpact/map')
|
|
35
35
|
* @returns {string} Absolute path to package lib directory
|
|
36
36
|
*/
|
|
37
37
|
function resolvePackageLib(packageName) {
|
|
@@ -41,7 +41,7 @@ function resolvePackageLib(packageName) {
|
|
|
41
41
|
return dirname(fileURLToPath(mainUrl));
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
const
|
|
44
|
+
const mapLibDir = resolvePackageLib("@forwardimpact/map");
|
|
45
45
|
const modelLibDir = resolvePackageLib("@forwardimpact/libpathway");
|
|
46
46
|
|
|
47
47
|
/**
|
|
@@ -141,11 +141,11 @@ ${framework.emojiIcon} Generating ${framework.title} static site...
|
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
// Copy @forwardimpact/
|
|
144
|
+
// Copy @forwardimpact/map and @forwardimpact/libpathway packages
|
|
145
145
|
// These are needed by the browser's import map
|
|
146
146
|
console.log("📚 Copying package dependencies...");
|
|
147
|
-
await cp(
|
|
148
|
-
console.log(` ✓
|
|
147
|
+
await cp(mapLibDir, join(outputDir, "map/lib"), { recursive: true });
|
|
148
|
+
console.log(` ✓ map/lib`);
|
|
149
149
|
await cp(modelLibDir, join(outputDir, "model/lib"), { recursive: true });
|
|
150
150
|
console.log(` ✓ model/lib`);
|
|
151
151
|
|
package/src/commands/dev.js
CHANGED
|
@@ -9,8 +9,8 @@ import { createServer } from "http";
|
|
|
9
9
|
import { readFile, stat } from "fs/promises";
|
|
10
10
|
import { join, extname, dirname } from "path";
|
|
11
11
|
import { fileURLToPath } from "url";
|
|
12
|
-
import { generateAllIndexes } from "@forwardimpact/
|
|
13
|
-
import { loadFrameworkConfig } from "@forwardimpact/
|
|
12
|
+
import { generateAllIndexes } from "@forwardimpact/map/index-generator";
|
|
13
|
+
import { loadFrameworkConfig } from "@forwardimpact/map/loader";
|
|
14
14
|
|
|
15
15
|
const __filename = fileURLToPath(import.meta.url);
|
|
16
16
|
const __dirname = dirname(__filename);
|
|
@@ -20,7 +20,7 @@ const rootDir = join(__dirname, "../..");
|
|
|
20
20
|
/**
|
|
21
21
|
* Resolve package directory using Node's module resolution.
|
|
22
22
|
* Works in both monorepo (development) and installed (production) contexts.
|
|
23
|
-
* @param {string} packageName - Package specifier (e.g., '@forwardimpact/
|
|
23
|
+
* @param {string} packageName - Package specifier (e.g., '@forwardimpact/map')
|
|
24
24
|
* @returns {string} Absolute path to package lib directory
|
|
25
25
|
*/
|
|
26
26
|
function resolvePackageLib(packageName) {
|
|
@@ -30,7 +30,7 @@ function resolvePackageLib(packageName) {
|
|
|
30
30
|
return dirname(fileURLToPath(mainUrl));
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
const
|
|
33
|
+
const mapLibDir = resolvePackageLib("@forwardimpact/map");
|
|
34
34
|
const modelLibDir = resolvePackageLib("@forwardimpact/libpathway");
|
|
35
35
|
|
|
36
36
|
const MIME_TYPES = {
|
|
@@ -134,9 +134,9 @@ export async function runDevCommand({ dataDir, options }) {
|
|
|
134
134
|
} else if (pathname.startsWith("/templates/")) {
|
|
135
135
|
// Serve from templates directory
|
|
136
136
|
filePath = join(rootDir, pathname);
|
|
137
|
-
} else if (pathname.startsWith("/
|
|
138
|
-
// Serve @forwardimpact/
|
|
139
|
-
filePath = join(
|
|
137
|
+
} else if (pathname.startsWith("/map/lib/")) {
|
|
138
|
+
// Serve @forwardimpact/map package files (resolved via Node module resolution)
|
|
139
|
+
filePath = join(mapLibDir, pathname.slice(9));
|
|
140
140
|
} else if (pathname.startsWith("/model/lib/")) {
|
|
141
141
|
// Serve @forwardimpact/libpathway package files (resolved via Node module resolution)
|
|
142
142
|
filePath = join(modelLibDir, pathname.slice(11));
|
package/src/commands/driver.js
CHANGED
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,10 +6,10 @@
|
|
|
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
|
*/
|
|
@@ -47,20 +47,20 @@ 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 clean lines for piping (discipline
|
|
57
|
+
// --list: Output clean lines for piping (discipline level track format)
|
|
58
58
|
if (options.list) {
|
|
59
59
|
for (const job of jobs) {
|
|
60
60
|
if (job.track) {
|
|
61
|
-
console.log(`${job.discipline.id} ${job.
|
|
61
|
+
console.log(`${job.discipline.id} ${job.level.id} ${job.track.id}`);
|
|
62
62
|
} else {
|
|
63
|
-
console.log(`${job.discipline.id} ${job.
|
|
63
|
+
console.log(`${job.discipline.id} ${job.level.id}`);
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
return;
|
|
@@ -82,15 +82,15 @@ export async function runJobCommand({ data, args, options, dataDir }) {
|
|
|
82
82
|
console.log(`\nTotal: ${jobs.length} valid job combinations`);
|
|
83
83
|
console.log(`\nRun 'npx pathway job --list' for all combinations`);
|
|
84
84
|
console.log(
|
|
85
|
-
`Run 'npx pathway job <discipline> <
|
|
85
|
+
`Run 'npx pathway job <discipline> <level> [--track=<track>]' for details\n`,
|
|
86
86
|
);
|
|
87
87
|
return;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
// Handle job detail view - requires discipline and
|
|
90
|
+
// Handle job detail view - requires discipline and level
|
|
91
91
|
if (args.length < 2) {
|
|
92
92
|
console.error(
|
|
93
|
-
"Usage: npx pathway job <discipline> <
|
|
93
|
+
"Usage: npx pathway job <discipline> <level> [--track=<track>]",
|
|
94
94
|
);
|
|
95
95
|
console.error(" npx pathway job --list");
|
|
96
96
|
console.error("Example: npx pathway job software_engineering L4");
|
|
@@ -101,7 +101,7 @@ export async function runJobCommand({ data, args, options, dataDir }) {
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
const discipline = data.disciplines.find((d) => d.id === args[0]);
|
|
104
|
-
const
|
|
104
|
+
const level = data.levels.find((g) => g.id === args[1]);
|
|
105
105
|
const track = options.track
|
|
106
106
|
? data.tracks.find((t) => t.id === options.track)
|
|
107
107
|
: null;
|
|
@@ -112,9 +112,9 @@ export async function runJobCommand({ data, args, options, dataDir }) {
|
|
|
112
112
|
process.exit(1);
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
if (!
|
|
116
|
-
console.error(`
|
|
117
|
-
console.error(`Available: ${data.
|
|
115
|
+
if (!level) {
|
|
116
|
+
console.error(`Level not found: ${args[1]}`);
|
|
117
|
+
console.error(`Available: ${data.levels.map((g) => g.id).join(", ")}`);
|
|
118
118
|
process.exit(1);
|
|
119
119
|
}
|
|
120
120
|
|
|
@@ -126,7 +126,7 @@ export async function runJobCommand({ data, args, options, dataDir }) {
|
|
|
126
126
|
|
|
127
127
|
const view = prepareJobDetail({
|
|
128
128
|
discipline,
|
|
129
|
-
|
|
129
|
+
level,
|
|
130
130
|
track,
|
|
131
131
|
skills: data.skills,
|
|
132
132
|
behaviours: data.behaviours,
|
|
@@ -137,8 +137,8 @@ export async function runJobCommand({ data, args, options, dataDir }) {
|
|
|
137
137
|
|
|
138
138
|
if (!view) {
|
|
139
139
|
const combo = track
|
|
140
|
-
? `${discipline.id} × ${
|
|
141
|
-
: `${discipline.id} × ${
|
|
140
|
+
? `${discipline.id} × ${level.id} × ${track.id}`
|
|
141
|
+
: `${discipline.id} × ${level.id}`;
|
|
142
142
|
console.error(`Invalid combination: ${combo}`);
|
|
143
143
|
if (track) {
|
|
144
144
|
const validTracks =
|
|
@@ -212,5 +212,5 @@ export async function runJobCommand({ data, args, options, dataDir }) {
|
|
|
212
212
|
|
|
213
213
|
// Load job template for description formatting
|
|
214
214
|
const jobTemplate = await loadJobTemplate(dataDir);
|
|
215
|
-
formatJob(view, options, { discipline,
|
|
215
|
+
formatJob(view, options, { discipline, level, track }, jobTemplate);
|
|
216
216
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Level CLI Command
|
|
3
|
+
*
|
|
4
|
+
* Handles level summary, listing, and detail display in the terminal.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* npx pathway level # Summary with stats
|
|
8
|
+
* npx pathway level --list # IDs only (for piping)
|
|
9
|
+
* npx pathway level <id> # Detail view
|
|
10
|
+
* npx pathway level --validate # Validation checks
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { createEntityCommand } from "./command-factory.js";
|
|
14
|
+
import { levelToMarkdown } from "../formatters/level/markdown.js";
|
|
15
|
+
import { formatTable } from "../lib/cli-output.js";
|
|
16
|
+
import { getConceptEmoji } from "@forwardimpact/map/levels";
|
|
17
|
+
import { capitalize } from "../formatters/shared.js";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Format level summary output
|
|
21
|
+
* @param {Array} levels - Raw level entities
|
|
22
|
+
* @param {Object} data - Full data context
|
|
23
|
+
*/
|
|
24
|
+
function formatSummary(levels, data) {
|
|
25
|
+
const { framework } = data;
|
|
26
|
+
const emoji = framework ? getConceptEmoji(framework, "level") : "📊";
|
|
27
|
+
|
|
28
|
+
console.log(`\n${emoji} Levels\n`);
|
|
29
|
+
|
|
30
|
+
const rows = levels.map((g) => [
|
|
31
|
+
g.id,
|
|
32
|
+
g.displayName || g.id,
|
|
33
|
+
g.typicalExperienceRange || "-",
|
|
34
|
+
capitalize(g.baseSkillProficiencies?.primary || "-"),
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
console.log(formatTable(["ID", "Name", "Experience", "Primary Level"], rows));
|
|
38
|
+
console.log(`\nTotal: ${levels.length} levels`);
|
|
39
|
+
console.log(`\nRun 'npx pathway level --list' for IDs`);
|
|
40
|
+
console.log(`Run 'npx pathway level <id>' for details\n`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Format level detail output
|
|
45
|
+
* @param {Object} level - Raw level entity
|
|
46
|
+
* @param {Object} framework - Framework config
|
|
47
|
+
*/
|
|
48
|
+
function formatDetail(level, framework) {
|
|
49
|
+
console.log(levelToMarkdown(level, framework));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const runLevelCommand = createEntityCommand({
|
|
53
|
+
entityName: "level",
|
|
54
|
+
pluralName: "levels",
|
|
55
|
+
findEntity: (data, id) => data.levels.find((g) => g.id === id),
|
|
56
|
+
presentDetail: (entity) => entity,
|
|
57
|
+
formatSummary,
|
|
58
|
+
formatDetail,
|
|
59
|
+
emojiIcon: "📊",
|
|
60
|
+
});
|