@forwardimpact/pathway 0.11.0 → 0.12.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/package.json +3 -3
- package/src/commands/agent.js +2 -2
- package/src/commands/skill.js +2 -2
- package/src/components/comparison-radar.js +7 -5
- package/src/css/components/badges.css +11 -31
- package/src/css/tokens.css +12 -20
- package/src/formatters/skill/shared.js +1 -1
- package/src/handout.html +3 -1
- package/src/index.html +3 -1
- package/src/lib/job-cache.js +7 -7
- package/src/pages/agent-builder.js +3 -3
- package/src/pages/landing.js +4 -1
- package/src/pages/progress.js +1 -0
- package/src/pages/self-assessment.js +7 -4
- package/src/slides.html +3 -1
- package/templates/skill.template.md +8 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forwardimpact/pathway",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Career progression web app and CLI for exploring roles and generating agents",
|
|
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/schema": "^0.
|
|
44
|
-
"@forwardimpact/model": "^0.
|
|
43
|
+
"@forwardimpact/schema": "^0.5.0",
|
|
44
|
+
"@forwardimpact/model": "^0.6.0",
|
|
45
45
|
"mustache": "^4.2.0",
|
|
46
46
|
"simple-icons": "^16.7.0",
|
|
47
47
|
"yaml": "^2.3.4"
|
package/src/commands/agent.js
CHANGED
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
validateAgentSkill,
|
|
38
38
|
deriveReferenceGrade,
|
|
39
39
|
deriveAgentSkills,
|
|
40
|
-
|
|
40
|
+
generateSkillMarkdown,
|
|
41
41
|
deriveToolkit,
|
|
42
42
|
buildAgentIndex,
|
|
43
43
|
} from "@forwardimpact/model";
|
|
@@ -502,7 +502,7 @@ export async function runAgentCommand({ data, args, options, dataDir }) {
|
|
|
502
502
|
const skillFiles = derivedSkills
|
|
503
503
|
.map((derived) => skillsWithAgent.find((s) => s.id === derived.skillId))
|
|
504
504
|
.filter((skill) => skill?.agent)
|
|
505
|
-
.map((skill) =>
|
|
505
|
+
.map((skill) => generateSkillMarkdown(skill, data.stages));
|
|
506
506
|
|
|
507
507
|
// Validate all profiles
|
|
508
508
|
for (const profile of profiles) {
|
package/src/commands/skill.js
CHANGED
|
@@ -16,7 +16,7 @@ import { skillToMarkdown } from "../formatters/skill/markdown.js";
|
|
|
16
16
|
import { prepareSkillsList } from "../formatters/skill/shared.js";
|
|
17
17
|
import { getConceptEmoji } from "@forwardimpact/schema/levels";
|
|
18
18
|
import { formatTable, formatError } from "../lib/cli-output.js";
|
|
19
|
-
import {
|
|
19
|
+
import { generateSkillMarkdown } from "@forwardimpact/model/agent";
|
|
20
20
|
import { formatAgentSkill } from "../formatters/agent/skill.js";
|
|
21
21
|
import { loadSkillTemplate } from "../lib/template-loader.js";
|
|
22
22
|
|
|
@@ -80,7 +80,7 @@ async function formatAgentDetail(skill, stages, dataDir) {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
const template = await loadSkillTemplate(dataDir);
|
|
83
|
-
const skillMd =
|
|
83
|
+
const skillMd = generateSkillMarkdown(skill, stages);
|
|
84
84
|
const output = formatAgentSkill(skillMd, template);
|
|
85
85
|
console.log(output);
|
|
86
86
|
}
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
getBehaviourMaturityIndex,
|
|
14
14
|
formatLevel,
|
|
15
15
|
} from "../lib/render.js";
|
|
16
|
-
import {
|
|
16
|
+
import { compareByCapability } from "@forwardimpact/model/policies";
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Create a comparison skill radar chart
|
|
@@ -24,7 +24,7 @@ import { getCapabilityIndex } from "@forwardimpact/schema/levels";
|
|
|
24
24
|
*/
|
|
25
25
|
export function createComparisonSkillRadar(
|
|
26
26
|
currentMatrix,
|
|
27
|
-
targetMatrix,
|
|
27
|
+
targetMatrix = [],
|
|
28
28
|
options = {},
|
|
29
29
|
) {
|
|
30
30
|
const container = div(
|
|
@@ -80,9 +80,11 @@ export function createComparisonSkillRadar(
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
// Sort by capability order, then by skill name within capability
|
|
83
|
+
const capabilityComparator = options.capabilities
|
|
84
|
+
? compareByCapability(options.capabilities)
|
|
85
|
+
: (a, b) => a.capability.localeCompare(b.capability);
|
|
83
86
|
skillEntries.sort((a, b) => {
|
|
84
|
-
const capDiff =
|
|
85
|
-
getCapabilityIndex(a.capability) - getCapabilityIndex(b.capability);
|
|
87
|
+
const capDiff = capabilityComparator(a, b);
|
|
86
88
|
if (capDiff !== 0) return capDiff;
|
|
87
89
|
return a.skillName.localeCompare(b.skillName);
|
|
88
90
|
});
|
|
@@ -141,7 +143,7 @@ export function createComparisonSkillRadar(
|
|
|
141
143
|
*/
|
|
142
144
|
export function createComparisonBehaviourRadar(
|
|
143
145
|
currentProfile,
|
|
144
|
-
targetProfile,
|
|
146
|
+
targetProfile = [],
|
|
145
147
|
options = {},
|
|
146
148
|
) {
|
|
147
149
|
const container = div(
|
|
@@ -93,50 +93,30 @@
|
|
|
93
93
|
font-weight: 500;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
/* Capability badges */
|
|
96
|
+
/* Capability badges - each capability ID maps to its own distinct color */
|
|
97
97
|
.badge-delivery {
|
|
98
|
-
background: var(--color-
|
|
99
|
-
color: var(--color-
|
|
98
|
+
background: var(--color-cap-delivery-light);
|
|
99
|
+
color: var(--color-cap-delivery);
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
.badge-scale {
|
|
103
|
-
background: var(--color-
|
|
104
|
-
color: var(--color-
|
|
103
|
+
background: var(--color-cap-scale-light);
|
|
104
|
+
color: var(--color-cap-scale);
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
.badge-reliability {
|
|
108
|
-
background: var(--color-
|
|
109
|
-
color: var(--color-
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
.badge-data {
|
|
113
|
-
background: var(--color-cat-data-light);
|
|
114
|
-
color: var(--color-cat-data);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
.badge-ai {
|
|
118
|
-
background: var(--color-cat-ai-light);
|
|
119
|
-
color: var(--color-cat-ai);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
.badge-process {
|
|
123
|
-
background: var(--color-cat-process-light);
|
|
124
|
-
color: var(--color-cat-process);
|
|
108
|
+
background: var(--color-cap-reliability-light);
|
|
109
|
+
color: var(--color-cap-reliability);
|
|
125
110
|
}
|
|
126
111
|
|
|
127
112
|
.badge-business {
|
|
128
|
-
background: var(--color-
|
|
129
|
-
color: var(--color-
|
|
113
|
+
background: var(--color-cap-business-light);
|
|
114
|
+
color: var(--color-cap-business);
|
|
130
115
|
}
|
|
131
116
|
|
|
132
117
|
.badge-people {
|
|
133
|
-
background: var(--color-
|
|
134
|
-
color: var(--color-
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
.badge-documentation {
|
|
138
|
-
background: var(--color-cat-documentation-light);
|
|
139
|
-
color: var(--color-cat-documentation);
|
|
118
|
+
background: var(--color-cap-people-light);
|
|
119
|
+
color: var(--color-cap-people);
|
|
140
120
|
}
|
|
141
121
|
|
|
142
122
|
/* Tool badge */
|
package/src/css/tokens.css
CHANGED
|
@@ -43,30 +43,22 @@
|
|
|
43
43
|
--color-maturity-5: #c4b5fd;
|
|
44
44
|
|
|
45
45
|
/* --------------------------------------------------------------------------
|
|
46
|
-
Colors -
|
|
46
|
+
Colors - Capabilities (text)
|
|
47
47
|
-------------------------------------------------------------------------- */
|
|
48
|
-
--color-
|
|
49
|
-
--color-
|
|
50
|
-
--color-
|
|
51
|
-
--color-
|
|
52
|
-
--color-
|
|
53
|
-
--color-cat-process: #c2410c; /* orange */
|
|
54
|
-
--color-cat-business: #a16207; /* amber */
|
|
55
|
-
--color-cat-people: #be185d; /* pink */
|
|
56
|
-
--color-cat-documentation: #15803d; /* green */
|
|
48
|
+
--color-cap-delivery: #dc2626; /* red - speed and efficiency */
|
|
49
|
+
--color-cap-scale: #1d4ed8; /* blue - infrastructure and systems */
|
|
50
|
+
--color-cap-reliability: #0891b2; /* cyan - stability and operations */
|
|
51
|
+
--color-cap-business: #b45309; /* amber - strategy and outcomes */
|
|
52
|
+
--color-cap-people: #be185d; /* pink - collaboration and leadership */
|
|
57
53
|
|
|
58
54
|
/* --------------------------------------------------------------------------
|
|
59
|
-
Colors -
|
|
55
|
+
Colors - Capabilities (backgrounds)
|
|
60
56
|
-------------------------------------------------------------------------- */
|
|
61
|
-
--color-
|
|
62
|
-
--color-
|
|
63
|
-
--color-
|
|
64
|
-
--color-
|
|
65
|
-
--color-
|
|
66
|
-
--color-cat-process-light: #ffedd5; /* orange */
|
|
67
|
-
--color-cat-business-light: #fef3c7; /* amber */
|
|
68
|
-
--color-cat-people-light: #fce7f3; /* pink */
|
|
69
|
-
--color-cat-documentation-light: #dcfce7; /* green */
|
|
57
|
+
--color-cap-delivery-light: #fecaca; /* red */
|
|
58
|
+
--color-cap-scale-light: #dbeafe; /* blue */
|
|
59
|
+
--color-cap-reliability-light: #cffafe; /* cyan */
|
|
60
|
+
--color-cap-business-light: #fef3c7; /* amber */
|
|
61
|
+
--color-cap-people-light: #fce7f3; /* pink */
|
|
70
62
|
|
|
71
63
|
/* --------------------------------------------------------------------------
|
|
72
64
|
Colors - Neutrals
|
|
@@ -43,7 +43,7 @@ export function prepareSkillsList(
|
|
|
43
43
|
capabilities,
|
|
44
44
|
descriptionLimit = 120,
|
|
45
45
|
) {
|
|
46
|
-
const grouped = groupSkillsByCapability(skills);
|
|
46
|
+
const grouped = groupSkillsByCapability(skills, capabilities);
|
|
47
47
|
|
|
48
48
|
const groups = {};
|
|
49
49
|
for (const [capability, capabilitySkills] of Object.entries(grouped)) {
|
package/src/handout.html
CHANGED
|
@@ -23,7 +23,9 @@
|
|
|
23
23
|
"@forwardimpact/model/checklist": "/model/lib/checklist.js",
|
|
24
24
|
"@forwardimpact/model/matching": "/model/lib/matching.js",
|
|
25
25
|
"@forwardimpact/model/profile": "/model/lib/profile.js",
|
|
26
|
-
"@forwardimpact/model/progression": "/model/lib/progression.js"
|
|
26
|
+
"@forwardimpact/model/progression": "/model/lib/progression.js",
|
|
27
|
+
"@forwardimpact/model/policies": "/model/lib/policies/index.js",
|
|
28
|
+
"@forwardimpact/model/toolkit": "/model/lib/toolkit.js"
|
|
27
29
|
}
|
|
28
30
|
}
|
|
29
31
|
</script>
|
package/src/index.html
CHANGED
|
@@ -35,7 +35,9 @@
|
|
|
35
35
|
"@forwardimpact/model/checklist": "/model/lib/checklist.js",
|
|
36
36
|
"@forwardimpact/model/matching": "/model/lib/matching.js",
|
|
37
37
|
"@forwardimpact/model/profile": "/model/lib/profile.js",
|
|
38
|
-
"@forwardimpact/model/progression": "/model/lib/progression.js"
|
|
38
|
+
"@forwardimpact/model/progression": "/model/lib/progression.js",
|
|
39
|
+
"@forwardimpact/model/policies": "/model/lib/policies/index.js",
|
|
40
|
+
"@forwardimpact/model/toolkit": "/model/lib/toolkit.js"
|
|
39
41
|
}
|
|
40
42
|
}
|
|
41
43
|
</script>
|
package/src/lib/job-cache.js
CHANGED
|
@@ -11,13 +11,13 @@ import { deriveJob } from "@forwardimpact/model/derivation";
|
|
|
11
11
|
const cache = new Map();
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
14
|
+
* Build a consistent cache key from job parameters
|
|
15
15
|
* @param {string} disciplineId
|
|
16
16
|
* @param {string} gradeId
|
|
17
17
|
* @param {string} [trackId] - Optional track ID
|
|
18
18
|
* @returns {string}
|
|
19
19
|
*/
|
|
20
|
-
export function
|
|
20
|
+
export function buildJobKey(disciplineId, gradeId, trackId = null) {
|
|
21
21
|
if (trackId) {
|
|
22
22
|
return `${disciplineId}_${gradeId}_${trackId}`;
|
|
23
23
|
}
|
|
@@ -43,7 +43,7 @@ export function getOrCreateJob({
|
|
|
43
43
|
behaviours,
|
|
44
44
|
capabilities,
|
|
45
45
|
}) {
|
|
46
|
-
const key =
|
|
46
|
+
const key = buildJobKey(discipline.id, grade.id, track?.id);
|
|
47
47
|
|
|
48
48
|
if (!cache.has(key)) {
|
|
49
49
|
const job = deriveJob({
|
|
@@ -66,7 +66,7 @@ export function getOrCreateJob({
|
|
|
66
66
|
/**
|
|
67
67
|
* Clear all cached jobs
|
|
68
68
|
*/
|
|
69
|
-
export function
|
|
69
|
+
export function clearCache() {
|
|
70
70
|
cache.clear();
|
|
71
71
|
}
|
|
72
72
|
|
|
@@ -76,14 +76,14 @@ export function clearJobCache() {
|
|
|
76
76
|
* @param {string} gradeId
|
|
77
77
|
* @param {string} [trackId] - Optional track ID
|
|
78
78
|
*/
|
|
79
|
-
export function
|
|
80
|
-
cache.delete(
|
|
79
|
+
export function invalidateCachedJob(disciplineId, gradeId, trackId = null) {
|
|
80
|
+
cache.delete(buildJobKey(disciplineId, gradeId, trackId));
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
/**
|
|
84
84
|
* Get the number of cached jobs (for testing/debugging)
|
|
85
85
|
* @returns {number}
|
|
86
86
|
*/
|
|
87
|
-
export function
|
|
87
|
+
export function getCachedJobCount() {
|
|
88
88
|
return cache.size;
|
|
89
89
|
}
|
|
@@ -24,7 +24,7 @@ import { loadAgentDataBrowser } from "../lib/yaml-loader.js";
|
|
|
24
24
|
import {
|
|
25
25
|
generateStageAgentProfile,
|
|
26
26
|
deriveStageAgent,
|
|
27
|
-
|
|
27
|
+
generateSkillMarkdown,
|
|
28
28
|
deriveAgentSkills,
|
|
29
29
|
deriveReferenceGrade,
|
|
30
30
|
deriveToolkit,
|
|
@@ -502,7 +502,7 @@ function createAllStagesPreview(context) {
|
|
|
502
502
|
const skillFiles = derivedSkills
|
|
503
503
|
.map((derived) => skills.find((s) => s.id === derived.skillId))
|
|
504
504
|
.filter((skill) => skill?.agent)
|
|
505
|
-
.map((skill) =>
|
|
505
|
+
.map((skill) => generateSkillMarkdown(skill, stages));
|
|
506
506
|
|
|
507
507
|
// Derive toolkit from agent skills
|
|
508
508
|
const toolkit = deriveToolkit({
|
|
@@ -633,7 +633,7 @@ function createSingleStagePreview(context, stage) {
|
|
|
633
633
|
const skillFiles = derivedSkills
|
|
634
634
|
.map((d) => skills.find((s) => s.id === d.skillId))
|
|
635
635
|
.filter((skill) => skill?.agent)
|
|
636
|
-
.map((skill) =>
|
|
636
|
+
.map((skill) => generateSkillMarkdown(skill, stages));
|
|
637
637
|
|
|
638
638
|
// Derive toolkit from agent skills
|
|
639
639
|
const toolkit = deriveToolkit({
|
package/src/pages/landing.js
CHANGED
|
@@ -45,7 +45,10 @@ export function renderLanding() {
|
|
|
45
45
|
const stages = data.stages || [];
|
|
46
46
|
|
|
47
47
|
// Calculate stats using centralized capability ordering
|
|
48
|
-
const skillsByCapability = groupSkillsByCapability(
|
|
48
|
+
const skillsByCapability = groupSkillsByCapability(
|
|
49
|
+
data.skills,
|
|
50
|
+
data.capabilities,
|
|
51
|
+
);
|
|
49
52
|
const capabilityCount = Object.keys(skillsByCapability).length;
|
|
50
53
|
const tools = aggregateTools(data.skills);
|
|
51
54
|
|
package/src/pages/progress.js
CHANGED
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
SKILL_LEVEL_ORDER,
|
|
23
23
|
BEHAVIOUR_MATURITY_ORDER,
|
|
24
24
|
groupSkillsByCapability,
|
|
25
|
-
|
|
25
|
+
getCapabilityOrder,
|
|
26
26
|
getCapabilityEmoji,
|
|
27
27
|
getConceptEmoji,
|
|
28
28
|
} from "@forwardimpact/schema/levels";
|
|
@@ -66,7 +66,10 @@ export function getAssessmentState() {
|
|
|
66
66
|
*/
|
|
67
67
|
function getWizardSteps(data) {
|
|
68
68
|
const { framework } = data;
|
|
69
|
-
const skillsByCapability = groupSkillsByCapability(
|
|
69
|
+
const skillsByCapability = groupSkillsByCapability(
|
|
70
|
+
data.skills,
|
|
71
|
+
data.capabilities,
|
|
72
|
+
);
|
|
70
73
|
const steps = [
|
|
71
74
|
{
|
|
72
75
|
id: "intro",
|
|
@@ -77,7 +80,7 @@ function getWizardSteps(data) {
|
|
|
77
80
|
];
|
|
78
81
|
|
|
79
82
|
// Add a step for each non-empty skill capability
|
|
80
|
-
for (const capability of
|
|
83
|
+
for (const capability of getCapabilityOrder(data.capabilities)) {
|
|
81
84
|
const skills = skillsByCapability[capability];
|
|
82
85
|
if (skills && skills.length > 0) {
|
|
83
86
|
steps.push({
|
|
@@ -277,7 +280,7 @@ function renderIntroStep(data) {
|
|
|
277
280
|
div(
|
|
278
281
|
{},
|
|
279
282
|
h4({}, `${data.skills.length} Skills`),
|
|
280
|
-
p({}, "Across " +
|
|
283
|
+
p({}, "Across " + data.capabilities.length + " capabilities"),
|
|
281
284
|
),
|
|
282
285
|
),
|
|
283
286
|
div(
|
package/src/slides.html
CHANGED
|
@@ -23,7 +23,9 @@
|
|
|
23
23
|
"@forwardimpact/model/checklist": "/model/lib/checklist.js",
|
|
24
24
|
"@forwardimpact/model/matching": "/model/lib/matching.js",
|
|
25
25
|
"@forwardimpact/model/profile": "/model/lib/profile.js",
|
|
26
|
-
"@forwardimpact/model/progression": "/model/lib/progression.js"
|
|
26
|
+
"@forwardimpact/model/progression": "/model/lib/progression.js",
|
|
27
|
+
"@forwardimpact/model/policies": "/model/lib/policies/index.js",
|
|
28
|
+
"@forwardimpact/model/toolkit": "/model/lib/toolkit.js"
|
|
27
29
|
}
|
|
28
30
|
}
|
|
29
31
|
</script>
|
|
@@ -30,10 +30,15 @@ description: {{{description}}}{{#hasUseWhen}} Use When: {{{useWhen}}}{{/hasUseWh
|
|
|
30
30
|
{{/hasStages}}
|
|
31
31
|
{{#hasToolReferences}}
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
## Required Tools
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
**MANDATORY:** You MUST use these tools when applying this skill. These are
|
|
36
|
+
organizational standards that override general knowledge or personal preferences.
|
|
37
|
+
|
|
38
|
+
If a blocking constraint prevents use of a required tool, document in your
|
|
39
|
+
output: (1) which tool requirement you cannot meet, (2) the specific constraint
|
|
40
|
+
preventing compliance, and (3) the alternative approach with acknowledged
|
|
41
|
+
trade-offs.
|
|
37
42
|
|
|
38
43
|
| Tool | Use When |
|
|
39
44
|
| ---- | -------- |
|