@forwardimpact/pathway 0.21.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.
Files changed (72) hide show
  1. package/README.md +3 -3
  2. package/bin/fit-pathway.js +22 -22
  3. package/package.json +3 -3
  4. package/src/commands/agent.js +7 -7
  5. package/src/commands/index.js +1 -1
  6. package/src/commands/init.js +1 -1
  7. package/src/commands/interview.js +8 -8
  8. package/src/commands/job.js +19 -19
  9. package/src/commands/level.js +60 -0
  10. package/src/commands/progress.js +20 -20
  11. package/src/commands/questions.js +3 -3
  12. package/src/components/action-buttons.js +3 -3
  13. package/src/components/builder.js +25 -25
  14. package/src/components/comparison-radar.js +3 -3
  15. package/src/components/detail.js +2 -2
  16. package/src/components/grid.js +1 -1
  17. package/src/components/radar-chart.js +3 -3
  18. package/src/components/skill-matrix.js +7 -7
  19. package/src/css/pages/landing.css +5 -5
  20. package/src/formatters/index.js +5 -5
  21. package/src/formatters/interview/shared.js +20 -20
  22. package/src/formatters/job/description.js +17 -17
  23. package/src/formatters/job/dom.js +12 -12
  24. package/src/formatters/job/markdown.js +7 -7
  25. package/src/formatters/json-ld.js +24 -24
  26. package/src/formatters/{grade → level}/dom.js +31 -27
  27. package/src/formatters/{grade → level}/markdown.js +19 -28
  28. package/src/formatters/{grade → level}/microdata.js +28 -38
  29. package/src/formatters/level/shared.js +86 -0
  30. package/src/formatters/progress/markdown.js +2 -2
  31. package/src/formatters/progress/shared.js +48 -48
  32. package/src/formatters/questions/markdown.js +8 -6
  33. package/src/formatters/questions/shared.js +7 -7
  34. package/src/formatters/skill/dom.js +4 -4
  35. package/src/formatters/skill/markdown.js +1 -1
  36. package/src/formatters/skill/microdata.js +3 -3
  37. package/src/formatters/skill/shared.js +2 -2
  38. package/src/handout-main.js +12 -12
  39. package/src/index.html +1 -1
  40. package/src/lib/card-mappers.js +16 -16
  41. package/src/lib/cli-command.js +3 -3
  42. package/src/lib/cli-output.js +2 -2
  43. package/src/lib/job-cache.js +11 -11
  44. package/src/lib/render.js +5 -5
  45. package/src/lib/state.js +2 -2
  46. package/src/lib/yaml-loader.js +9 -9
  47. package/src/main.js +10 -10
  48. package/src/pages/agent-builder.js +11 -11
  49. package/src/pages/assessment-results.js +27 -23
  50. package/src/pages/interview-builder.js +6 -6
  51. package/src/pages/interview.js +8 -8
  52. package/src/pages/job-builder.js +6 -6
  53. package/src/pages/job.js +7 -7
  54. package/src/pages/landing.js +8 -8
  55. package/src/pages/level.js +122 -0
  56. package/src/pages/progress-builder.js +8 -8
  57. package/src/pages/progress.js +74 -74
  58. package/src/pages/self-assessment.js +7 -7
  59. package/src/slide-main.js +22 -22
  60. package/src/slides/chapter.js +4 -4
  61. package/src/slides/index.js +10 -10
  62. package/src/slides/interview.js +2 -2
  63. package/src/slides/job.js +3 -3
  64. package/src/slides/level.js +32 -0
  65. package/src/slides/overview.js +9 -9
  66. package/src/slides/progress.js +13 -13
  67. package/src/types.js +1 -1
  68. package/templates/job.template.md +2 -2
  69. package/src/commands/grade.js +0 -60
  70. package/src/formatters/grade/shared.js +0 -86
  71. package/src/pages/grade.js +0 -122
  72. 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 grade to explore roles
56
- - **Skill Browser** — View all skills with level descriptions
57
- - **Career Progression** — Compare grades and identify growth areas
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
 
@@ -10,16 +10,16 @@
10
10
  *
11
11
  * Commands:
12
12
  * discipline [<id>] Show disciplines
13
- * grade [<id>] Show grades
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> <grade>] [--track=TRACK] Generate job definition
21
- * interview <discipline> <grade> [--track=TRACK] [--type=mission|decomposition|stakeholder] Generate interview
22
- * progress <discipline> <grade> [--track=TRACK] [--compare=GRADE] Career progression
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 { runGradeCommand } from "../src/commands/grade.js";
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
- grade: runGradeCommand,
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
- grade [<id>] Browse career grades/levels
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 × grade × track combinations.
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> <grade> Detail view (trackless)
122
- npx fit-pathway job <d> <g> --track=<track> Detail view (with track)
123
- npx fit-pathway job <d> <g> --skills Plain list of skill IDs
124
- npx fit-pathway job <d> <g> --tools Plain list of tool names
125
- npx fit-pathway job <d> <g> --checklist=<stage> Show handoff checklist
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> <grade> All types
174
- npx fit-pathway interview <d> <g> --track=<track> With track
175
- npx fit-pathway interview <d> <g> --track=<t> --type=<type> Single type
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 grades.
186
+ Analyze career progression between levels.
187
187
 
188
188
  Usage:
189
- npx fit-pathway progress <discipline> <grade>
190
- npx fit-pathway progress <d> <g> --track=<track>
191
- npx fit-pathway progress <d> <g> --compare=<to_grade>
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=GRADE Compare to specific grade
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 level
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.21.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": {
@@ -40,8 +40,8 @@
40
40
  "./commands": "./src/commands/index.js"
41
41
  },
42
42
  "dependencies": {
43
- "@forwardimpact/map": "^0.10.0",
44
- "@forwardimpact/libpathway": "^2.0.0",
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"
@@ -35,7 +35,7 @@ import {
35
35
  generateStageAgentProfile,
36
36
  validateAgentProfile,
37
37
  validateAgentSkill,
38
- deriveReferenceGrade,
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 grade for derivation
418
- const grade = deriveReferenceGrade(data.grades);
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
- grade,
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
- grade,
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
- grade,
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
- grade,
537
+ level,
538
538
  skills: skillsWithAgent,
539
539
  });
540
540
 
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  export { runDisciplineCommand } from "./discipline.js";
8
- export { runGradeCommand } from "./grade.js";
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";
@@ -54,7 +54,7 @@ Next steps:
54
54
  Data structure:
55
55
  data/
56
56
  ├── framework.yaml # Framework metadata
57
- ├── grades.yaml # Career levels
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> <grade> # All interview types
8
- * npx fit-pathway interview <discipline> <grade> --track=<track> # With track
9
- * npx fit-pathway interview <discipline> <grade> --track=<track> --type=mission # Single type
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", "grade_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
- grade: data.grades.find((g) => g.id === args[1]),
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.grade) {
72
- return `Grade not found: ${entities.grade}`;
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
- grade: entities.grade,
82
+ level: entities.level,
83
83
  track: entities.track,
84
84
  skills: data.skills,
85
85
  behaviours: data.behaviours,
@@ -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> <grade> # Detail view (trackless)
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
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
- grades: data.grades,
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 grade track format)
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.grade.id} ${job.track.id}`);
61
+ console.log(`${job.discipline.id} ${job.level.id} ${job.track.id}`);
62
62
  } else {
63
- console.log(`${job.discipline.id} ${job.grade.id}`);
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> <grade> [--track=<track>]' for details\n`,
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 grade
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> <grade> [--track=<track>]",
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 grade = data.grades.find((g) => g.id === args[1]);
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 (!grade) {
116
- console.error(`Grade not found: ${args[1]}`);
117
- console.error(`Available: ${data.grades.map((g) => g.id).join(", ")}`);
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
- grade,
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} × ${grade.id} × ${track.id}`
141
- : `${discipline.id} × ${grade.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, grade, track }, jobTemplate);
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
+ });
@@ -4,15 +4,15 @@
4
4
  * Shows career progression analysis in the terminal.
5
5
  *
6
6
  * Usage:
7
- * npx pathway progress <discipline> <grade> # Progress for trackless job
8
- * npx pathway progress <discipline> <grade> --track=<track> # Progress with track
9
- * npx pathway progress <discipline> <from_grade> --compare=<to_grade> # Compare grades
7
+ * npx pathway progress <discipline> <level> # Progress for trackless job
8
+ * npx pathway progress <discipline> <level> --track=<track> # Progress with track
9
+ * npx pathway progress <discipline> <from_level> --compare=<to_level> # Compare levels
10
10
  */
11
11
 
12
12
  import { createCompositeCommand } from "./command-factory.js";
13
13
  import {
14
14
  prepareProgressDetail,
15
- getDefaultTargetGrade,
15
+ getDefaultTargetLevel,
16
16
  } from "../formatters/progress/shared.js";
17
17
  import { progressToMarkdown } from "../formatters/progress/markdown.js";
18
18
 
@@ -26,53 +26,53 @@ function formatProgress(view) {
26
26
 
27
27
  export const runProgressCommand = createCompositeCommand({
28
28
  commandName: "progress",
29
- requiredArgs: ["discipline_id", "grade_id"],
29
+ requiredArgs: ["discipline_id", "level_id"],
30
30
  findEntities: (data, args, options) => {
31
31
  const discipline = data.disciplines.find((d) => d.id === args[0]);
32
- const grade = data.grades.find((g) => g.id === args[1]);
32
+ const level = data.levels.find((g) => g.id === args[1]);
33
33
  const track = options.track
34
34
  ? data.tracks.find((t) => t.id === options.track)
35
35
  : null;
36
36
 
37
- let targetGrade;
37
+ let targetLevel;
38
38
  if (options.compare) {
39
- targetGrade = data.grades.find((g) => g.id === options.compare);
40
- if (!targetGrade) {
41
- console.error(`Target grade not found: ${options.compare}`);
39
+ targetLevel = data.levels.find((g) => g.id === options.compare);
40
+ if (!targetLevel) {
41
+ console.error(`Target level not found: ${options.compare}`);
42
42
  process.exit(1);
43
43
  }
44
44
  } else {
45
- targetGrade = getDefaultTargetGrade(grade, data.grades);
46
- if (!targetGrade) {
47
- console.error("No next grade available for progression.");
45
+ targetLevel = getDefaultTargetLevel(level, data.levels);
46
+ if (!targetLevel) {
47
+ console.error("No next level available for progression.");
48
48
  process.exit(1);
49
49
  }
50
50
  }
51
51
 
52
- return { discipline, grade, track, targetGrade };
52
+ return { discipline, level, track, targetLevel };
53
53
  },
54
54
  validateEntities: (entities, _data, options) => {
55
55
  if (!entities.discipline) {
56
56
  return `Discipline not found`;
57
57
  }
58
- if (!entities.grade) {
59
- return `Grade not found`;
58
+ if (!entities.level) {
59
+ return `Level not found`;
60
60
  }
61
61
  if (options.track && !entities.track) {
62
62
  return `Track not found: ${options.track}`;
63
63
  }
64
- if (!entities.targetGrade) {
65
- return `Target grade not found`;
64
+ if (!entities.targetLevel) {
65
+ return `Target level not found`;
66
66
  }
67
67
  return null;
68
68
  },
69
69
  presenter: (entities, data) =>
70
70
  prepareProgressDetail({
71
71
  fromDiscipline: entities.discipline,
72
- fromGrade: entities.grade,
72
+ fromLevel: entities.level,
73
73
  fromTrack: entities.track,
74
74
  toDiscipline: entities.discipline,
75
- toGrade: entities.targetGrade,
75
+ toLevel: entities.targetLevel,
76
76
  toTrack: entities.track,
77
77
  skills: data.skills,
78
78
  behaviours: data.behaviours,
@@ -49,7 +49,7 @@ function showQuestionsSummary(data) {
49
49
  console.log(`\n❓ Questions\n`);
50
50
 
51
51
  // Skill questions by level
52
- const skillLevels = [
52
+ const skillProficiencies = [
53
53
  "awareness",
54
54
  "foundational",
55
55
  "working",
@@ -57,10 +57,10 @@ function showQuestionsSummary(data) {
57
57
  "expert",
58
58
  ];
59
59
  const roleTypes = ["professionalQuestions", "managementQuestions"];
60
- const skillRows = skillLevels.map((level) => {
60
+ const skillRows = skillProficiencies.map((level) => {
61
61
  let count = 0;
62
62
  for (const skill of skills) {
63
- const sq = questions.skillLevels?.[skill.id];
63
+ const sq = questions.skillProficiencies?.[skill.id];
64
64
  if (sq) {
65
65
  for (const roleType of roleTypes) {
66
66
  count += (sq[roleType]?.[level] || []).length;
@@ -30,7 +30,7 @@ export function createNavButton({ label, href, variant = "primary" }) {
30
30
  /**
31
31
  * Create a button to navigate to job builder with a parameter
32
32
  * @param {Object} options - Configuration options
33
- * @param {string} options.paramName - Parameter name (discipline, grade, track)
33
+ * @param {string} options.paramName - Parameter name (discipline, level, track)
34
34
  * @param {string} options.paramValue - Parameter value (the ID)
35
35
  * @param {string} [options.label] - Optional custom label
36
36
  * @returns {HTMLElement}
@@ -38,7 +38,7 @@ export function createNavButton({ label, href, variant = "primary" }) {
38
38
  export function createJobBuilderButton({ paramName, paramValue, label }) {
39
39
  const defaultLabels = {
40
40
  discipline: "Build Job with this Discipline →",
41
- grade: "Build Job at this Grade →",
41
+ level: "Build Job at this Level →",
42
42
  track: "Build Job with this Track →",
43
43
  };
44
44
 
@@ -52,7 +52,7 @@ export function createJobBuilderButton({ paramName, paramValue, label }) {
52
52
  /**
53
53
  * Create a button to navigate to interview prep with a parameter
54
54
  * @param {Object} options - Configuration options
55
- * @param {string} options.paramName - Parameter name (discipline, grade, track)
55
+ * @param {string} options.paramName - Parameter name (discipline, level, track)
56
56
  * @param {string} options.paramValue - Parameter value (the ID)
57
57
  * @param {string} [options.label] - Optional custom label
58
58
  * @returns {HTMLElement}