@forwardimpact/pathway 0.3.0 → 0.5.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/app/commands/agent.js +1 -1
- package/app/commands/behaviour.js +1 -1
- package/app/commands/command-factory.js +2 -2
- package/app/commands/discipline.js +1 -1
- package/app/commands/driver.js +1 -1
- package/app/commands/grade.js +1 -1
- package/app/commands/index.js +4 -3
- package/app/commands/serve.js +2 -2
- package/app/commands/site.js +22 -2
- package/app/commands/skill.js +57 -3
- package/app/commands/stage.js +1 -1
- package/app/commands/tool.js +112 -0
- package/app/commands/track.js +1 -1
- package/app/components/card.js +11 -1
- package/app/components/checklist.js +6 -4
- package/app/components/code-display.js +153 -0
- package/app/components/markdown-textarea.js +153 -0
- package/app/css/bundles/app.css +14 -0
- package/app/css/components/badges.css +15 -8
- package/app/css/components/forms.css +55 -0
- package/app/css/components/layout.css +12 -0
- package/app/css/components/surfaces.css +71 -3
- package/app/css/components/typography.css +1 -2
- package/app/css/pages/agent-builder.css +11 -102
- package/app/css/pages/detail.css +60 -0
- package/app/css/pages/job-builder.css +0 -42
- package/app/css/tokens.css +3 -0
- package/app/formatters/agent/dom.js +26 -71
- package/app/formatters/agent/profile.js +67 -10
- package/app/formatters/agent/skill.js +48 -6
- package/app/formatters/grade/dom.js +6 -6
- package/app/formatters/job/description.js +21 -16
- package/app/formatters/job/dom.js +9 -70
- package/app/formatters/json-ld.js +1 -1
- package/app/formatters/shared.js +58 -0
- package/app/formatters/skill/dom.js +70 -3
- package/app/formatters/skill/markdown.js +18 -0
- package/app/formatters/skill/shared.js +14 -4
- package/app/formatters/stage/microdata.js +2 -2
- package/app/formatters/stage/shared.js +3 -3
- package/app/formatters/tool/shared.js +78 -0
- package/app/handout-main.js +19 -18
- package/app/index.html +16 -3
- package/app/lib/card-mappers.js +91 -17
- package/app/lib/render.js +4 -0
- package/app/lib/yaml-loader.js +12 -1
- package/app/main.js +4 -0
- package/app/model/agent.js +47 -23
- package/app/model/checklist.js +2 -2
- package/app/model/derivation.js +5 -5
- package/app/model/levels.js +4 -2
- package/app/model/loader.js +12 -1
- package/app/model/validation.js +77 -11
- package/app/pages/agent-builder.js +121 -77
- package/app/pages/landing.js +35 -15
- package/app/pages/self-assessment.js +7 -5
- package/app/pages/skill.js +5 -17
- package/app/pages/stage.js +12 -8
- package/app/pages/tool.js +50 -0
- package/app/slide-main.js +1 -1
- package/app/slides/chapter.js +8 -8
- package/app/slides/index.js +26 -26
- package/app/slides/overview.js +8 -8
- package/app/slides/skill.js +1 -0
- package/bin/pathway.js +31 -16
- package/examples/capabilities/business.yaml +18 -18
- package/examples/capabilities/delivery.yaml +54 -37
- package/examples/capabilities/people.yaml +1 -1
- package/examples/capabilities/reliability.yaml +130 -115
- package/examples/capabilities/scale.yaml +39 -37
- package/examples/disciplines/engineering_management.yaml +1 -1
- package/examples/framework.yaml +21 -9
- package/examples/grades.yaml +5 -7
- package/examples/self-assessments.yaml +1 -1
- package/examples/stages.yaml +18 -10
- package/package.json +2 -1
- package/templates/agent.template.md +47 -17
- package/templates/job.template.md +8 -8
- package/templates/skill.template.md +33 -11
- package/examples/agents/.claude/skills/architecture-design/SKILL.md +0 -130
- package/examples/agents/.claude/skills/cloud-platforms/SKILL.md +0 -131
- package/examples/agents/.claude/skills/code-quality-review/SKILL.md +0 -108
- package/examples/agents/.claude/skills/devops-cicd/SKILL.md +0 -142
- package/examples/agents/.claude/skills/full-stack-development/SKILL.md +0 -134
- package/examples/agents/.claude/skills/sre-practices/SKILL.md +0 -163
- package/examples/agents/.claude/skills/technical-debt-management/SKILL.md +0 -164
- package/examples/agents/.github/agents/se-platform-code.agent.md +0 -132
- package/examples/agents/.github/agents/se-platform-plan.agent.md +0 -131
- package/examples/agents/.github/agents/se-platform-review.agent.md +0 -136
- package/examples/agents/.vscode/settings.json +0 -8
package/app/pages/landing.js
CHANGED
|
@@ -7,6 +7,7 @@ import { getState } from "../lib/state.js";
|
|
|
7
7
|
import { createStatCard } from "../components/card.js";
|
|
8
8
|
import { groupSkillsByCapability, getConceptEmoji } from "../model/levels.js";
|
|
9
9
|
import { getStageEmoji } from "../formatters/stage/shared.js";
|
|
10
|
+
import { aggregateTools } from "../formatters/tool/shared.js";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Create lifecycle flow visualization for landing page
|
|
@@ -43,6 +44,7 @@ export function renderLanding() {
|
|
|
43
44
|
// Calculate stats using centralized capability ordering
|
|
44
45
|
const skillsByCapability = groupSkillsByCapability(data.skills);
|
|
45
46
|
const capabilityCount = Object.keys(skillsByCapability).length;
|
|
47
|
+
const tools = aggregateTools(data.skills);
|
|
46
48
|
|
|
47
49
|
const page = div(
|
|
48
50
|
{ className: "landing-page" },
|
|
@@ -51,7 +53,7 @@ export function renderLanding() {
|
|
|
51
53
|
{ className: "landing-hero" },
|
|
52
54
|
div(
|
|
53
55
|
{ className: "hero-title-wrapper" },
|
|
54
|
-
h1({}, `${framework.
|
|
56
|
+
h1({}, `${framework.emojiIcon} ${framework.title}`),
|
|
55
57
|
span({ className: "brand-tag brand-tag-hero" }, framework.tag),
|
|
56
58
|
),
|
|
57
59
|
p({}, framework.description.trim()),
|
|
@@ -83,7 +85,7 @@ export function renderLanding() {
|
|
|
83
85
|
|
|
84
86
|
// Stats grid
|
|
85
87
|
div(
|
|
86
|
-
{ className: "grid grid-
|
|
88
|
+
{ className: "grid grid-4" },
|
|
87
89
|
createStatCard({
|
|
88
90
|
value: data.disciplines.length,
|
|
89
91
|
label: "Disciplines",
|
|
@@ -99,28 +101,41 @@ export function renderLanding() {
|
|
|
99
101
|
label: "Tracks",
|
|
100
102
|
href: "/track",
|
|
101
103
|
}),
|
|
104
|
+
createStatCard({
|
|
105
|
+
value: data.behaviours.length,
|
|
106
|
+
label: "Behaviours",
|
|
107
|
+
href: "/behaviour",
|
|
108
|
+
}),
|
|
102
109
|
createStatCard({
|
|
103
110
|
value: data.skills.length,
|
|
104
111
|
label: "Skills",
|
|
105
112
|
href: "/skill",
|
|
106
113
|
}),
|
|
107
114
|
createStatCard({
|
|
108
|
-
value: data.
|
|
109
|
-
label: "
|
|
110
|
-
href: "/
|
|
115
|
+
value: data.drivers.length,
|
|
116
|
+
label: "Drivers",
|
|
117
|
+
href: "/driver",
|
|
111
118
|
}),
|
|
112
119
|
createStatCard({
|
|
113
120
|
value: stages.length,
|
|
114
121
|
label: "Stages",
|
|
115
122
|
href: "/stage",
|
|
116
123
|
}),
|
|
124
|
+
createStatCard({
|
|
125
|
+
value: tools.length,
|
|
126
|
+
label: "Tools",
|
|
127
|
+
href: "/tool",
|
|
128
|
+
}),
|
|
117
129
|
),
|
|
118
130
|
|
|
119
131
|
// Lifecycle flow visualization
|
|
120
132
|
stages.length > 0
|
|
121
133
|
? div(
|
|
122
134
|
{ className: "section section-detail" },
|
|
123
|
-
h2(
|
|
135
|
+
h2(
|
|
136
|
+
{ className: "section-title" },
|
|
137
|
+
`${getConceptEmoji(framework, "stage")} Engineering Lifecycle`,
|
|
138
|
+
),
|
|
124
139
|
p(
|
|
125
140
|
{ className: "text-muted", style: "margin-bottom: 1rem" },
|
|
126
141
|
"The three stages of engineering work, from planning through review.",
|
|
@@ -134,7 +149,7 @@ export function renderLanding() {
|
|
|
134
149
|
{ className: "section section-detail" },
|
|
135
150
|
h2({ className: "section-title" }, "Explore the Framework"),
|
|
136
151
|
div(
|
|
137
|
-
{ className: "grid grid-
|
|
152
|
+
{ className: "grid grid-4" },
|
|
138
153
|
createQuickLinkCard(
|
|
139
154
|
`${getConceptEmoji(framework, "discipline")} ${framework.entityDefinitions.discipline.title}`,
|
|
140
155
|
`${data.disciplines.length} ${framework.entityDefinitions.discipline.title.toLowerCase()} — ${framework.entityDefinitions.discipline.description.trim().split("\n")[0]}`,
|
|
@@ -150,26 +165,31 @@ export function renderLanding() {
|
|
|
150
165
|
`${data.tracks.length} ${framework.entityDefinitions.track.title.toLowerCase()} — ${framework.entityDefinitions.track.description.trim().split("\n")[0]}`,
|
|
151
166
|
"/track",
|
|
152
167
|
),
|
|
153
|
-
createQuickLinkCard(
|
|
154
|
-
`${getConceptEmoji(framework, "skill")} ${framework.entityDefinitions.skill.title}`,
|
|
155
|
-
`${data.skills.length} ${framework.entityDefinitions.skill.title.toLowerCase()} across ${capabilityCount} capabilities — ${framework.entityDefinitions.skill.description.trim().split("\n")[0]}`,
|
|
156
|
-
"/skill",
|
|
157
|
-
),
|
|
158
168
|
createQuickLinkCard(
|
|
159
169
|
`${getConceptEmoji(framework, "behaviour")} ${framework.entityDefinitions.behaviour.title}`,
|
|
160
170
|
`${data.behaviours.length} ${framework.entityDefinitions.behaviour.title.toLowerCase()} — ${framework.entityDefinitions.behaviour.description.trim().split("\n")[0]}`,
|
|
161
171
|
"/behaviour",
|
|
162
172
|
),
|
|
163
173
|
createQuickLinkCard(
|
|
164
|
-
"
|
|
165
|
-
`${
|
|
166
|
-
"/
|
|
174
|
+
`${getConceptEmoji(framework, "skill")} ${framework.entityDefinitions.skill.title}`,
|
|
175
|
+
`${data.skills.length} ${framework.entityDefinitions.skill.title.toLowerCase()} across ${capabilityCount} capabilities — ${framework.entityDefinitions.skill.description.trim().split("\n")[0]}`,
|
|
176
|
+
"/skill",
|
|
167
177
|
),
|
|
168
178
|
createQuickLinkCard(
|
|
169
179
|
`${getConceptEmoji(framework, "driver")} ${framework.entityDefinitions.driver.title}`,
|
|
170
180
|
`${data.drivers.length} ${framework.entityDefinitions.driver.title.toLowerCase()} — ${framework.entityDefinitions.driver.description.trim().split("\n")[0]}`,
|
|
171
181
|
"/driver",
|
|
172
182
|
),
|
|
183
|
+
createQuickLinkCard(
|
|
184
|
+
`${getConceptEmoji(framework, "stage")} ${framework.entityDefinitions.stage.title}`,
|
|
185
|
+
`${stages.length} ${framework.entityDefinitions.stage.title.toLowerCase()} — ${framework.entityDefinitions.stage.description.trim().split("\n")[0]}`,
|
|
186
|
+
"/stage",
|
|
187
|
+
),
|
|
188
|
+
createQuickLinkCard(
|
|
189
|
+
`${getConceptEmoji(framework, "tool")} ${framework.entityDefinitions.tool.title}`,
|
|
190
|
+
`${tools.length} ${framework.entityDefinitions.tool.title.toLowerCase()} — ${framework.entityDefinitions.tool.description.trim().split("\n")[0]}`,
|
|
191
|
+
"/tool",
|
|
192
|
+
),
|
|
173
193
|
),
|
|
174
194
|
),
|
|
175
195
|
|
|
@@ -82,7 +82,7 @@ function getWizardSteps(data) {
|
|
|
82
82
|
if (skills && skills.length > 0) {
|
|
83
83
|
steps.push({
|
|
84
84
|
id: `skills-${capability}`,
|
|
85
|
-
name: formatCapability(capability),
|
|
85
|
+
name: formatCapability(capability, data.capabilities),
|
|
86
86
|
icon: getCapabilityEmoji(data.capabilities, capability),
|
|
87
87
|
type: "skills",
|
|
88
88
|
capability: capability,
|
|
@@ -113,11 +113,13 @@ function getWizardSteps(data) {
|
|
|
113
113
|
|
|
114
114
|
/**
|
|
115
115
|
* Format capability name for display
|
|
116
|
-
* @param {string}
|
|
116
|
+
* @param {string} capabilityId
|
|
117
|
+
* @param {Array} capabilities
|
|
117
118
|
* @returns {string}
|
|
118
119
|
*/
|
|
119
|
-
function formatCapability(
|
|
120
|
-
|
|
120
|
+
function formatCapability(capabilityId, capabilities) {
|
|
121
|
+
const capability = capabilities.find((c) => c.id === capabilityId);
|
|
122
|
+
return capability?.name || capabilityId;
|
|
121
123
|
}
|
|
122
124
|
|
|
123
125
|
/**
|
|
@@ -412,7 +414,7 @@ function renderSkillsStep(step, data) {
|
|
|
412
414
|
h2(
|
|
413
415
|
{},
|
|
414
416
|
span({ className: "step-header-icon" }, step.icon),
|
|
415
|
-
` ${formatCapability(capability)} Skills`,
|
|
417
|
+
` ${formatCapability(capability, data.capabilities)} Skills`,
|
|
416
418
|
),
|
|
417
419
|
span(
|
|
418
420
|
{ className: "step-progress" },
|
package/app/pages/skill.js
CHANGED
|
@@ -89,25 +89,13 @@ export function renderSkillDetail(params) {
|
|
|
89
89
|
|
|
90
90
|
/**
|
|
91
91
|
* Format capability for display
|
|
92
|
-
* @param {string}
|
|
92
|
+
* @param {string} capabilityId
|
|
93
93
|
* @param {Array} capabilities
|
|
94
94
|
* @returns {string}
|
|
95
95
|
*/
|
|
96
|
-
function formatCapability(
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
reliability: "Reliability",
|
|
101
|
-
data: "Data",
|
|
102
|
-
ai: "AI",
|
|
103
|
-
process: "Process",
|
|
104
|
-
business: "Business",
|
|
105
|
-
people: "People",
|
|
106
|
-
documentation: "Documentation",
|
|
107
|
-
};
|
|
108
|
-
const label =
|
|
109
|
-
capabilityLabels[capability] ||
|
|
110
|
-
capability.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
111
|
-
const emoji = getCapabilityEmoji(capabilities, capability);
|
|
96
|
+
function formatCapability(capabilityId, capabilities) {
|
|
97
|
+
const capability = capabilities.find((c) => c.id === capabilityId);
|
|
98
|
+
const label = capability?.name || capabilityId;
|
|
99
|
+
const emoji = getCapabilityEmoji(capabilities, capabilityId);
|
|
112
100
|
return `${emoji} ${label}`;
|
|
113
101
|
}
|
package/app/pages/stage.js
CHANGED
|
@@ -7,15 +7,16 @@ import { getState } from "../lib/state.js";
|
|
|
7
7
|
import { createCardList } from "../components/list.js";
|
|
8
8
|
import { renderNotFound } from "../components/error-page.js";
|
|
9
9
|
import { prepareStagesList, stageToDOM } from "../formatters/stage/index.js";
|
|
10
|
+
import { getConceptEmoji } from "../model/levels.js";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Map stage to card configuration
|
|
13
|
-
* @param {Object} stage - Prepared stage item (includes
|
|
14
|
+
* @param {Object} stage - Prepared stage item (includes emojiIcon)
|
|
14
15
|
* @returns {Object} Card configuration
|
|
15
16
|
*/
|
|
16
17
|
function stageToCardConfig(stage) {
|
|
17
18
|
return {
|
|
18
|
-
title: `${stage.
|
|
19
|
+
title: `${stage.emojiIcon} ${stage.name}`,
|
|
19
20
|
description: stage.truncatedDescription,
|
|
20
21
|
href: `/stage/${stage.id}`,
|
|
21
22
|
};
|
|
@@ -23,19 +24,18 @@ function stageToCardConfig(stage) {
|
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
* Create lifecycle flow visualization
|
|
26
|
-
* @param {Array} stages - Array of stage items (each includes
|
|
27
|
+
* @param {Array} stages - Array of stage items (each includes emojiIcon)
|
|
27
28
|
* @returns {HTMLElement}
|
|
28
29
|
*/
|
|
29
30
|
function createLifecycleFlow(stages) {
|
|
30
31
|
const flowItems = stages.map((stage, index) => {
|
|
31
|
-
const emoji = stage.emoji || "🔄";
|
|
32
32
|
const isLast = index === stages.length - 1;
|
|
33
33
|
|
|
34
34
|
return div(
|
|
35
35
|
{ className: "lifecycle-flow-item" },
|
|
36
36
|
a(
|
|
37
37
|
{ href: `#/stage/${stage.id}`, className: "lifecycle-stage" },
|
|
38
|
-
span({ className: "lifecycle-emoji" },
|
|
38
|
+
span({ className: "lifecycle-emoji" }, stage.emojiIcon),
|
|
39
39
|
span({ className: "lifecycle-name" }, stage.name),
|
|
40
40
|
),
|
|
41
41
|
!isLast ? span({ className: "lifecycle-arrow" }, "→") : null,
|
|
@@ -50,7 +50,9 @@ function createLifecycleFlow(stages) {
|
|
|
50
50
|
*/
|
|
51
51
|
export function renderStagesList() {
|
|
52
52
|
const { data } = getState();
|
|
53
|
+
const { framework } = data;
|
|
53
54
|
const stages = data.stages || [];
|
|
55
|
+
const stageEmoji = getConceptEmoji(framework, "stage");
|
|
54
56
|
|
|
55
57
|
// Transform data for list view
|
|
56
58
|
const { items } = prepareStagesList(stages);
|
|
@@ -60,11 +62,13 @@ export function renderStagesList() {
|
|
|
60
62
|
// Header
|
|
61
63
|
div(
|
|
62
64
|
{ className: "page-header" },
|
|
63
|
-
h1(
|
|
65
|
+
h1(
|
|
66
|
+
{ className: "page-title" },
|
|
67
|
+
`${stageEmoji} ${framework.entityDefinitions.stage.title}`,
|
|
68
|
+
),
|
|
64
69
|
p(
|
|
65
70
|
{ className: "page-description" },
|
|
66
|
-
|
|
67
|
-
"constraints, and handoffs to guide work from planning through review.",
|
|
71
|
+
framework.entityDefinitions.stage.description.trim().split("\n")[0],
|
|
68
72
|
),
|
|
69
73
|
),
|
|
70
74
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tools page
|
|
3
|
+
*
|
|
4
|
+
* Displays aggregated tools from all skills with links to skill context.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { render, div, h1, p } from "../lib/render.js";
|
|
8
|
+
import { getState } from "../lib/state.js";
|
|
9
|
+
import { prepareToolsList } from "../formatters/tool/shared.js";
|
|
10
|
+
import { createBadge } from "../components/card.js";
|
|
11
|
+
import { createCardList } from "../components/list.js";
|
|
12
|
+
import { toolToCardConfig } from "../lib/card-mappers.js";
|
|
13
|
+
import { getConceptEmoji } from "../model/levels.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Render tools list page
|
|
17
|
+
*/
|
|
18
|
+
export function renderToolsList() {
|
|
19
|
+
const { data } = getState();
|
|
20
|
+
const { framework } = data;
|
|
21
|
+
const toolEmoji = getConceptEmoji(framework, "tool");
|
|
22
|
+
|
|
23
|
+
const { tools, totalCount } = prepareToolsList(data.skills);
|
|
24
|
+
|
|
25
|
+
const page = div(
|
|
26
|
+
{ className: "tools-page" },
|
|
27
|
+
// Header
|
|
28
|
+
div(
|
|
29
|
+
{ className: "page-header" },
|
|
30
|
+
h1(
|
|
31
|
+
{ className: "page-title" },
|
|
32
|
+
`${toolEmoji} ${framework.entityDefinitions.tool.title}`,
|
|
33
|
+
),
|
|
34
|
+
p(
|
|
35
|
+
{ className: "page-description" },
|
|
36
|
+
framework.entityDefinitions.tool.description.trim().split("\n")[0],
|
|
37
|
+
),
|
|
38
|
+
createBadge(`${totalCount} tools`, "default"),
|
|
39
|
+
),
|
|
40
|
+
|
|
41
|
+
// Tools list using standard card grid
|
|
42
|
+
createCardList(
|
|
43
|
+
tools,
|
|
44
|
+
(tool) => toolToCardConfig(tool, data.capabilities),
|
|
45
|
+
"No tools defined yet.",
|
|
46
|
+
),
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
render(page);
|
|
50
|
+
}
|
package/app/slide-main.js
CHANGED
|
@@ -346,7 +346,7 @@ function populateBrandHeader(framework) {
|
|
|
346
346
|
header.appendChild(
|
|
347
347
|
a(
|
|
348
348
|
{ className: "brand-title", href: "#/" },
|
|
349
|
-
`${framework.
|
|
349
|
+
`${framework.emojiIcon} ${framework.title}`,
|
|
350
350
|
),
|
|
351
351
|
);
|
|
352
352
|
header.appendChild(span({ className: "brand-tag" }, framework.tag));
|
package/app/slides/chapter.js
CHANGED
|
@@ -20,37 +20,37 @@ export function renderChapterSlide({ render, data, params }) {
|
|
|
20
20
|
const chapterConfig = {
|
|
21
21
|
driver: {
|
|
22
22
|
title: framework.entityDefinitions.driver.title,
|
|
23
|
-
|
|
23
|
+
emojiIcon: framework.entityDefinitions.driver.emojiIcon,
|
|
24
24
|
description: framework.entityDefinitions.driver.description,
|
|
25
25
|
},
|
|
26
26
|
skill: {
|
|
27
27
|
title: framework.entityDefinitions.skill.title,
|
|
28
|
-
|
|
28
|
+
emojiIcon: framework.entityDefinitions.skill.emojiIcon,
|
|
29
29
|
description: framework.entityDefinitions.skill.description,
|
|
30
30
|
},
|
|
31
31
|
behaviour: {
|
|
32
32
|
title: framework.entityDefinitions.behaviour.title,
|
|
33
|
-
|
|
33
|
+
emojiIcon: framework.entityDefinitions.behaviour.emojiIcon,
|
|
34
34
|
description: framework.entityDefinitions.behaviour.description,
|
|
35
35
|
},
|
|
36
36
|
discipline: {
|
|
37
37
|
title: framework.entityDefinitions.discipline.title,
|
|
38
|
-
|
|
38
|
+
emojiIcon: framework.entityDefinitions.discipline.emojiIcon,
|
|
39
39
|
description: framework.entityDefinitions.discipline.description,
|
|
40
40
|
},
|
|
41
41
|
grade: {
|
|
42
42
|
title: framework.entityDefinitions.grade.title,
|
|
43
|
-
|
|
43
|
+
emojiIcon: framework.entityDefinitions.grade.emojiIcon,
|
|
44
44
|
description: framework.entityDefinitions.grade.description,
|
|
45
45
|
},
|
|
46
46
|
track: {
|
|
47
47
|
title: framework.entityDefinitions.track.title,
|
|
48
|
-
|
|
48
|
+
emojiIcon: framework.entityDefinitions.track.emojiIcon,
|
|
49
49
|
description: framework.entityDefinitions.track.description,
|
|
50
50
|
},
|
|
51
51
|
job: {
|
|
52
52
|
title: framework.entityDefinitions.job.title,
|
|
53
|
-
|
|
53
|
+
emojiIcon: framework.entityDefinitions.job.emojiIcon,
|
|
54
54
|
description: framework.entityDefinitions.job.description,
|
|
55
55
|
},
|
|
56
56
|
};
|
|
@@ -72,7 +72,7 @@ export function renderChapterSlide({ render, data, params }) {
|
|
|
72
72
|
{ className: "slide chapter-cover" },
|
|
73
73
|
h1(
|
|
74
74
|
{ className: "chapter-title" },
|
|
75
|
-
config.
|
|
75
|
+
config.emojiIcon ? `${config.emojiIcon} ` : "",
|
|
76
76
|
span({ className: "gradient-text" }, config.title),
|
|
77
77
|
),
|
|
78
78
|
p({ className: "chapter-description" }, config.description.trim()),
|
package/app/slides/index.js
CHANGED
|
@@ -22,7 +22,7 @@ export function renderSlideIndex({ render, data }) {
|
|
|
22
22
|
{ className: "page-header" },
|
|
23
23
|
heading1(
|
|
24
24
|
{ className: "page-title" },
|
|
25
|
-
`${framework.
|
|
25
|
+
`${framework.emojiIcon} ${framework.title}`,
|
|
26
26
|
),
|
|
27
27
|
p(
|
|
28
28
|
{ className: "page-description" },
|
|
@@ -55,25 +55,6 @@ export function renderSlideIndex({ render, data }) {
|
|
|
55
55
|
),
|
|
56
56
|
),
|
|
57
57
|
|
|
58
|
-
// Tracks
|
|
59
|
-
div(
|
|
60
|
-
{ className: "slide-section" },
|
|
61
|
-
a(
|
|
62
|
-
{ href: "#/overview/track" },
|
|
63
|
-
heading2(
|
|
64
|
-
{ className: "slide-section-title" },
|
|
65
|
-
`${getConceptEmoji(data.framework, "track")} `,
|
|
66
|
-
span({ className: "gradient-text" }, "Tracks"),
|
|
67
|
-
),
|
|
68
|
-
),
|
|
69
|
-
ul(
|
|
70
|
-
{ className: "related-list" },
|
|
71
|
-
...data.tracks.map((track) =>
|
|
72
|
-
li({}, a({ href: `#/track/${track.id}` }, track.name)),
|
|
73
|
-
),
|
|
74
|
-
),
|
|
75
|
-
),
|
|
76
|
-
|
|
77
58
|
// Grades
|
|
78
59
|
div(
|
|
79
60
|
{ className: "slide-section" },
|
|
@@ -99,21 +80,21 @@ export function renderSlideIndex({ render, data }) {
|
|
|
99
80
|
),
|
|
100
81
|
),
|
|
101
82
|
|
|
102
|
-
//
|
|
83
|
+
// Tracks
|
|
103
84
|
div(
|
|
104
85
|
{ className: "slide-section" },
|
|
105
86
|
a(
|
|
106
|
-
{ href: "#/overview/
|
|
87
|
+
{ href: "#/overview/track" },
|
|
107
88
|
heading2(
|
|
108
89
|
{ className: "slide-section-title" },
|
|
109
|
-
`${getConceptEmoji(data.framework, "
|
|
110
|
-
span({ className: "gradient-text" }, "
|
|
90
|
+
`${getConceptEmoji(data.framework, "track")} `,
|
|
91
|
+
span({ className: "gradient-text" }, "Tracks"),
|
|
111
92
|
),
|
|
112
93
|
),
|
|
113
94
|
ul(
|
|
114
95
|
{ className: "related-list" },
|
|
115
|
-
...data.
|
|
116
|
-
li({}, a({ href: `#/
|
|
96
|
+
...data.tracks.map((track) =>
|
|
97
|
+
li({}, a({ href: `#/track/${track.id}` }, track.name)),
|
|
117
98
|
),
|
|
118
99
|
),
|
|
119
100
|
),
|
|
@@ -137,6 +118,25 @@ export function renderSlideIndex({ render, data }) {
|
|
|
137
118
|
),
|
|
138
119
|
),
|
|
139
120
|
|
|
121
|
+
// Skills
|
|
122
|
+
div(
|
|
123
|
+
{ className: "slide-section" },
|
|
124
|
+
a(
|
|
125
|
+
{ href: "#/overview/skill" },
|
|
126
|
+
heading2(
|
|
127
|
+
{ className: "slide-section-title" },
|
|
128
|
+
`${getConceptEmoji(data.framework, "skill")} `,
|
|
129
|
+
span({ className: "gradient-text" }, "Skills"),
|
|
130
|
+
),
|
|
131
|
+
),
|
|
132
|
+
ul(
|
|
133
|
+
{ className: "related-list" },
|
|
134
|
+
...data.skills.map((skill) =>
|
|
135
|
+
li({}, a({ href: `#/skill/${skill.id}` }, skill.name)),
|
|
136
|
+
),
|
|
137
|
+
),
|
|
138
|
+
),
|
|
139
|
+
|
|
140
140
|
// Drivers
|
|
141
141
|
div(
|
|
142
142
|
{ className: "slide-section" },
|
package/app/slides/overview.js
CHANGED
|
@@ -63,14 +63,14 @@ export function renderOverviewSlide({ render, data, params }) {
|
|
|
63
63
|
const chapterConfig = {
|
|
64
64
|
driver: {
|
|
65
65
|
title: framework.entityDefinitions.driver.title,
|
|
66
|
-
|
|
66
|
+
emojiIcon: framework.entityDefinitions.driver.emojiIcon,
|
|
67
67
|
description: framework.entityDefinitions.driver.description,
|
|
68
68
|
entities: prepareDriversList(data.drivers).items,
|
|
69
69
|
mapper: driverToCardConfig,
|
|
70
70
|
},
|
|
71
71
|
skill: {
|
|
72
72
|
title: framework.entityDefinitions.skill.title,
|
|
73
|
-
|
|
73
|
+
emojiIcon: framework.entityDefinitions.skill.emojiIcon,
|
|
74
74
|
description: framework.entityDefinitions.skill.description,
|
|
75
75
|
entities: Object.values(
|
|
76
76
|
prepareSkillsList(data.skills, data.capabilities).groups,
|
|
@@ -79,14 +79,14 @@ export function renderOverviewSlide({ render, data, params }) {
|
|
|
79
79
|
},
|
|
80
80
|
behaviour: {
|
|
81
81
|
title: framework.entityDefinitions.behaviour.title,
|
|
82
|
-
|
|
82
|
+
emojiIcon: framework.entityDefinitions.behaviour.emojiIcon,
|
|
83
83
|
description: framework.entityDefinitions.behaviour.description,
|
|
84
84
|
entities: prepareBehavioursList(data.behaviours).items,
|
|
85
85
|
mapper: behaviourToCardConfig,
|
|
86
86
|
},
|
|
87
87
|
discipline: {
|
|
88
88
|
title: framework.entityDefinitions.discipline.title,
|
|
89
|
-
|
|
89
|
+
emojiIcon: framework.entityDefinitions.discipline.emojiIcon,
|
|
90
90
|
description: framework.entityDefinitions.discipline.description,
|
|
91
91
|
groups: prepareDisciplinesList(data.disciplines).groups,
|
|
92
92
|
mapper: disciplineToCardConfig,
|
|
@@ -94,21 +94,21 @@ export function renderOverviewSlide({ render, data, params }) {
|
|
|
94
94
|
},
|
|
95
95
|
grade: {
|
|
96
96
|
title: framework.entityDefinitions.grade.title,
|
|
97
|
-
|
|
97
|
+
emojiIcon: framework.entityDefinitions.grade.emojiIcon,
|
|
98
98
|
description: framework.entityDefinitions.grade.description,
|
|
99
99
|
entities: prepareGradesList(data.grades).items,
|
|
100
100
|
mapper: gradeToCardConfig,
|
|
101
101
|
},
|
|
102
102
|
track: {
|
|
103
103
|
title: framework.entityDefinitions.track.title,
|
|
104
|
-
|
|
104
|
+
emojiIcon: framework.entityDefinitions.track.emojiIcon,
|
|
105
105
|
description: framework.entityDefinitions.track.description,
|
|
106
106
|
entities: prepareTracksList(data.tracks).items,
|
|
107
107
|
mapper: trackToCardConfig,
|
|
108
108
|
},
|
|
109
109
|
job: {
|
|
110
110
|
title: framework.entityDefinitions.job.title,
|
|
111
|
-
|
|
111
|
+
emojiIcon: framework.entityDefinitions.job.emojiIcon,
|
|
112
112
|
description: framework.entityDefinitions.job.description,
|
|
113
113
|
entities: generateAllJobs({
|
|
114
114
|
disciplines: data.disciplines,
|
|
@@ -150,7 +150,7 @@ export function renderOverviewSlide({ render, data, params }) {
|
|
|
150
150
|
{ className: "overview-header" },
|
|
151
151
|
h1(
|
|
152
152
|
{ className: "overview-title" },
|
|
153
|
-
config.
|
|
153
|
+
config.emojiIcon ? `${config.emojiIcon} ` : "",
|
|
154
154
|
span({ className: "gradient-text" }, config.title),
|
|
155
155
|
),
|
|
156
156
|
p({ className: "overview-description" }, config.description.trim()),
|
package/app/slides/skill.js
CHANGED
package/bin/pathway.js
CHANGED
|
@@ -9,15 +9,17 @@
|
|
|
9
9
|
* npx pathway <command> [options]
|
|
10
10
|
*
|
|
11
11
|
* Commands:
|
|
12
|
-
* skill [<id>] Show skills (summary, --list, or detail)
|
|
13
|
-
* behaviour [<id>] Show behaviours
|
|
14
12
|
* discipline [<id>] Show disciplines
|
|
15
13
|
* grade [<id>] Show grades
|
|
16
14
|
* track [<id>] Show tracks
|
|
15
|
+
* behaviour [<id>] Show behaviours
|
|
16
|
+
* skill [<id>] Show skills (summary, --list, or detail)
|
|
17
17
|
* driver [<id>] Show drivers
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
18
|
+
* stage [<id>] Show stages
|
|
19
|
+
* tool [<name>] Show tools
|
|
20
|
+
* job [<discipline> <grade>] [--track=TRACK] Generate job definition
|
|
21
|
+
* interview <discipline> <grade> [--track=TRACK] [--type=TYPE] Generate interview
|
|
22
|
+
* progress <discipline> <grade> [--track=TRACK] [--compare=GRADE] Career progression
|
|
21
23
|
* questions [options] Browse interview questions
|
|
22
24
|
* agent [<discipline> <track>] [--output=PATH] Generate AI agent
|
|
23
25
|
*
|
|
@@ -37,13 +39,14 @@ import { formatError } from "../app/lib/cli-output.js";
|
|
|
37
39
|
import { runSchemaValidation } from "../app/model/schema-validation.js";
|
|
38
40
|
|
|
39
41
|
// Import command handlers
|
|
40
|
-
import { runSkillCommand } from "../app/commands/skill.js";
|
|
41
|
-
import { runBehaviourCommand } from "../app/commands/behaviour.js";
|
|
42
42
|
import { runDisciplineCommand } from "../app/commands/discipline.js";
|
|
43
43
|
import { runGradeCommand } from "../app/commands/grade.js";
|
|
44
44
|
import { runTrackCommand } from "../app/commands/track.js";
|
|
45
|
+
import { runBehaviourCommand } from "../app/commands/behaviour.js";
|
|
46
|
+
import { runSkillCommand } from "../app/commands/skill.js";
|
|
45
47
|
import { runDriverCommand } from "../app/commands/driver.js";
|
|
46
48
|
import { runStageCommand } from "../app/commands/stage.js";
|
|
49
|
+
import { runToolCommand } from "../app/commands/tool.js";
|
|
47
50
|
import { runJobCommand } from "../app/commands/job.js";
|
|
48
51
|
import { runInterviewCommand } from "../app/commands/interview.js";
|
|
49
52
|
import { runProgressCommand } from "../app/commands/progress.js";
|
|
@@ -58,13 +61,14 @@ const __dirname = dirname(__filename);
|
|
|
58
61
|
const rootDir = join(__dirname, "..");
|
|
59
62
|
|
|
60
63
|
const COMMANDS = {
|
|
61
|
-
skill: runSkillCommand,
|
|
62
|
-
behaviour: runBehaviourCommand,
|
|
63
64
|
discipline: runDisciplineCommand,
|
|
64
65
|
grade: runGradeCommand,
|
|
65
66
|
track: runTrackCommand,
|
|
67
|
+
behaviour: runBehaviourCommand,
|
|
68
|
+
skill: runSkillCommand,
|
|
66
69
|
driver: runDriverCommand,
|
|
67
70
|
stage: runStageCommand,
|
|
71
|
+
tool: runToolCommand,
|
|
68
72
|
job: runJobCommand,
|
|
69
73
|
interview: runInterviewCommand,
|
|
70
74
|
progress: runProgressCommand,
|
|
@@ -86,19 +90,21 @@ Getting Started:
|
|
|
86
90
|
site [--output=PATH] Generate static site to ./site/
|
|
87
91
|
|
|
88
92
|
Entity Commands (summary by default, --list for IDs, <id> for detail):
|
|
89
|
-
skill [<id>] Browse skills
|
|
90
|
-
behaviour [<id>] Browse behaviours
|
|
91
93
|
discipline [<id>] Browse disciplines
|
|
92
94
|
grade [<id>] Browse grades
|
|
93
95
|
track [<id>] Browse tracks
|
|
96
|
+
behaviour [<id>] Browse behaviours
|
|
97
|
+
skill [<id>] Browse skills
|
|
98
|
+
--agent Output as agent SKILL.md format
|
|
94
99
|
driver [<id>] Browse drivers
|
|
95
100
|
stage [<id>] Browse lifecycle stages
|
|
101
|
+
tool [<name>] Browse recommended tools
|
|
96
102
|
|
|
97
103
|
Composite Commands:
|
|
98
|
-
job [<discipline> <
|
|
99
|
-
interview <discipline> <
|
|
104
|
+
job [<discipline> <grade>] [--track=TRACK] Generate job definition
|
|
105
|
+
interview <discipline> <grade> [--track=TRACK] [--type=TYPE]
|
|
100
106
|
Generate interview questions
|
|
101
|
-
progress <discipline> <
|
|
107
|
+
progress <discipline> <grade> [--track=TRACK] [--compare=GRADE]
|
|
102
108
|
Show career progression
|
|
103
109
|
questions [filters] Browse interview questions
|
|
104
110
|
agent <discipline> [--track=<track>] Generate AI coding agent
|
|
@@ -130,11 +136,17 @@ Examples:
|
|
|
130
136
|
npx pathway skill # Summary of all skills
|
|
131
137
|
npx pathway skill --list # Skill IDs for piping
|
|
132
138
|
npx pathway skill ai_evaluation # Detail view
|
|
139
|
+
npx pathway skill architecture_design --agent # Agent SKILL.md output
|
|
140
|
+
|
|
141
|
+
npx pathway tool # Summary of all tools
|
|
142
|
+
npx pathway tool --list # Tool names for piping
|
|
143
|
+
npx pathway tool DuckDB # Tool detail with skill usages
|
|
133
144
|
|
|
134
145
|
npx pathway job # Summary of valid combinations
|
|
135
146
|
npx pathway job --list # All combinations for piping
|
|
136
|
-
npx pathway job software_engineering
|
|
137
|
-
npx pathway job
|
|
147
|
+
npx pathway job software_engineering L4
|
|
148
|
+
npx pathway job software_engineering L4 --track=platform
|
|
149
|
+
npx pathway job se L3 --track=platform --checklist=code
|
|
138
150
|
|
|
139
151
|
npx pathway questions --level=practitioner
|
|
140
152
|
npx pathway questions --stats
|
|
@@ -176,6 +188,7 @@ function parseArgs(args) {
|
|
|
176
188
|
output: null,
|
|
177
189
|
stage: null,
|
|
178
190
|
"all-stages": false,
|
|
191
|
+
agent: false,
|
|
179
192
|
// Serve command options
|
|
180
193
|
port: null,
|
|
181
194
|
// Init command options
|
|
@@ -227,6 +240,8 @@ function parseArgs(args) {
|
|
|
227
240
|
result.stage = arg.slice(8);
|
|
228
241
|
} else if (arg === "--all-stages") {
|
|
229
242
|
result["all-stages"] = true;
|
|
243
|
+
} else if (arg === "--agent") {
|
|
244
|
+
result.agent = true;
|
|
230
245
|
} else if (arg.startsWith("--checklist=")) {
|
|
231
246
|
result.checklist = arg.slice(12);
|
|
232
247
|
} else if (arg.startsWith("--port=")) {
|