@forwardimpact/pathway 0.1.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 (227) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +104 -0
  3. package/app/commands/agent.js +430 -0
  4. package/app/commands/behaviour.js +61 -0
  5. package/app/commands/command-factory.js +211 -0
  6. package/app/commands/discipline.js +58 -0
  7. package/app/commands/driver.js +94 -0
  8. package/app/commands/grade.js +60 -0
  9. package/app/commands/index.js +20 -0
  10. package/app/commands/init.js +67 -0
  11. package/app/commands/interview.js +68 -0
  12. package/app/commands/job.js +157 -0
  13. package/app/commands/progress.js +77 -0
  14. package/app/commands/questions.js +179 -0
  15. package/app/commands/serve.js +143 -0
  16. package/app/commands/site.js +121 -0
  17. package/app/commands/skill.js +76 -0
  18. package/app/commands/stage.js +129 -0
  19. package/app/commands/track.js +70 -0
  20. package/app/components/action-buttons.js +66 -0
  21. package/app/components/behaviour-profile.js +53 -0
  22. package/app/components/builder.js +341 -0
  23. package/app/components/card.js +98 -0
  24. package/app/components/checklist.js +145 -0
  25. package/app/components/comparison-radar.js +237 -0
  26. package/app/components/detail.js +230 -0
  27. package/app/components/error-page.js +72 -0
  28. package/app/components/grid.js +109 -0
  29. package/app/components/list.js +120 -0
  30. package/app/components/modifier-table.js +142 -0
  31. package/app/components/nav.js +64 -0
  32. package/app/components/progression-table.js +320 -0
  33. package/app/components/radar-chart.js +102 -0
  34. package/app/components/skill-matrix.js +97 -0
  35. package/app/css/base.css +56 -0
  36. package/app/css/bundles/app.css +40 -0
  37. package/app/css/bundles/handout.css +43 -0
  38. package/app/css/bundles/slides.css +40 -0
  39. package/app/css/components/badges.css +215 -0
  40. package/app/css/components/buttons.css +101 -0
  41. package/app/css/components/forms.css +105 -0
  42. package/app/css/components/layout.css +209 -0
  43. package/app/css/components/nav.css +166 -0
  44. package/app/css/components/progress.css +166 -0
  45. package/app/css/components/states.css +82 -0
  46. package/app/css/components/surfaces.css +243 -0
  47. package/app/css/components/tables.css +362 -0
  48. package/app/css/components/typography.css +122 -0
  49. package/app/css/components/utilities.css +41 -0
  50. package/app/css/pages/agent-builder.css +391 -0
  51. package/app/css/pages/assessment-results.css +453 -0
  52. package/app/css/pages/detail.css +59 -0
  53. package/app/css/pages/interview-builder.css +148 -0
  54. package/app/css/pages/job-builder.css +134 -0
  55. package/app/css/pages/landing.css +92 -0
  56. package/app/css/pages/lifecycle.css +118 -0
  57. package/app/css/pages/progress-builder.css +274 -0
  58. package/app/css/pages/self-assessment.css +502 -0
  59. package/app/css/reset.css +50 -0
  60. package/app/css/tokens.css +153 -0
  61. package/app/css/views/handout.css +30 -0
  62. package/app/css/views/print.css +608 -0
  63. package/app/css/views/slide-animations.css +113 -0
  64. package/app/css/views/slide-base.css +330 -0
  65. package/app/css/views/slide-sections.css +597 -0
  66. package/app/css/views/slide-tables.css +275 -0
  67. package/app/formatters/agent/dom.js +540 -0
  68. package/app/formatters/agent/profile.js +133 -0
  69. package/app/formatters/agent/skill.js +58 -0
  70. package/app/formatters/behaviour/dom.js +91 -0
  71. package/app/formatters/behaviour/markdown.js +54 -0
  72. package/app/formatters/behaviour/shared.js +64 -0
  73. package/app/formatters/discipline/dom.js +187 -0
  74. package/app/formatters/discipline/markdown.js +87 -0
  75. package/app/formatters/discipline/shared.js +131 -0
  76. package/app/formatters/driver/dom.js +103 -0
  77. package/app/formatters/driver/shared.js +92 -0
  78. package/app/formatters/grade/dom.js +208 -0
  79. package/app/formatters/grade/markdown.js +94 -0
  80. package/app/formatters/grade/shared.js +86 -0
  81. package/app/formatters/index.js +50 -0
  82. package/app/formatters/interview/dom.js +97 -0
  83. package/app/formatters/interview/markdown.js +66 -0
  84. package/app/formatters/interview/shared.js +332 -0
  85. package/app/formatters/job/description.js +176 -0
  86. package/app/formatters/job/dom.js +411 -0
  87. package/app/formatters/job/markdown.js +102 -0
  88. package/app/formatters/progress/dom.js +135 -0
  89. package/app/formatters/progress/markdown.js +86 -0
  90. package/app/formatters/progress/shared.js +339 -0
  91. package/app/formatters/questions/json.js +43 -0
  92. package/app/formatters/questions/markdown.js +303 -0
  93. package/app/formatters/questions/shared.js +274 -0
  94. package/app/formatters/questions/yaml.js +76 -0
  95. package/app/formatters/shared.js +71 -0
  96. package/app/formatters/skill/dom.js +168 -0
  97. package/app/formatters/skill/markdown.js +109 -0
  98. package/app/formatters/skill/shared.js +125 -0
  99. package/app/formatters/stage/dom.js +135 -0
  100. package/app/formatters/stage/index.js +12 -0
  101. package/app/formatters/stage/shared.js +111 -0
  102. package/app/formatters/track/dom.js +128 -0
  103. package/app/formatters/track/markdown.js +105 -0
  104. package/app/formatters/track/shared.js +181 -0
  105. package/app/handout-main.js +421 -0
  106. package/app/handout.html +21 -0
  107. package/app/index.html +59 -0
  108. package/app/lib/card-mappers.js +173 -0
  109. package/app/lib/cli-output.js +270 -0
  110. package/app/lib/error-boundary.js +70 -0
  111. package/app/lib/errors.js +49 -0
  112. package/app/lib/form-controls.js +47 -0
  113. package/app/lib/job-cache.js +86 -0
  114. package/app/lib/markdown.js +114 -0
  115. package/app/lib/radar.js +866 -0
  116. package/app/lib/reactive.js +77 -0
  117. package/app/lib/render.js +212 -0
  118. package/app/lib/router-core.js +160 -0
  119. package/app/lib/router-pages.js +16 -0
  120. package/app/lib/router-slides.js +202 -0
  121. package/app/lib/state.js +148 -0
  122. package/app/lib/utils.js +14 -0
  123. package/app/lib/yaml-loader.js +327 -0
  124. package/app/main.js +213 -0
  125. package/app/model/agent.js +702 -0
  126. package/app/model/checklist.js +137 -0
  127. package/app/model/derivation.js +699 -0
  128. package/app/model/index-generator.js +71 -0
  129. package/app/model/interview.js +539 -0
  130. package/app/model/job.js +222 -0
  131. package/app/model/levels.js +591 -0
  132. package/app/model/loader.js +564 -0
  133. package/app/model/matching.js +858 -0
  134. package/app/model/modifiers.js +158 -0
  135. package/app/model/profile.js +266 -0
  136. package/app/model/progression.js +507 -0
  137. package/app/model/validation.js +1385 -0
  138. package/app/pages/agent-builder.js +823 -0
  139. package/app/pages/assessment-results.js +507 -0
  140. package/app/pages/behaviour.js +70 -0
  141. package/app/pages/discipline.js +71 -0
  142. package/app/pages/driver.js +106 -0
  143. package/app/pages/grade.js +117 -0
  144. package/app/pages/interview-builder.js +50 -0
  145. package/app/pages/interview.js +304 -0
  146. package/app/pages/job-builder.js +50 -0
  147. package/app/pages/job.js +58 -0
  148. package/app/pages/landing.js +305 -0
  149. package/app/pages/progress-builder.js +58 -0
  150. package/app/pages/progress.js +495 -0
  151. package/app/pages/self-assessment.js +729 -0
  152. package/app/pages/skill.js +113 -0
  153. package/app/pages/stage.js +231 -0
  154. package/app/pages/track.js +69 -0
  155. package/app/slide-main.js +360 -0
  156. package/app/slides/behaviour.js +38 -0
  157. package/app/slides/chapter.js +82 -0
  158. package/app/slides/discipline.js +40 -0
  159. package/app/slides/driver.js +39 -0
  160. package/app/slides/grade.js +32 -0
  161. package/app/slides/index.js +198 -0
  162. package/app/slides/interview.js +58 -0
  163. package/app/slides/job.js +55 -0
  164. package/app/slides/overview.js +126 -0
  165. package/app/slides/progress.js +83 -0
  166. package/app/slides/skill.js +40 -0
  167. package/app/slides/track.js +39 -0
  168. package/app/slides.html +56 -0
  169. package/app/types.js +147 -0
  170. package/bin/pathway.js +489 -0
  171. package/examples/agents/.claude/skills/architecture-design/SKILL.md +88 -0
  172. package/examples/agents/.claude/skills/cloud-platforms/SKILL.md +90 -0
  173. package/examples/agents/.claude/skills/code-quality-review/SKILL.md +67 -0
  174. package/examples/agents/.claude/skills/data-modeling/SKILL.md +99 -0
  175. package/examples/agents/.claude/skills/developer-experience/SKILL.md +99 -0
  176. package/examples/agents/.claude/skills/devops-cicd/SKILL.md +96 -0
  177. package/examples/agents/.claude/skills/full-stack-development/SKILL.md +90 -0
  178. package/examples/agents/.claude/skills/knowledge-management/SKILL.md +100 -0
  179. package/examples/agents/.claude/skills/pattern-generalization/SKILL.md +102 -0
  180. package/examples/agents/.claude/skills/sre-practices/SKILL.md +117 -0
  181. package/examples/agents/.claude/skills/technical-debt-management/SKILL.md +123 -0
  182. package/examples/agents/.claude/skills/technical-writing/SKILL.md +129 -0
  183. package/examples/agents/.github/agents/se-platform-code.agent.md +181 -0
  184. package/examples/agents/.github/agents/se-platform-plan.agent.md +178 -0
  185. package/examples/agents/.github/agents/se-platform-review.agent.md +113 -0
  186. package/examples/agents/.vscode/settings.json +8 -0
  187. package/examples/behaviours/_index.yaml +8 -0
  188. package/examples/behaviours/outcome_ownership.yaml +44 -0
  189. package/examples/behaviours/polymathic_knowledge.yaml +42 -0
  190. package/examples/behaviours/precise_communication.yaml +40 -0
  191. package/examples/behaviours/relentless_curiosity.yaml +38 -0
  192. package/examples/behaviours/systems_thinking.yaml +41 -0
  193. package/examples/capabilities/_index.yaml +8 -0
  194. package/examples/capabilities/business.yaml +251 -0
  195. package/examples/capabilities/delivery.yaml +352 -0
  196. package/examples/capabilities/people.yaml +100 -0
  197. package/examples/capabilities/reliability.yaml +318 -0
  198. package/examples/capabilities/scale.yaml +394 -0
  199. package/examples/disciplines/_index.yaml +5 -0
  200. package/examples/disciplines/data_engineering.yaml +76 -0
  201. package/examples/disciplines/software_engineering.yaml +76 -0
  202. package/examples/drivers.yaml +205 -0
  203. package/examples/framework.yaml +58 -0
  204. package/examples/grades.yaml +118 -0
  205. package/examples/questions/behaviours/outcome_ownership.yaml +52 -0
  206. package/examples/questions/behaviours/polymathic_knowledge.yaml +48 -0
  207. package/examples/questions/behaviours/precise_communication.yaml +55 -0
  208. package/examples/questions/behaviours/relentless_curiosity.yaml +51 -0
  209. package/examples/questions/behaviours/systems_thinking.yaml +53 -0
  210. package/examples/questions/skills/architecture_design.yaml +54 -0
  211. package/examples/questions/skills/cloud_platforms.yaml +48 -0
  212. package/examples/questions/skills/code_quality.yaml +49 -0
  213. package/examples/questions/skills/data_modeling.yaml +46 -0
  214. package/examples/questions/skills/devops.yaml +47 -0
  215. package/examples/questions/skills/full_stack_development.yaml +48 -0
  216. package/examples/questions/skills/sre_practices.yaml +44 -0
  217. package/examples/questions/skills/stakeholder_management.yaml +49 -0
  218. package/examples/questions/skills/team_collaboration.yaml +43 -0
  219. package/examples/questions/skills/technical_writing.yaml +43 -0
  220. package/examples/self-assessments.yaml +66 -0
  221. package/examples/stages.yaml +76 -0
  222. package/examples/tracks/_index.yaml +6 -0
  223. package/examples/tracks/manager.yaml +53 -0
  224. package/examples/tracks/platform.yaml +54 -0
  225. package/examples/tracks/sre.yaml +58 -0
  226. package/examples/vscode-settings.yaml +22 -0
  227. package/package.json +68 -0
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Discipline CLI Command
3
+ *
4
+ * Handles discipline summary, listing, and detail display in the terminal.
5
+ *
6
+ * Usage:
7
+ * npx pathway discipline # Summary with stats
8
+ * npx pathway discipline --list # IDs only (for piping)
9
+ * npx pathway discipline <id> # Detail view
10
+ * npx pathway discipline --validate # Validation checks
11
+ */
12
+
13
+ import { createEntityCommand } from "./command-factory.js";
14
+ import { disciplineToMarkdown } from "../formatters/discipline/markdown.js";
15
+ import { formatTable } from "../lib/cli-output.js";
16
+
17
+ /**
18
+ * Format discipline summary output
19
+ * @param {Array} disciplines - Raw discipline entities
20
+ */
21
+ function formatSummary(disciplines) {
22
+ console.log(`\n📋 Disciplines\n`);
23
+
24
+ const rows = disciplines.map((d) => [
25
+ d.id,
26
+ d.coreSkills?.length || 0,
27
+ d.supportingSkills?.length || 0,
28
+ d.broadSkills?.length || 0,
29
+ ]);
30
+
31
+ console.log(formatTable(["ID", "Core", "Supporting", "Broad"], rows));
32
+ console.log(`\nTotal: ${disciplines.length} disciplines`);
33
+ console.log(`\nRun 'npx pathway discipline --list' for IDs`);
34
+ console.log(`Run 'npx pathway discipline <id>' for details\n`);
35
+ }
36
+
37
+ /**
38
+ * Format discipline detail output
39
+ * @param {Object} viewAndContext - Contains discipline entity and context
40
+ */
41
+ function formatDetail(viewAndContext) {
42
+ const { discipline, skills, behaviours } = viewAndContext;
43
+ console.log(disciplineToMarkdown(discipline, { skills, behaviours }));
44
+ }
45
+
46
+ export const runDisciplineCommand = createEntityCommand({
47
+ entityName: "discipline",
48
+ pluralName: "disciplines",
49
+ findEntity: (data, id) => data.disciplines.find((d) => d.id === id),
50
+ presentDetail: (entity, data) => ({
51
+ discipline: entity,
52
+ skills: data.skills,
53
+ behaviours: data.behaviours,
54
+ }),
55
+ formatSummary,
56
+ formatDetail,
57
+ emoji: "📋",
58
+ });
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Driver CLI Command
3
+ *
4
+ * Handles driver summary, listing, and detail display in the terminal.
5
+ *
6
+ * Usage:
7
+ * npx pathway driver # Summary with stats
8
+ * npx pathway driver --list # IDs only (for piping)
9
+ * npx pathway driver <id> # Detail view
10
+ * npx pathway driver --validate # Validation checks
11
+ */
12
+
13
+ import { createEntityCommand } from "./command-factory.js";
14
+ import { prepareDriverDetail } from "../formatters/driver/shared.js";
15
+ import { formatTable } from "../lib/cli-output.js";
16
+ import {
17
+ formatHeader,
18
+ formatSubheader,
19
+ formatBullet,
20
+ } from "../lib/cli-output.js";
21
+ import { getConceptEmoji } from "../model/levels.js";
22
+
23
+ /**
24
+ * Format driver summary output
25
+ * @param {Array} drivers - Raw driver entities
26
+ * @param {Object} data - Full data context
27
+ */
28
+ function formatSummary(drivers, data) {
29
+ const { skills, behaviours, framework } = data;
30
+ const emoji = framework ? getConceptEmoji(framework, "driver") : "🎯";
31
+
32
+ console.log(`\n${emoji} Drivers\n`);
33
+
34
+ const rows = drivers.map((d) => {
35
+ const contributingSkills = skills.filter((s) =>
36
+ d.contributingSkills?.includes(s.id),
37
+ ).length;
38
+ const contributingBehaviours = behaviours.filter((b) =>
39
+ d.contributingBehaviours?.includes(b.id),
40
+ ).length;
41
+ return [d.id, d.name, contributingSkills, contributingBehaviours];
42
+ });
43
+
44
+ console.log(formatTable(["ID", "Name", "Skills", "Behaviours"], rows));
45
+ console.log(`\nTotal: ${drivers.length} drivers`);
46
+ console.log(`\nRun 'npx pathway driver --list' for IDs`);
47
+ console.log(`Run 'npx pathway driver <id>' for details\n`);
48
+ }
49
+
50
+ /**
51
+ * Format driver detail output
52
+ * @param {Object} viewAndContext - Contains driver entity and context
53
+ * @param {Object} framework - Framework config
54
+ */
55
+ function formatDetail(viewAndContext, framework) {
56
+ const { driver, skills, behaviours } = viewAndContext;
57
+ const view = prepareDriverDetail(driver, { skills, behaviours });
58
+ const emoji = framework ? getConceptEmoji(framework, "driver") : "🎯";
59
+
60
+ console.log(formatHeader(`\n${emoji} ${view.name}\n`));
61
+ console.log(`${view.description}\n`);
62
+
63
+ // Contributing skills
64
+ if (view.contributingSkills.length > 0) {
65
+ console.log(formatSubheader("Contributing Skills\n"));
66
+ for (const s of view.contributingSkills) {
67
+ console.log(formatBullet(s.name, 1));
68
+ }
69
+ console.log();
70
+ }
71
+
72
+ // Contributing behaviours
73
+ if (view.contributingBehaviours.length > 0) {
74
+ console.log(formatSubheader("Contributing Behaviours\n"));
75
+ for (const b of view.contributingBehaviours) {
76
+ console.log(formatBullet(b.name, 1));
77
+ }
78
+ console.log();
79
+ }
80
+ }
81
+
82
+ export const runDriverCommand = createEntityCommand({
83
+ entityName: "driver",
84
+ pluralName: "drivers",
85
+ findEntity: (data, id) => data.drivers.find((d) => d.id === id),
86
+ presentDetail: (entity, data) => ({
87
+ driver: entity,
88
+ skills: data.skills,
89
+ behaviours: data.behaviours,
90
+ }),
91
+ formatSummary,
92
+ formatDetail,
93
+ emoji: "🎯",
94
+ });
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Grade CLI Command
3
+ *
4
+ * Handles grade summary, listing, and detail display in the terminal.
5
+ *
6
+ * Usage:
7
+ * npx pathway grade # Summary with stats
8
+ * npx pathway grade --list # IDs only (for piping)
9
+ * npx pathway grade <id> # Detail view
10
+ * npx pathway grade --validate # Validation checks
11
+ */
12
+
13
+ import { createEntityCommand } from "./command-factory.js";
14
+ import { gradeToMarkdown } from "../formatters/grade/markdown.js";
15
+ import { formatTable } from "../lib/cli-output.js";
16
+ import { getConceptEmoji } from "../model/levels.js";
17
+ import { capitalize } from "../formatters/shared.js";
18
+
19
+ /**
20
+ * Format grade summary output
21
+ * @param {Array} grades - Raw grade entities
22
+ * @param {Object} data - Full data context
23
+ */
24
+ function formatSummary(grades, data) {
25
+ const { framework } = data;
26
+ const emoji = framework ? getConceptEmoji(framework, "grade") : "📊";
27
+
28
+ console.log(`\n${emoji} Grades\n`);
29
+
30
+ const rows = grades.map((g) => [
31
+ g.id,
32
+ g.displayName || g.id,
33
+ g.typicalExperienceRange || "-",
34
+ capitalize(g.baseSkillLevels?.primary || "-"),
35
+ ]);
36
+
37
+ console.log(formatTable(["ID", "Name", "Experience", "Primary Level"], rows));
38
+ console.log(`\nTotal: ${grades.length} grades`);
39
+ console.log(`\nRun 'npx pathway grade --list' for IDs`);
40
+ console.log(`Run 'npx pathway grade <id>' for details\n`);
41
+ }
42
+
43
+ /**
44
+ * Format grade detail output
45
+ * @param {Object} grade - Raw grade entity
46
+ * @param {Object} framework - Framework config
47
+ */
48
+ function formatDetail(grade, framework) {
49
+ console.log(gradeToMarkdown(grade, framework));
50
+ }
51
+
52
+ export const runGradeCommand = createEntityCommand({
53
+ entityName: "grade",
54
+ pluralName: "grades",
55
+ findEntity: (data, id) => data.grades.find((g) => g.id === id),
56
+ presentDetail: (entity) => entity,
57
+ formatSummary,
58
+ formatDetail,
59
+ emoji: "📊",
60
+ });
@@ -0,0 +1,20 @@
1
+ /**
2
+ * CLI Commands Index
3
+ *
4
+ * Re-exports all command handlers for convenient importing.
5
+ */
6
+
7
+ export { runSkillCommand } from "./skill.js";
8
+ export { runBehaviourCommand } from "./behaviour.js";
9
+ export { runDriverCommand } from "./driver.js";
10
+ export { runDisciplineCommand } from "./discipline.js";
11
+ export { runGradeCommand } from "./grade.js";
12
+ export { runTrackCommand } from "./track.js";
13
+ export { runStageCommand } from "./stage.js";
14
+ export { runJobCommand } from "./job.js";
15
+ export { runInterviewCommand } from "./interview.js";
16
+ export { runProgressCommand } from "./progress.js";
17
+ export { runQuestionsCommand } from "./questions.js";
18
+ export { runServeCommand } from "./serve.js";
19
+ export { runInitCommand } from "./init.js";
20
+ export { runSiteCommand } from "./site.js";
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Init Command
3
+ *
4
+ * Initializes a new Engineering Pathway project by copying example data.
5
+ */
6
+
7
+ import { cp, access } from "fs/promises";
8
+ import { join, dirname } from "path";
9
+ import { fileURLToPath } from "url";
10
+
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = dirname(__filename);
13
+ const examplesDir = join(__dirname, "..", "..", "examples");
14
+
15
+ /**
16
+ * Run the init command
17
+ * @param {Object} params - Command parameters
18
+ * @param {Object} params.options - Command options
19
+ */
20
+ export async function runInitCommand({ options }) {
21
+ const targetPath = options.path || process.cwd();
22
+ const dataDir = join(targetPath, "data");
23
+
24
+ // Check if data/ already exists
25
+ try {
26
+ await access(dataDir);
27
+ console.error("Error: ./data/ already exists.");
28
+ console.error("Remove it first or use a different directory.");
29
+ process.exit(1);
30
+ } catch {
31
+ // Directory doesn't exist, proceed
32
+ }
33
+
34
+ // Check if examples directory exists
35
+ try {
36
+ await access(examplesDir);
37
+ } catch {
38
+ console.error("Error: Examples directory not found in package.");
39
+ console.error("This may indicate a corrupted package installation.");
40
+ process.exit(1);
41
+ }
42
+
43
+ // Copy example data
44
+ console.log("Creating ./data/ with example data...\n");
45
+ await cp(examplesDir, dataDir, { recursive: true });
46
+
47
+ console.log(`✅ Created ./data/ with example data.
48
+
49
+ Next steps:
50
+ 1. Edit data files to match your organization
51
+ 2. npx pathway --validate
52
+ 3. npx pathway serve
53
+
54
+ Data structure:
55
+ data/
56
+ ├── framework.yaml # Framework metadata
57
+ ├── grades.yaml # Career levels
58
+ ├── stages.yaml # Lifecycle stages
59
+ ├── drivers.yaml # Business drivers
60
+ ├── capabilities.yaml # Capability areas
61
+ ├── disciplines/ # Engineering disciplines
62
+ ├── tracks/ # Role tracks
63
+ ├── skills/ # Technical skills
64
+ ├── behaviours/ # Behavioural expectations
65
+ └── questions/ # Interview questions
66
+ `);
67
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Interview CLI Command
3
+ *
4
+ * Generates and displays interview questions in the terminal.
5
+ */
6
+
7
+ import { createCompositeCommand } from "./command-factory.js";
8
+ import {
9
+ prepareInterviewDetail,
10
+ INTERVIEW_TYPES,
11
+ } from "../formatters/interview/shared.js";
12
+ import { interviewToMarkdown } from "../formatters/interview/markdown.js";
13
+
14
+ /**
15
+ * Format interview output
16
+ * @param {Object} view - Presenter view
17
+ * @param {Object} options - Options including framework
18
+ */
19
+ function formatInterview(view, options) {
20
+ console.log(interviewToMarkdown(view, { framework: options.framework }));
21
+ }
22
+
23
+ export const runInterviewCommand = createCompositeCommand({
24
+ commandName: "interview",
25
+ requiredArgs: ["discipline_id", "track_id", "grade_id"],
26
+ findEntities: (data, args, options) => {
27
+ const interviewType = options.type || "full";
28
+
29
+ if (!INTERVIEW_TYPES[interviewType]) {
30
+ console.error(`Unknown interview type: ${interviewType}`);
31
+ console.error("Available types: full, short, behaviour");
32
+ process.exit(1);
33
+ }
34
+
35
+ return {
36
+ discipline: data.disciplines.find((d) => d.id === args[0]),
37
+ track: data.tracks.find((t) => t.id === args[1]),
38
+ grade: data.grades.find((g) => g.id === args[2]),
39
+ interviewType,
40
+ };
41
+ },
42
+ validateEntities: (entities, _data) => {
43
+ if (!entities.discipline) {
44
+ return `Discipline not found: ${entities.discipline}`;
45
+ }
46
+ if (!entities.grade) {
47
+ return `Grade not found: ${entities.grade}`;
48
+ }
49
+ if (!entities.track) {
50
+ return `Track not found: ${entities.track}`;
51
+ }
52
+ return null;
53
+ },
54
+ presenter: (entities, data, _options) =>
55
+ prepareInterviewDetail({
56
+ discipline: entities.discipline,
57
+ grade: entities.grade,
58
+ track: entities.track,
59
+ skills: data.skills,
60
+ behaviours: data.behaviours,
61
+ questions: data.questions,
62
+ interviewType: entities.interviewType,
63
+ }),
64
+ formatter: (view, options, data) =>
65
+ formatInterview(view, { ...options, framework: data.framework }),
66
+ usageExample:
67
+ "npx pathway interview software_engineering platform L4 --type=short",
68
+ });
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Job CLI Command
3
+ *
4
+ * Generates and displays job definitions in the terminal.
5
+ *
6
+ * Usage:
7
+ * npx pathway job # Summary with stats
8
+ * npx pathway job --list # All valid combinations (for piping)
9
+ * npx pathway job <discipline> <track> <grade> # Detail view
10
+ * npx pathway job se platform L3 --checklist=code_to_review # Show checklist for handoff
11
+ * npx pathway job --validate # Validation checks
12
+ */
13
+
14
+ import { prepareJobDetail } from "../model/job.js";
15
+ import { jobToMarkdown } from "../formatters/job/markdown.js";
16
+ import { generateAllJobs } from "../model/derivation.js";
17
+ import { formatTable } from "../lib/cli-output.js";
18
+ import {
19
+ deriveChecklist,
20
+ formatChecklistMarkdown,
21
+ } from "../model/checklist.js";
22
+
23
+ /**
24
+ * Format job output
25
+ * @param {Object} view - Presenter view
26
+ * @param {Object} _options - Command options
27
+ * @param {Object} entities - Original entities
28
+ */
29
+ function formatJob(view, _options, entities) {
30
+ console.log(jobToMarkdown(view, entities));
31
+ }
32
+
33
+ /**
34
+ * Run job command
35
+ * @param {Object} params
36
+ * @param {Object} params.data - All loaded data
37
+ * @param {string[]} params.args - Command arguments
38
+ * @param {Object} params.options - Command options
39
+ */
40
+ export async function runJobCommand({ data, args, options }) {
41
+ const jobs = generateAllJobs({
42
+ disciplines: data.disciplines,
43
+ grades: data.grades,
44
+ tracks: data.tracks,
45
+ skills: data.skills,
46
+ behaviours: data.behaviours,
47
+ validationRules: data.framework.validationRules,
48
+ });
49
+
50
+ // --list: Output clean lines for piping
51
+ if (options.list) {
52
+ for (const job of jobs) {
53
+ console.log(`${job.discipline.id} ${job.track.id} ${job.grade.id}`);
54
+ }
55
+ return;
56
+ }
57
+
58
+ // No args: Show summary
59
+ if (args.length === 0) {
60
+ console.log(`\n💼 Jobs\n`);
61
+
62
+ // Count by discipline
63
+ const byDiscipline = {};
64
+ for (const job of jobs) {
65
+ byDiscipline[job.discipline.id] =
66
+ (byDiscipline[job.discipline.id] || 0) + 1;
67
+ }
68
+
69
+ const rows = Object.entries(byDiscipline).map(([id, count]) => [id, count]);
70
+ console.log(formatTable(["Discipline", "Combinations"], rows));
71
+ console.log(`\nTotal: ${jobs.length} valid job combinations`);
72
+ console.log(`\nRun 'npx pathway job --list' for all combinations`);
73
+ console.log(
74
+ `Run 'npx pathway job <discipline> <track> <grade>' for details\n`,
75
+ );
76
+ return;
77
+ }
78
+
79
+ // Handle job detail view
80
+ if (args.length < 3) {
81
+ console.error("Usage: npx pathway job <discipline> <track> <grade>");
82
+ console.error(" npx pathway job --list");
83
+ console.error("Example: npx pathway job software_engineering platform L4");
84
+ process.exit(1);
85
+ }
86
+
87
+ const discipline = data.disciplines.find((d) => d.id === args[0]);
88
+ const track = data.tracks.find((t) => t.id === args[1]);
89
+ const grade = data.grades.find((g) => g.id === args[2]);
90
+
91
+ if (!discipline) {
92
+ console.error(`Discipline not found: ${args[0]}`);
93
+ console.error(`Available: ${data.disciplines.map((d) => d.id).join(", ")}`);
94
+ process.exit(1);
95
+ }
96
+
97
+ if (!track) {
98
+ console.error(`Track not found: ${args[1]}`);
99
+ console.error(`Available: ${data.tracks.map((t) => t.id).join(", ")}`);
100
+ process.exit(1);
101
+ }
102
+
103
+ if (!grade) {
104
+ console.error(`Grade not found: ${args[2]}`);
105
+ console.error(`Available: ${data.grades.map((g) => g.id).join(", ")}`);
106
+ process.exit(1);
107
+ }
108
+
109
+ const view = prepareJobDetail({
110
+ discipline,
111
+ grade,
112
+ track,
113
+ skills: data.skills,
114
+ behaviours: data.behaviours,
115
+ drivers: data.drivers,
116
+ capabilities: data.capabilities,
117
+ });
118
+
119
+ if (!view) {
120
+ console.error("Failed to generate job output.");
121
+ process.exit(1);
122
+ }
123
+
124
+ if (options.json) {
125
+ console.log(JSON.stringify(view, null, 2));
126
+ return;
127
+ }
128
+
129
+ // --checklist: Show checklist for a specific handoff
130
+ if (options.checklist) {
131
+ const validHandoffs = ["plan_to_code", "code_to_review"];
132
+ if (!validHandoffs.includes(options.checklist)) {
133
+ console.error(`Invalid handoff: ${options.checklist}`);
134
+ console.error(`Available: ${validHandoffs.join(", ")}`);
135
+ process.exit(1);
136
+ }
137
+
138
+ const checklist = deriveChecklist({
139
+ handoff: options.checklist,
140
+ skillMatrix: view.skillMatrix,
141
+ capabilities: data.capabilities,
142
+ });
143
+
144
+ if (checklist.length === 0) {
145
+ console.log(`\nNo checklist items for ${options.checklist}\n`);
146
+ return;
147
+ }
148
+
149
+ const handoffLabel = options.checklist.replace(/_/g, " → ");
150
+ console.log(`\n# ${view.title} — ${handoffLabel}\n`);
151
+ console.log(formatChecklistMarkdown(checklist));
152
+ console.log("");
153
+ return;
154
+ }
155
+
156
+ formatJob(view, options, { discipline, grade, track });
157
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Progress CLI Command
3
+ *
4
+ * Shows career progression analysis in the terminal.
5
+ */
6
+
7
+ import { createCompositeCommand } from "./command-factory.js";
8
+ import {
9
+ prepareProgressDetail,
10
+ getDefaultTargetGrade,
11
+ } from "../formatters/progress/shared.js";
12
+ import { progressToMarkdown } from "../formatters/progress/markdown.js";
13
+
14
+ /**
15
+ * Format progress output
16
+ * @param {Object} view - Presenter view
17
+ */
18
+ function formatProgress(view) {
19
+ console.log(progressToMarkdown(view));
20
+ }
21
+
22
+ export const runProgressCommand = createCompositeCommand({
23
+ commandName: "progress",
24
+ requiredArgs: ["discipline_id", "track_id", "grade_id"],
25
+ findEntities: (data, args, options) => {
26
+ const discipline = data.disciplines.find((d) => d.id === args[0]);
27
+ const track = data.tracks.find((t) => t.id === args[1]);
28
+ const grade = data.grades.find((g) => g.id === args[2]);
29
+
30
+ let targetGrade;
31
+ if (options.compare) {
32
+ targetGrade = data.grades.find((g) => g.id === options.compare);
33
+ if (!targetGrade) {
34
+ console.error(`Target grade not found: ${options.compare}`);
35
+ process.exit(1);
36
+ }
37
+ } else {
38
+ targetGrade = getDefaultTargetGrade(grade, data.grades);
39
+ if (!targetGrade) {
40
+ console.error("No next grade available for progression.");
41
+ process.exit(1);
42
+ }
43
+ }
44
+
45
+ return { discipline, grade, track, targetGrade };
46
+ },
47
+ validateEntities: (entities, _data) => {
48
+ if (!entities.discipline) {
49
+ return `Discipline not found`;
50
+ }
51
+ if (!entities.grade) {
52
+ return `Grade not found`;
53
+ }
54
+ if (!entities.track) {
55
+ return `Track not found`;
56
+ }
57
+ if (!entities.targetGrade) {
58
+ return `Target grade not found`;
59
+ }
60
+ return null;
61
+ },
62
+ presenter: (entities, data) =>
63
+ prepareProgressDetail({
64
+ fromDiscipline: entities.discipline,
65
+ fromGrade: entities.grade,
66
+ fromTrack: entities.track,
67
+ toDiscipline: entities.discipline,
68
+ toGrade: entities.targetGrade,
69
+ toTrack: entities.track,
70
+ skills: data.skills,
71
+ behaviours: data.behaviours,
72
+ capabilities: data.capabilities,
73
+ }),
74
+ formatter: formatProgress,
75
+ usageExample:
76
+ "npx pathway progress software_engineering platform L3 --compare=L4",
77
+ });