@forwardimpact/pathway 0.4.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/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/serve.js +2 -2
- package/app/commands/site.js +22 -2
- package/app/commands/skill.js +1 -1
- package/app/commands/stage.js +1 -1
- package/app/commands/track.js +1 -1
- package/app/components/card.js +11 -1
- package/app/components/code-display.js +153 -0
- package/app/components/markdown-textarea.js +68 -47
- package/app/css/bundles/app.css +14 -0
- package/app/css/components/badges.css +15 -8
- package/app/css/components/forms.css +23 -13
- package/app/css/components/surfaces.css +49 -3
- package/app/css/components/typography.css +1 -2
- package/app/css/pages/agent-builder.css +11 -102
- package/app/css/pages/detail.css +11 -1
- package/app/css/tokens.css +3 -0
- package/app/formatters/agent/dom.js +26 -71
- package/app/formatters/agent/profile.js +11 -6
- package/app/formatters/grade/dom.js +6 -6
- package/app/formatters/job/dom.js +3 -3
- package/app/formatters/json-ld.js +1 -1
- package/app/formatters/skill/dom.js +68 -56
- package/app/formatters/skill/shared.js +3 -1
- package/app/formatters/stage/microdata.js +2 -2
- package/app/formatters/stage/shared.js +3 -3
- package/app/formatters/tool/shared.js +6 -0
- package/app/handout-main.js +12 -11
- package/app/index.html +7 -1
- package/app/lib/card-mappers.js +27 -0
- package/app/model/agent.js +21 -5
- package/app/model/checklist.js +2 -2
- package/app/model/derivation.js +2 -2
- package/app/model/levels.js +2 -2
- package/app/model/validation.js +3 -3
- package/app/pages/agent-builder.js +119 -75
- package/app/pages/landing.js +1 -1
- package/app/pages/stage.js +4 -4
- package/app/slide-main.js +1 -1
- package/app/slides/chapter.js +8 -8
- package/app/slides/index.js +1 -1
- package/app/slides/overview.js +8 -8
- package/app/slides/skill.js +1 -0
- package/examples/capabilities/business.yaml +1 -1
- package/examples/capabilities/delivery.yaml +3 -1
- package/examples/capabilities/people.yaml +1 -1
- package/examples/capabilities/reliability.yaml +3 -1
- package/examples/capabilities/scale.yaml +1 -1
- package/examples/framework.yaml +11 -11
- 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 +12 -9
- 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
|
@@ -18,7 +18,8 @@ import {
|
|
|
18
18
|
} from "../../lib/render.js";
|
|
19
19
|
import { createBackLink } from "../../components/nav.js";
|
|
20
20
|
import { createLevelCell } from "../../components/detail.js";
|
|
21
|
-
import {
|
|
21
|
+
import { createCodeDisplay } from "../../components/code-display.js";
|
|
22
|
+
import { createToolIcon } from "../../lib/card-mappers.js";
|
|
22
23
|
import { SKILL_LEVEL_ORDER } from "../../model/levels.js";
|
|
23
24
|
import { prepareSkillDetail } from "./shared.js";
|
|
24
25
|
import { createJsonLdScript, skillToJsonLd } from "../json-ld.js";
|
|
@@ -32,11 +33,19 @@ import { createJsonLdScript, skillToJsonLd } from "../json-ld.js";
|
|
|
32
33
|
* @param {Array} context.drivers - All drivers
|
|
33
34
|
* @param {Array} context.capabilities - Capability entities
|
|
34
35
|
* @param {boolean} [context.showBackLink=true] - Whether to show back navigation link
|
|
36
|
+
* @param {boolean} [context.showToolsAndPatterns=true] - Whether to show recommended tools and implementation patterns
|
|
35
37
|
* @returns {HTMLElement}
|
|
36
38
|
*/
|
|
37
39
|
export function skillToDOM(
|
|
38
40
|
skill,
|
|
39
|
-
{
|
|
41
|
+
{
|
|
42
|
+
disciplines,
|
|
43
|
+
tracks,
|
|
44
|
+
drivers,
|
|
45
|
+
capabilities,
|
|
46
|
+
showBackLink = true,
|
|
47
|
+
showToolsAndPatterns = true,
|
|
48
|
+
} = {},
|
|
40
49
|
) {
|
|
41
50
|
const view = prepareSkillDetail(skill, {
|
|
42
51
|
disciplines,
|
|
@@ -98,60 +107,6 @@ export function skillToDOM(
|
|
|
98
107
|
),
|
|
99
108
|
),
|
|
100
109
|
|
|
101
|
-
// Recommended Tools
|
|
102
|
-
view.toolReferences.length > 0
|
|
103
|
-
? div(
|
|
104
|
-
{ className: "detail-section" },
|
|
105
|
-
heading2({ className: "section-title" }, "Recommended Tools"),
|
|
106
|
-
table(
|
|
107
|
-
{ className: "tools-table" },
|
|
108
|
-
thead({}, tr({}, th({}, "Tool"), th({}, "Use When"))),
|
|
109
|
-
tbody(
|
|
110
|
-
{},
|
|
111
|
-
...view.toolReferences.map((tool) =>
|
|
112
|
-
tr(
|
|
113
|
-
{},
|
|
114
|
-
td(
|
|
115
|
-
{},
|
|
116
|
-
tool.url
|
|
117
|
-
? a(
|
|
118
|
-
{
|
|
119
|
-
href: tool.url,
|
|
120
|
-
target: "_blank",
|
|
121
|
-
rel: "noopener noreferrer",
|
|
122
|
-
},
|
|
123
|
-
tool.name,
|
|
124
|
-
)
|
|
125
|
-
: tool.name,
|
|
126
|
-
),
|
|
127
|
-
td({}, tool.useWhen),
|
|
128
|
-
),
|
|
129
|
-
),
|
|
130
|
-
),
|
|
131
|
-
),
|
|
132
|
-
showBackLink
|
|
133
|
-
? p(
|
|
134
|
-
{ className: "see-all-link" },
|
|
135
|
-
a({ href: "#/tool" }, "See all tools →"),
|
|
136
|
-
)
|
|
137
|
-
: null,
|
|
138
|
-
)
|
|
139
|
-
: null,
|
|
140
|
-
|
|
141
|
-
// Implementation Reference
|
|
142
|
-
view.implementationReference
|
|
143
|
-
? div(
|
|
144
|
-
{ className: "detail-section" },
|
|
145
|
-
heading2({ className: "section-title" }, "Implementation Patterns"),
|
|
146
|
-
createMarkdownTextarea({
|
|
147
|
-
markdown: view.implementationReference,
|
|
148
|
-
description:
|
|
149
|
-
"Project-specific implementation guidance for this skill.",
|
|
150
|
-
minHeight: 450,
|
|
151
|
-
}),
|
|
152
|
-
)
|
|
153
|
-
: null,
|
|
154
|
-
|
|
155
110
|
// Used in Disciplines and Linked to Drivers in two columns
|
|
156
111
|
view.relatedDisciplines.length > 0 || view.relatedDrivers.length > 0
|
|
157
112
|
? div(
|
|
@@ -222,5 +177,62 @@ export function skillToDOM(
|
|
|
222
177
|
),
|
|
223
178
|
)
|
|
224
179
|
: null,
|
|
180
|
+
|
|
181
|
+
// Recommended Tools
|
|
182
|
+
showToolsAndPatterns && view.toolReferences.length > 0
|
|
183
|
+
? div(
|
|
184
|
+
{ className: "detail-section" },
|
|
185
|
+
heading2({ className: "section-title" }, "Recommended Tools"),
|
|
186
|
+
table(
|
|
187
|
+
{ className: "tools-table" },
|
|
188
|
+
thead({}, tr({}, th({}, "Tool"), th({}, "Use When"))),
|
|
189
|
+
tbody(
|
|
190
|
+
{},
|
|
191
|
+
...view.toolReferences.map((tool) =>
|
|
192
|
+
tr(
|
|
193
|
+
{},
|
|
194
|
+
td(
|
|
195
|
+
{ className: "tool-name-cell" },
|
|
196
|
+
tool.simpleIcon
|
|
197
|
+
? createToolIcon(tool.simpleIcon, tool.name)
|
|
198
|
+
: null,
|
|
199
|
+
tool.url
|
|
200
|
+
? a(
|
|
201
|
+
{
|
|
202
|
+
href: tool.url,
|
|
203
|
+
target: "_blank",
|
|
204
|
+
rel: "noopener noreferrer",
|
|
205
|
+
},
|
|
206
|
+
tool.name,
|
|
207
|
+
)
|
|
208
|
+
: tool.name,
|
|
209
|
+
),
|
|
210
|
+
td({}, tool.useWhen),
|
|
211
|
+
),
|
|
212
|
+
),
|
|
213
|
+
),
|
|
214
|
+
),
|
|
215
|
+
showBackLink
|
|
216
|
+
? p(
|
|
217
|
+
{ className: "see-all-link" },
|
|
218
|
+
a({ href: "#/tool" }, "See all tools →"),
|
|
219
|
+
)
|
|
220
|
+
: null,
|
|
221
|
+
)
|
|
222
|
+
: null,
|
|
223
|
+
|
|
224
|
+
// Implementation Reference
|
|
225
|
+
showToolsAndPatterns && view.implementationReference
|
|
226
|
+
? div(
|
|
227
|
+
{ className: "detail-section" },
|
|
228
|
+
heading2({ className: "section-title" }, "Implementation Patterns"),
|
|
229
|
+
createCodeDisplay({
|
|
230
|
+
content: view.implementationReference,
|
|
231
|
+
description:
|
|
232
|
+
"Project-specific implementation guidance for this skill.",
|
|
233
|
+
minHeight: 450,
|
|
234
|
+
}),
|
|
235
|
+
)
|
|
236
|
+
: null,
|
|
225
237
|
);
|
|
226
238
|
}
|
|
@@ -127,7 +127,9 @@ export function prepareSkillDetail(
|
|
|
127
127
|
relatedDisciplines,
|
|
128
128
|
relatedTracks,
|
|
129
129
|
relatedDrivers,
|
|
130
|
-
toolReferences: skill.toolReferences || []
|
|
130
|
+
toolReferences: (skill.toolReferences || [])
|
|
131
|
+
.slice()
|
|
132
|
+
.sort((a, b) => a.name.localeCompare(b.name)),
|
|
131
133
|
implementationReference: skill.implementationReference || null,
|
|
132
134
|
};
|
|
133
135
|
}
|
|
@@ -32,7 +32,7 @@ export function stageListToMicrodata(stages) {
|
|
|
32
32
|
? `→ ${stage.handoffs.map((h) => h.target).join(", ")}`
|
|
33
33
|
: "";
|
|
34
34
|
return `${openTag("article", { itemtype: "Stage", itemid: `#${stage.id}` })}
|
|
35
|
-
${prop("h2", "name", `${stage.
|
|
35
|
+
${prop("h2", "name", `${stage.emojiIcon} ${stage.name}`)}
|
|
36
36
|
${prop("p", "description", stage.truncatedDescription)}
|
|
37
37
|
${handoffText ? `<p>Handoffs: ${handoffText}</p>` : ""}
|
|
38
38
|
</article>`;
|
|
@@ -100,7 +100,7 @@ ${prop("p", "prompt", h.prompt)}
|
|
|
100
100
|
${openTag("article", { itemtype: "Stage", itemid: `#${view.id}` })}
|
|
101
101
|
${prop("h1", "name", view.name)}
|
|
102
102
|
${metaTag("id", view.id)}
|
|
103
|
-
${stage.
|
|
103
|
+
${stage.emojiIcon ? metaTag("emojiIcon", stage.emojiIcon) : ""}
|
|
104
104
|
${prop("p", "description", view.description)}
|
|
105
105
|
${sections.join("\n")}
|
|
106
106
|
</article>
|
|
@@ -10,7 +10,7 @@ import { truncate } from "../shared.js";
|
|
|
10
10
|
* @typedef {Object} StageListItem
|
|
11
11
|
* @property {string} id
|
|
12
12
|
* @property {string} name
|
|
13
|
-
* @property {string}
|
|
13
|
+
* @property {string} emojiIcon
|
|
14
14
|
* @property {string} description
|
|
15
15
|
* @property {string} truncatedDescription
|
|
16
16
|
* @property {Array<{target: string, label: string}>} handoffs
|
|
@@ -27,7 +27,7 @@ export function prepareStagesList(stages, descriptionLimit = 150) {
|
|
|
27
27
|
return {
|
|
28
28
|
id: stage.id,
|
|
29
29
|
name: stage.name,
|
|
30
|
-
|
|
30
|
+
emojiIcon: stage.emojiIcon,
|
|
31
31
|
description: stage.description,
|
|
32
32
|
truncatedDescription: truncate(stage.description, descriptionLimit),
|
|
33
33
|
handoffs: (stage.handoffs || []).map((h) => ({
|
|
@@ -80,5 +80,5 @@ export function prepareStageDetail(stage) {
|
|
|
80
80
|
*/
|
|
81
81
|
export function getStageEmoji(stages, stageId) {
|
|
82
82
|
const stage = stages.find((s) => s.id === stageId);
|
|
83
|
-
return stage?.
|
|
83
|
+
return stage?.emojiIcon;
|
|
84
84
|
}
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* @typedef {Object} AggregatedTool
|
|
17
17
|
* @property {string} name
|
|
18
18
|
* @property {string} [url]
|
|
19
|
+
* @property {string} [simpleIcon]
|
|
19
20
|
* @property {string} description
|
|
20
21
|
* @property {ToolUsage[]} usages
|
|
21
22
|
*/
|
|
@@ -42,10 +43,15 @@ export function aggregateTools(skills) {
|
|
|
42
43
|
const existing = toolMap.get(tool.name);
|
|
43
44
|
if (existing) {
|
|
44
45
|
existing.usages.push(usage);
|
|
46
|
+
// Prefer simpleIcon from first occurrence that has one
|
|
47
|
+
if (!existing.simpleIcon && tool.simpleIcon) {
|
|
48
|
+
existing.simpleIcon = tool.simpleIcon;
|
|
49
|
+
}
|
|
45
50
|
} else {
|
|
46
51
|
toolMap.set(tool.name, {
|
|
47
52
|
name: tool.name,
|
|
48
53
|
url: tool.url,
|
|
54
|
+
simpleIcon: tool.simpleIcon,
|
|
49
55
|
description: tool.description,
|
|
50
56
|
usages: [usage],
|
|
51
57
|
});
|
package/app/handout-main.js
CHANGED
|
@@ -41,17 +41,17 @@ import { sortTracksByName } from "./formatters/track/shared.js";
|
|
|
41
41
|
/**
|
|
42
42
|
* Create a chapter cover page
|
|
43
43
|
* @param {Object} params
|
|
44
|
-
* @param {string} params.
|
|
44
|
+
* @param {string} params.emojiIcon - Chapter emoji
|
|
45
45
|
* @param {string} params.title - Chapter title
|
|
46
46
|
* @param {string} params.description - Chapter description
|
|
47
47
|
* @returns {HTMLElement}
|
|
48
48
|
*/
|
|
49
|
-
function createChapterCover({
|
|
49
|
+
function createChapterCover({ emojiIcon, title, description }) {
|
|
50
50
|
return div(
|
|
51
51
|
{ className: "chapter-cover" },
|
|
52
52
|
h1(
|
|
53
53
|
{ className: "chapter-title" },
|
|
54
|
-
|
|
54
|
+
emojiIcon,
|
|
55
55
|
" ",
|
|
56
56
|
span({ className: "gradient-text" }, title),
|
|
57
57
|
),
|
|
@@ -111,7 +111,7 @@ function renderIndex(data) {
|
|
|
111
111
|
{ className: "page-header" },
|
|
112
112
|
heading1(
|
|
113
113
|
{ className: "page-title" },
|
|
114
|
-
`${framework.
|
|
114
|
+
`${framework.emojiIcon} ${framework.title} Handouts`,
|
|
115
115
|
),
|
|
116
116
|
p(
|
|
117
117
|
{ className: "page-description" },
|
|
@@ -190,7 +190,7 @@ function renderDriverHandout(data) {
|
|
|
190
190
|
const content = div(
|
|
191
191
|
{},
|
|
192
192
|
createChapterCover({
|
|
193
|
-
|
|
193
|
+
emojiIcon: getConceptEmoji(framework, "driver"),
|
|
194
194
|
title: framework.entityDefinitions.driver.title,
|
|
195
195
|
description: framework.entityDefinitions.driver.description,
|
|
196
196
|
}),
|
|
@@ -227,13 +227,14 @@ function renderSkillHandout(data) {
|
|
|
227
227
|
drivers: data.drivers,
|
|
228
228
|
capabilities: data.capabilities,
|
|
229
229
|
showBackLink: false,
|
|
230
|
+
showToolsAndPatterns: false,
|
|
230
231
|
});
|
|
231
232
|
});
|
|
232
233
|
|
|
233
234
|
const content = div(
|
|
234
235
|
{},
|
|
235
236
|
createChapterCover({
|
|
236
|
-
|
|
237
|
+
emojiIcon: getConceptEmoji(framework, "skill"),
|
|
237
238
|
title: framework.entityDefinitions.skill.title,
|
|
238
239
|
description: framework.entityDefinitions.skill.description,
|
|
239
240
|
}),
|
|
@@ -261,7 +262,7 @@ function renderBehaviourHandout(data) {
|
|
|
261
262
|
const content = div(
|
|
262
263
|
{},
|
|
263
264
|
createChapterCover({
|
|
264
|
-
|
|
265
|
+
emojiIcon: getConceptEmoji(framework, "behaviour"),
|
|
265
266
|
title: framework.entityDefinitions.behaviour.title,
|
|
266
267
|
description: framework.entityDefinitions.behaviour.description,
|
|
267
268
|
}),
|
|
@@ -322,7 +323,7 @@ function renderJobHandout(data) {
|
|
|
322
323
|
{},
|
|
323
324
|
// Disciplines chapter
|
|
324
325
|
createChapterCover({
|
|
325
|
-
|
|
326
|
+
emojiIcon: getConceptEmoji(framework, "discipline"),
|
|
326
327
|
title: framework.entityDefinitions.discipline.title,
|
|
327
328
|
description: framework.entityDefinitions.discipline.description,
|
|
328
329
|
}),
|
|
@@ -330,7 +331,7 @@ function renderJobHandout(data) {
|
|
|
330
331
|
|
|
331
332
|
// Grades chapter (moved before Tracks)
|
|
332
333
|
createChapterCover({
|
|
333
|
-
|
|
334
|
+
emojiIcon: getConceptEmoji(framework, "grade"),
|
|
334
335
|
title: framework.entityDefinitions.grade.title,
|
|
335
336
|
description: framework.entityDefinitions.grade.description,
|
|
336
337
|
}),
|
|
@@ -338,7 +339,7 @@ function renderJobHandout(data) {
|
|
|
338
339
|
|
|
339
340
|
// Tracks chapter (moved after Grades)
|
|
340
341
|
createChapterCover({
|
|
341
|
-
|
|
342
|
+
emojiIcon: getConceptEmoji(framework, "track"),
|
|
342
343
|
title: framework.entityDefinitions.track.title,
|
|
343
344
|
description: framework.entityDefinitions.track.description,
|
|
344
345
|
}),
|
|
@@ -393,7 +394,7 @@ function populateBrandHeader(framework) {
|
|
|
393
394
|
header.appendChild(
|
|
394
395
|
a(
|
|
395
396
|
{ className: "brand-title", href: "#/" },
|
|
396
|
-
`${framework.
|
|
397
|
+
`${framework.emojiIcon} ${framework.title}`,
|
|
397
398
|
),
|
|
398
399
|
);
|
|
399
400
|
header.appendChild(span({ className: "brand-tag" }, framework.tag));
|
package/app/index.html
CHANGED
|
@@ -4,11 +4,17 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Engineering Pathway</title>
|
|
7
|
-
<link rel="
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
9
|
+
<link
|
|
10
|
+
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500&display=swap"
|
|
11
|
+
rel="stylesheet"
|
|
12
|
+
/>
|
|
8
13
|
<link
|
|
9
14
|
rel="stylesheet"
|
|
10
15
|
href="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism.min.css"
|
|
11
16
|
/>
|
|
17
|
+
<link rel="stylesheet" href="css/bundles/app.css" />
|
|
12
18
|
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/prism.min.js"></script>
|
|
13
19
|
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-markdown.min.js"></script>
|
|
14
20
|
<script type="importmap">
|
package/app/lib/card-mappers.js
CHANGED
|
@@ -181,13 +181,40 @@ export function toolToCardConfig(tool, capabilities) {
|
|
|
181
181
|
// Create skills list as card content
|
|
182
182
|
const skillsList = createSkillsList(tool.usages, capabilities);
|
|
183
183
|
|
|
184
|
+
// Create icon element if available
|
|
185
|
+
const icon = tool.simpleIcon
|
|
186
|
+
? createToolIcon(tool.simpleIcon, tool.name)
|
|
187
|
+
: null;
|
|
188
|
+
|
|
184
189
|
return {
|
|
185
190
|
title: tool.name,
|
|
186
191
|
description: tool.description,
|
|
187
192
|
// Docs link in header badges (upper right)
|
|
188
193
|
badges: tool.url ? [createExternalLink("Docs →", tool.url)] : [],
|
|
189
194
|
content: skillsList,
|
|
195
|
+
icon,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Create a tool icon element using Simple Icons CDN
|
|
201
|
+
* @param {string} slug - Simple Icons slug (e.g., 'terraform', 'docker')
|
|
202
|
+
* @param {string} name - Tool name for alt text
|
|
203
|
+
* @returns {HTMLElement}
|
|
204
|
+
*/
|
|
205
|
+
export function createToolIcon(slug, name) {
|
|
206
|
+
const img = document.createElement("img");
|
|
207
|
+
// Use black color for consistent monochrome appearance
|
|
208
|
+
img.src = `https://cdn.simpleicons.org/${slug}/000000`;
|
|
209
|
+
img.alt = `${name} icon`;
|
|
210
|
+
img.className = "tool-icon";
|
|
211
|
+
img.width = 28;
|
|
212
|
+
img.height = 28;
|
|
213
|
+
// Gracefully handle missing icons
|
|
214
|
+
img.onerror = () => {
|
|
215
|
+
img.style.display = "none";
|
|
190
216
|
};
|
|
217
|
+
return img;
|
|
191
218
|
}
|
|
192
219
|
|
|
193
220
|
/**
|
package/app/model/agent.js
CHANGED
|
@@ -320,8 +320,11 @@ function estimateBodyDataLength(bodyData) {
|
|
|
320
320
|
}
|
|
321
321
|
|
|
322
322
|
// Array fields
|
|
323
|
-
if (bodyData.
|
|
324
|
-
|
|
323
|
+
if (bodyData.skillIndex) {
|
|
324
|
+
for (const skill of bodyData.skillIndex) {
|
|
325
|
+
length +=
|
|
326
|
+
skill.name.length + skill.dirname.length + skill.useWhen.length + 50;
|
|
327
|
+
}
|
|
325
328
|
}
|
|
326
329
|
if (bodyData.beforeMakingChanges) {
|
|
327
330
|
for (const item of bodyData.beforeMakingChanges) {
|
|
@@ -491,6 +494,7 @@ function getChecklistStage(stageId) {
|
|
|
491
494
|
* @param {Array} params.derivedSkills - Skills sorted by level
|
|
492
495
|
* @param {Array} params.derivedBehaviours - Behaviours sorted by maturity
|
|
493
496
|
* @param {Array} params.agentBehaviours - Agent behaviour definitions
|
|
497
|
+
* @param {Array} params.skills - All skill definitions (for agent section lookup)
|
|
494
498
|
* @param {string} params.checklistMarkdown - Pre-formatted checklist markdown
|
|
495
499
|
* @returns {Object} Structured profile body data
|
|
496
500
|
*/
|
|
@@ -503,6 +507,7 @@ function buildStageProfileBodyData({
|
|
|
503
507
|
derivedSkills,
|
|
504
508
|
derivedBehaviours,
|
|
505
509
|
agentBehaviours,
|
|
510
|
+
skills,
|
|
506
511
|
checklistMarkdown,
|
|
507
512
|
}) {
|
|
508
513
|
const name = `${humanDiscipline.specialization || humanDiscipline.name} - ${humanTrack.name}`;
|
|
@@ -532,8 +537,18 @@ function buildStageProfileBodyData({
|
|
|
532
537
|
? substituteTemplateVars(rawDelegation, humanDiscipline)
|
|
533
538
|
: null;
|
|
534
539
|
|
|
535
|
-
//
|
|
536
|
-
const
|
|
540
|
+
// Build skill index from derived skills with agent sections
|
|
541
|
+
const skillIndex = derivedSkills
|
|
542
|
+
.map((derived) => {
|
|
543
|
+
const skill = skills.find((s) => s.id === derived.skillId);
|
|
544
|
+
if (!skill?.agent) return null;
|
|
545
|
+
return {
|
|
546
|
+
name: derived.skillName,
|
|
547
|
+
dirname: skill.agent.name,
|
|
548
|
+
useWhen: skill.agent.useWhen?.trim() || "",
|
|
549
|
+
};
|
|
550
|
+
})
|
|
551
|
+
.filter(Boolean);
|
|
537
552
|
|
|
538
553
|
// Operational Context - use track's roleContext (shared with human job descriptions)
|
|
539
554
|
const operationalContext = humanTrack.roleContext.trim();
|
|
@@ -557,7 +572,7 @@ function buildStageProfileBodyData({
|
|
|
557
572
|
stageDescription: stage.description,
|
|
558
573
|
identity: identity.trim(),
|
|
559
574
|
priority: priority ? priority.trim() : null,
|
|
560
|
-
|
|
575
|
+
skillIndex,
|
|
561
576
|
beforeMakingChanges,
|
|
562
577
|
delegation: delegation ? delegation.trim() : null,
|
|
563
578
|
operationalContext,
|
|
@@ -719,6 +734,7 @@ export function generateStageAgentProfile({
|
|
|
719
734
|
derivedSkills: agent.derivedSkills,
|
|
720
735
|
derivedBehaviours: agent.derivedBehaviours,
|
|
721
736
|
agentBehaviours,
|
|
737
|
+
skills,
|
|
722
738
|
checklistMarkdown,
|
|
723
739
|
});
|
|
724
740
|
|
package/app/model/checklist.js
CHANGED
|
@@ -72,7 +72,7 @@ export function deriveChecklist({
|
|
|
72
72
|
capability: {
|
|
73
73
|
id: capability.id,
|
|
74
74
|
name: capability.name,
|
|
75
|
-
|
|
75
|
+
emojiIcon: capability.emojiIcon,
|
|
76
76
|
},
|
|
77
77
|
items: stageData.ready,
|
|
78
78
|
});
|
|
@@ -94,7 +94,7 @@ export function formatChecklistMarkdown(checklist) {
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
const sections = checklist.map(({ skill, capability, items }) => {
|
|
97
|
-
const header = `**${capability.
|
|
97
|
+
const header = `**${capability.emojiIcon} ${skill.name}**`;
|
|
98
98
|
const itemList = items.map((item) => `- [ ] ${item}`).join("\n");
|
|
99
99
|
return `${header}\n\n${itemList}`;
|
|
100
100
|
});
|
package/app/model/derivation.js
CHANGED
|
@@ -437,7 +437,7 @@ function generateJobId(discipline, grade, track = null) {
|
|
|
437
437
|
* @param {import('./levels.js').SkillMatrixEntry[]} params.skillMatrix - Derived skill matrix for the job
|
|
438
438
|
* @param {Object[]} params.capabilities - Capability definitions with responsibilities
|
|
439
439
|
* @param {import('./levels.js').Discipline} params.discipline - The discipline (determines which responsibilities to use)
|
|
440
|
-
* @returns {Array<{capability: string, capabilityName: string,
|
|
440
|
+
* @returns {Array<{capability: string, capabilityName: string, emojiIcon: string, responsibility: string, level: string}>}
|
|
441
441
|
*/
|
|
442
442
|
export function deriveResponsibilities({
|
|
443
443
|
skillMatrix,
|
|
@@ -484,7 +484,7 @@ export function deriveResponsibilities({
|
|
|
484
484
|
responsibilities.push({
|
|
485
485
|
capability: capabilityId,
|
|
486
486
|
capabilityName: capability.name,
|
|
487
|
-
|
|
487
|
+
emojiIcon: capability.emojiIcon || "💡",
|
|
488
488
|
displayOrder: capability.displayOrder ?? 999,
|
|
489
489
|
responsibility: responsibilityText,
|
|
490
490
|
level,
|
package/app/model/levels.js
CHANGED
|
@@ -220,7 +220,7 @@ export function getCapabilityOrder(capabilities) {
|
|
|
220
220
|
*/
|
|
221
221
|
export function getCapabilityEmoji(capabilities, capabilityId) {
|
|
222
222
|
const capability = getCapabilityById(capabilities, capabilityId);
|
|
223
|
-
return capability?.
|
|
223
|
+
return capability?.emojiIcon || "💡";
|
|
224
224
|
}
|
|
225
225
|
|
|
226
226
|
/**
|
|
@@ -597,5 +597,5 @@ export function behaviourMaturityMeetsRequirement(actual, required) {
|
|
|
597
597
|
* @returns {string} The emoji for the concept or default "💡"
|
|
598
598
|
*/
|
|
599
599
|
export function getConceptEmoji(framework, concept) {
|
|
600
|
-
return framework?.entityDefinitions?.[concept]?.
|
|
600
|
+
return framework?.entityDefinitions?.[concept]?.emojiIcon || "💡";
|
|
601
601
|
}
|
package/app/model/validation.js
CHANGED
|
@@ -1239,12 +1239,12 @@ function validateCapability(capability, index) {
|
|
|
1239
1239
|
),
|
|
1240
1240
|
);
|
|
1241
1241
|
}
|
|
1242
|
-
if (!capability.
|
|
1242
|
+
if (!capability.emojiIcon) {
|
|
1243
1243
|
warnings.push(
|
|
1244
1244
|
createWarning(
|
|
1245
1245
|
"MISSING_OPTIONAL",
|
|
1246
|
-
"Capability missing
|
|
1247
|
-
`${path}.
|
|
1246
|
+
"Capability missing emojiIcon",
|
|
1247
|
+
`${path}.emojiIcon`,
|
|
1248
1248
|
),
|
|
1249
1249
|
);
|
|
1250
1250
|
}
|