@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,320 @@
1
+ /**
2
+ * Progression table component
3
+ * Displays skill or behaviour changes between current and target levels
4
+ */
5
+
6
+ /** @typedef {import('../types.js').SkillChangeItem} SkillChangeItem */
7
+ /** @typedef {import('../types.js').BehaviourChangeItem} BehaviourChangeItem */
8
+
9
+ import {
10
+ div,
11
+ table,
12
+ thead,
13
+ tbody,
14
+ tr,
15
+ th,
16
+ td,
17
+ a,
18
+ span,
19
+ } from "../lib/render.js";
20
+ import { formatLevel } from "../lib/render.js";
21
+ import { createLevelCell, createEmptyLevelCell } from "./detail.js";
22
+ import { createBadge } from "./card.js";
23
+
24
+ /**
25
+ * Create a progression table showing changes
26
+ * @param {SkillChangeItem[]|BehaviourChangeItem[]} changes - Array of change objects
27
+ * @param {'skill'|'behaviour'} type - Type of changes
28
+ * @returns {HTMLElement}
29
+ */
30
+ export function createProgressionTable(changes, type = "skill") {
31
+ if (!changes || changes.length === 0) {
32
+ return div({ className: "empty-state" }, "No changes required");
33
+ }
34
+
35
+ const isSkill = type === "skill";
36
+ const maxLevels = 5;
37
+
38
+ // Separate into changes, gained, lost, and no-changes
39
+ const gained = changes.filter((c) => c.isGained);
40
+ const lost = changes.filter((c) => c.isLost);
41
+ const changesRequired = changes.filter(
42
+ (c) => c.change !== 0 && !c.isGained && !c.isLost,
43
+ );
44
+ const noChanges = changes.filter((c) => c.change === 0);
45
+
46
+ const createRow = (item) => {
47
+ // Handle gained skills (new in target role)
48
+ if (item.isGained) {
49
+ if (isSkill) {
50
+ return tr(
51
+ { className: "change-gained" },
52
+ td(
53
+ {},
54
+ a({ href: `#/skill/${item.id}` }, item.name),
55
+ span({ className: "gained-badge" }, "NEW"),
56
+ ),
57
+ td({}, createBadge(item.capability, item.capability)),
58
+ td({}, createBadge(formatLevel(item.type), item.type)),
59
+ createEmptyLevelCell(),
60
+ createLevelCell(item.targetIndex, maxLevels, item.targetLevel),
61
+ td(
62
+ { className: "change-cell change-gained" },
63
+ span({ className: "change-indicator" }, "+"),
64
+ ),
65
+ );
66
+ } else {
67
+ return tr(
68
+ { className: "change-gained" },
69
+ td(
70
+ {},
71
+ a({ href: `#/behaviour/${item.id}` }, item.name),
72
+ span({ className: "gained-badge" }, "NEW"),
73
+ ),
74
+ createEmptyLevelCell(),
75
+ createLevelCell(item.targetIndex, maxLevels, item.targetLevel),
76
+ td(
77
+ { className: "change-cell change-gained" },
78
+ span({ className: "change-indicator" }, "+"),
79
+ ),
80
+ );
81
+ }
82
+ }
83
+
84
+ // Handle lost skills (removed in target role)
85
+ if (item.isLost) {
86
+ if (isSkill) {
87
+ return tr(
88
+ { className: "change-lost" },
89
+ td(
90
+ {},
91
+ a({ href: `#/skill/${item.id}` }, item.name),
92
+ span({ className: "lost-badge" }, "REMOVED"),
93
+ ),
94
+ td({}, createBadge(item.capability, item.capability)),
95
+ td({}, createBadge(formatLevel(item.type), item.type)),
96
+ createLevelCell(item.currentIndex, maxLevels, item.currentLevel),
97
+ createEmptyLevelCell(),
98
+ td(
99
+ { className: "change-cell change-lost" },
100
+ span({ className: "change-indicator" }, "−"),
101
+ ),
102
+ );
103
+ } else {
104
+ return tr(
105
+ { className: "change-lost" },
106
+ td(
107
+ {},
108
+ a({ href: `#/behaviour/${item.id}` }, item.name),
109
+ span({ className: "lost-badge" }, "REMOVED"),
110
+ ),
111
+ createLevelCell(item.currentIndex, maxLevels, item.currentLevel),
112
+ createEmptyLevelCell(),
113
+ td(
114
+ { className: "change-cell change-lost" },
115
+ span({ className: "change-indicator" }, "−"),
116
+ ),
117
+ );
118
+ }
119
+ }
120
+
121
+ // Normal change (both current and target exist)
122
+ const changeClass =
123
+ item.change > 0
124
+ ? "change-up"
125
+ : item.change < 0
126
+ ? "change-down"
127
+ : "change-same";
128
+ const changeIcon = item.change > 0 ? "↑" : item.change < 0 ? "↓" : "—";
129
+ const changeText =
130
+ item.change !== 0 ? `${changeIcon} ${Math.abs(item.change)}` : "—";
131
+
132
+ if (isSkill) {
133
+ return tr(
134
+ { className: changeClass },
135
+ td({}, a({ href: `#/skill/${item.id}` }, item.name)),
136
+ td({}, createBadge(item.capability, item.capability)),
137
+ td({}, createBadge(formatLevel(item.type), item.type)),
138
+ createLevelCell(item.currentIndex, maxLevels, item.currentLevel),
139
+ createLevelCell(item.targetIndex, maxLevels, item.targetLevel),
140
+ td(
141
+ { className: `change-cell ${changeClass}` },
142
+ span({ className: "change-indicator" }, changeText),
143
+ ),
144
+ );
145
+ } else {
146
+ return tr(
147
+ { className: changeClass },
148
+ td({}, a({ href: `#/behaviour/${item.id}` }, item.name)),
149
+ createLevelCell(item.currentIndex, maxLevels, item.currentLevel),
150
+ createLevelCell(item.targetIndex, maxLevels, item.targetLevel),
151
+ td(
152
+ { className: `change-cell ${changeClass}` },
153
+ span({ className: "change-indicator" }, changeText),
154
+ ),
155
+ );
156
+ }
157
+ };
158
+
159
+ const skillHeaders = tr(
160
+ {},
161
+ th({}, isSkill ? "Skill" : "Behaviour"),
162
+ ...(isSkill ? [th({}, "Capability"), th({}, "Type")] : []),
163
+ th({}, "Current"),
164
+ th({}, "Target"),
165
+ th({}, "Change"),
166
+ );
167
+
168
+ const content = [];
169
+
170
+ // Show new skills first (in target role but not current)
171
+ if (gained.length > 0) {
172
+ content.push(
173
+ div(
174
+ { className: "progression-group" },
175
+ div(
176
+ { className: "progression-group-header gained-header" },
177
+ span({ className: "group-icon" }, "➕"),
178
+ span(
179
+ {},
180
+ `${gained.length} new ${type}${gained.length > 1 ? "s" : ""}`,
181
+ ),
182
+ ),
183
+ div(
184
+ { className: "table-container" },
185
+ table(
186
+ {
187
+ className: `table progression-table ${isSkill ? "skill-table" : "behaviour-table"}`,
188
+ },
189
+ thead({}, skillHeaders),
190
+ tbody({}, ...gained.map(createRow)),
191
+ ),
192
+ ),
193
+ ),
194
+ );
195
+ }
196
+
197
+ // Show level changes
198
+ if (changesRequired.length > 0) {
199
+ content.push(
200
+ div(
201
+ { className: "progression-group" },
202
+ div(
203
+ { className: "progression-group-header" },
204
+ span({ className: "group-icon" }, "📈"),
205
+ span(
206
+ {},
207
+ `${changesRequired.length} ${type}${changesRequired.length > 1 ? "s" : ""} need growth`,
208
+ ),
209
+ ),
210
+ div(
211
+ { className: "table-container" },
212
+ table(
213
+ {
214
+ className: `table progression-table ${isSkill ? "skill-table" : "behaviour-table"}`,
215
+ },
216
+ thead({}, skillHeaders),
217
+ tbody({}, ...changesRequired.map(createRow)),
218
+ ),
219
+ ),
220
+ ),
221
+ );
222
+ }
223
+
224
+ // Show removed skills (in current role but not target)
225
+ if (lost.length > 0) {
226
+ content.push(
227
+ div(
228
+ { className: "progression-group" },
229
+ div(
230
+ { className: "progression-group-header lost-header" },
231
+ span({ className: "group-icon" }, "➖"),
232
+ span(
233
+ {},
234
+ `${lost.length} ${type}${lost.length > 1 ? "s" : ""} removed`,
235
+ ),
236
+ ),
237
+ div(
238
+ { className: "table-container" },
239
+ table(
240
+ {
241
+ className: `table progression-table ${isSkill ? "skill-table" : "behaviour-table"}`,
242
+ },
243
+ thead({}, skillHeaders),
244
+ tbody({}, ...lost.map(createRow)),
245
+ ),
246
+ ),
247
+ ),
248
+ );
249
+ }
250
+
251
+ // Show no changes (collapsed by default)
252
+ if (noChanges.length > 0) {
253
+ const noChangeHeaders = tr(
254
+ {},
255
+ th({}, isSkill ? "Skill" : "Behaviour"),
256
+ ...(isSkill ? [th({}, "Capability"), th({}, "Type")] : []),
257
+ th({}, "Current"),
258
+ th({}, "Target"),
259
+ th({}, "Change"),
260
+ );
261
+
262
+ const detailsId = `no-changes-${type}-${Date.now()}`;
263
+ content.push(
264
+ div(
265
+ { className: "progression-group no-change-group" },
266
+ createCollapsibleHeader(
267
+ detailsId,
268
+ `${noChanges.length} ${type}${noChanges.length > 1 ? "s" : ""} unchanged`,
269
+ "✓",
270
+ ),
271
+ div(
272
+ {
273
+ className: "collapsible-content",
274
+ id: detailsId,
275
+ style: "display: none",
276
+ },
277
+ div(
278
+ { className: "table-container" },
279
+ table(
280
+ {
281
+ className: `table progression-table ${isSkill ? "skill-table" : "behaviour-table"}`,
282
+ },
283
+ thead({}, noChangeHeaders),
284
+ tbody({}, ...noChanges.map(createRow)),
285
+ ),
286
+ ),
287
+ ),
288
+ ),
289
+ );
290
+ }
291
+
292
+ return div({ className: "progression-tables" }, ...content);
293
+ }
294
+
295
+ /**
296
+ * Create a collapsible header
297
+ */
298
+ function createCollapsibleHeader(targetId, text, icon) {
299
+ const header = div(
300
+ { className: "progression-group-header collapsible-header" },
301
+ span({ className: "group-icon" }, icon),
302
+ span({}, text),
303
+ span({ className: "collapse-indicator" }, "▶"),
304
+ );
305
+
306
+ header.style.cursor = "pointer";
307
+ header.addEventListener("click", () => {
308
+ const content = document.getElementById(targetId);
309
+ const indicator = header.querySelector(".collapse-indicator");
310
+ if (content.style.display === "none") {
311
+ content.style.display = "block";
312
+ indicator.textContent = "▼";
313
+ } else {
314
+ content.style.display = "none";
315
+ indicator.textContent = "▶";
316
+ }
317
+ });
318
+
319
+ return header;
320
+ }
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Radar chart component wrapper
3
+ */
4
+
5
+ /** @typedef {import('../types.js').SkillMatrixItem} SkillMatrixItem */
6
+ /** @typedef {import('../types.js').BehaviourProfileItem} BehaviourProfileItem */
7
+
8
+ import { RadarChart } from "../lib/radar.js";
9
+ import { div, h3 } from "../lib/render.js";
10
+ import {
11
+ getSkillLevelIndex,
12
+ getBehaviourMaturityIndex,
13
+ formatLevel,
14
+ } from "../lib/render.js";
15
+
16
+ /**
17
+ * Create a skill radar chart
18
+ * @param {SkillMatrixItem[]} skillMatrix - Skill matrix entries
19
+ * @param {Object} [options]
20
+ * @returns {HTMLElement}
21
+ */
22
+ export function createSkillRadar(skillMatrix, options = {}) {
23
+ const container = div(
24
+ { className: "radar-container" },
25
+ h3({ className: "radar-title" }, options.title || "Skills Radar"),
26
+ div({ className: "radar-chart-wrapper", id: "skill-radar-wrapper" }),
27
+ );
28
+
29
+ // Render chart after container is in DOM
30
+ setTimeout(() => {
31
+ const wrapper = container.querySelector("#skill-radar-wrapper");
32
+ if (!wrapper || !skillMatrix || skillMatrix.length === 0) return;
33
+
34
+ const data = skillMatrix.map((skill) => ({
35
+ label: skill.skillName,
36
+ value: getSkillLevelIndex(skill.level),
37
+ maxValue: 5,
38
+ description: `${formatLevel(skill.type)} skill - ${formatLevel(skill.level)}`,
39
+ }));
40
+
41
+ const chart = new RadarChart({
42
+ container: wrapper,
43
+ data,
44
+ options: {
45
+ levels: 5,
46
+ color: options.color || "#3b82f6",
47
+ strokeColor: options.strokeColor || "#2563eb",
48
+ size: options.size || 400,
49
+ showLabels: true,
50
+ showTooltips: true,
51
+ },
52
+ });
53
+
54
+ chart.render();
55
+ }, 0);
56
+
57
+ return container;
58
+ }
59
+
60
+ /**
61
+ * Create a behaviour radar chart
62
+ * @param {BehaviourProfileItem[]} behaviourProfile - Behaviour profile entries
63
+ * @param {Object} [options]
64
+ * @returns {HTMLElement}
65
+ */
66
+ export function createBehaviourRadar(behaviourProfile, options = {}) {
67
+ const container = div(
68
+ { className: "radar-container" },
69
+ h3({ className: "radar-title" }, options.title || "Behaviours Radar"),
70
+ div({ className: "radar-chart-wrapper", id: "behaviour-radar-wrapper" }),
71
+ );
72
+
73
+ // Render chart after container is in DOM
74
+ setTimeout(() => {
75
+ const wrapper = container.querySelector("#behaviour-radar-wrapper");
76
+ if (!wrapper || !behaviourProfile || behaviourProfile.length === 0) return;
77
+
78
+ const data = behaviourProfile.map((behaviour) => ({
79
+ label: behaviour.behaviourName,
80
+ value: getBehaviourMaturityIndex(behaviour.maturity),
81
+ maxValue: 5,
82
+ description: `${formatLevel(behaviour.maturity)}`,
83
+ }));
84
+
85
+ const chart = new RadarChart({
86
+ container: wrapper,
87
+ data,
88
+ options: {
89
+ levels: 5,
90
+ color: options.color || "#10b981",
91
+ strokeColor: options.strokeColor || "#059669",
92
+ size: options.size || 400,
93
+ showLabels: true,
94
+ showTooltips: true,
95
+ },
96
+ });
97
+
98
+ chart.render();
99
+ }, 0);
100
+
101
+ return container;
102
+ }
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Skill matrix display component
3
+ */
4
+
5
+ /** @typedef {import('../types.js').SkillMatrixItem} SkillMatrixItem */
6
+
7
+ import {
8
+ div,
9
+ span,
10
+ table,
11
+ thead,
12
+ tbody,
13
+ tr,
14
+ th,
15
+ td,
16
+ a,
17
+ } from "../lib/render.js";
18
+ import { getSkillLevelIndex } from "../lib/render.js";
19
+ import { createLevelCell } from "./detail.js";
20
+ import { createBadge } from "./card.js";
21
+ import { SKILL_LEVEL_ORDER } from "../model/levels.js";
22
+ import { truncate } from "../formatters/shared.js";
23
+
24
+ /**
25
+ * Sort skills by level descending (expert first), then alphabetically
26
+ * @param {SkillMatrixItem[]} skills
27
+ * @returns {SkillMatrixItem[]}
28
+ */
29
+ function sortByLevelDescending(skills) {
30
+ return [...skills].sort((a, b) => {
31
+ const levelA = SKILL_LEVEL_ORDER.indexOf(a.level);
32
+ const levelB = SKILL_LEVEL_ORDER.indexOf(b.level);
33
+ if (levelB !== levelA) {
34
+ return levelB - levelA;
35
+ }
36
+ return a.skillName.localeCompare(b.skillName);
37
+ });
38
+ }
39
+
40
+ /**
41
+ * Create a skill matrix table
42
+ * @param {SkillMatrixItem[]} skillMatrix - Skill matrix entries
43
+ * @returns {HTMLElement}
44
+ */
45
+ export function createSkillMatrix(skillMatrix) {
46
+ if (!skillMatrix || skillMatrix.length === 0) {
47
+ return div({ className: "empty-state" }, "No skills in matrix");
48
+ }
49
+
50
+ const sortedSkills = sortByLevelDescending(skillMatrix);
51
+
52
+ const rows = sortedSkills.map((skill) => {
53
+ const levelIndex = getSkillLevelIndex(skill.level);
54
+
55
+ return tr(
56
+ { className: skill.isHumanOnly ? "human-only-row" : "" },
57
+ td(
58
+ {},
59
+ a({ href: `#/skill/${skill.skillId}` }, skill.skillName),
60
+ skill.isHumanOnly
61
+ ? span(
62
+ {
63
+ className: "human-only-indicator",
64
+ title:
65
+ "Human-Only — Requires interpersonal skills; excluded from agents",
66
+ },
67
+ " 🤲",
68
+ )
69
+ : null,
70
+ ),
71
+ td({}, createBadge(skill.capability, skill.capability)),
72
+ createLevelCell(levelIndex, 5, skill.level),
73
+ td(
74
+ { className: "skill-description" },
75
+ truncate(skill.levelDescription, 80),
76
+ ),
77
+ );
78
+ });
79
+
80
+ return div(
81
+ { className: "table-container" },
82
+ table(
83
+ { className: "table matrix-table skill-matrix" },
84
+ thead(
85
+ {},
86
+ tr(
87
+ {},
88
+ th({}, "Skill"),
89
+ th({}, "Capability"),
90
+ th({}, "Level"),
91
+ th({}, "Description"),
92
+ ),
93
+ ),
94
+ tbody({}, ...rows),
95
+ ),
96
+ );
97
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Base Styles
3
+ *
4
+ * Typography, links, and body defaults using design tokens.
5
+ * Import after tokens.css and reset.css.
6
+ */
7
+
8
+ @layer base {
9
+ body {
10
+ font-family: var(--font-family);
11
+ font-size: var(--font-size-base);
12
+ line-height: 1.6;
13
+ color: var(--color-text);
14
+ background-color: var(--color-bg);
15
+ }
16
+
17
+ h1,
18
+ h2,
19
+ h3,
20
+ h4,
21
+ h5,
22
+ h6 {
23
+ margin-bottom: var(--space-md);
24
+ }
25
+
26
+ h1 {
27
+ font-size: var(--font-size-3xl);
28
+ font-weight: 700;
29
+ }
30
+
31
+ h2 {
32
+ font-size: var(--font-size-2xl);
33
+ }
34
+
35
+ h3 {
36
+ font-size: var(--font-size-xl);
37
+ }
38
+
39
+ h4 {
40
+ font-size: var(--font-size-lg);
41
+ }
42
+
43
+ p {
44
+ margin-bottom: var(--space-md);
45
+ }
46
+
47
+ a {
48
+ color: var(--color-primary);
49
+ text-decoration: none;
50
+ }
51
+
52
+ a:hover {
53
+ color: var(--color-primary-dark);
54
+ text-decoration: underline;
55
+ }
56
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * App Bundle
3
+ *
4
+ * Main entry point for index.html (web app).
5
+ * Single import for all app styles.
6
+ */
7
+
8
+ /* Layer order declaration - must be first */
9
+ @layer tokens, reset, base, components, utilities, pages;
10
+
11
+ /* Foundation */
12
+ @import "../tokens.css" layer(tokens);
13
+ @import "../reset.css" layer(reset);
14
+ @import "../base.css" layer(base);
15
+
16
+ /* Components */
17
+ @import "../components/layout.css" layer(components);
18
+ @import "../components/surfaces.css" layer(components);
19
+ @import "../components/typography.css" layer(components);
20
+ @import "../components/badges.css" layer(components);
21
+ @import "../components/buttons.css" layer(components);
22
+ @import "../components/forms.css" layer(components);
23
+ @import "../components/tables.css" layer(components);
24
+ @import "../components/progress.css" layer(components);
25
+ @import "../components/states.css" layer(components);
26
+ @import "../components/nav.css" layer(components);
27
+
28
+ /* Utilities */
29
+ @import "../components/utilities.css" layer(utilities);
30
+
31
+ /* Pages */
32
+ @import "../pages/landing.css" layer(pages);
33
+ @import "../pages/job-builder.css" layer(pages);
34
+ @import "../pages/agent-builder.css" layer(pages);
35
+ @import "../pages/lifecycle.css" layer(pages);
36
+ @import "../pages/detail.css" layer(pages);
37
+ @import "../pages/interview-builder.css" layer(pages);
38
+ @import "../pages/self-assessment.css" layer(pages);
39
+ @import "../pages/assessment-results.css" layer(pages);
40
+ @import "../pages/progress-builder.css" layer(pages);
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Handout Bundle
3
+ *
4
+ * Entry point for handout.html.
5
+ * Includes slide styles plus handout-specific overrides.
6
+ */
7
+
8
+ /* Layer order declaration - must be first */
9
+ @layer tokens, reset, base, components, utilities, pages, slides, handout, print;
10
+
11
+ /* Foundation */
12
+ @import "../tokens.css" layer(tokens);
13
+ @import "../reset.css" layer(reset);
14
+ @import "../base.css" layer(base);
15
+
16
+ /* Components */
17
+ @import "../components/layout.css" layer(components);
18
+ @import "../components/surfaces.css" layer(components);
19
+ @import "../components/typography.css" layer(components);
20
+ @import "../components/badges.css" layer(components);
21
+ @import "../components/buttons.css" layer(components);
22
+ @import "../components/forms.css" layer(components);
23
+ @import "../components/tables.css" layer(components);
24
+ @import "../components/progress.css" layer(components);
25
+ @import "../components/states.css" layer(components);
26
+
27
+ /* Utilities */
28
+ @import "../components/utilities.css" layer(utilities);
29
+
30
+ /* Pages (for shared formatters) */
31
+ @import "../pages/detail.css" layer(pages);
32
+
33
+ /* Slide views */
34
+ @import "../views/slide-animations.css" layer(slides);
35
+ @import "../views/slide-base.css" layer(slides);
36
+ @import "../views/slide-sections.css" layer(slides);
37
+ @import "../views/slide-tables.css" layer(slides);
38
+
39
+ /* Handout overrides */
40
+ @import "../views/handout.css" layer(handout);
41
+
42
+ /* Print */
43
+ @import "../views/print.css" layer(print);
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Slides Bundle
3
+ *
4
+ * Entry point for slides.html.
5
+ * Includes app styles plus slide-specific layers.
6
+ */
7
+
8
+ /* Layer order declaration - must be first */
9
+ @layer tokens, reset, base, components, utilities, pages, slides, print;
10
+
11
+ /* Foundation */
12
+ @import "../tokens.css" layer(tokens);
13
+ @import "../reset.css" layer(reset);
14
+ @import "../base.css" layer(base);
15
+
16
+ /* Components */
17
+ @import "../components/layout.css" layer(components);
18
+ @import "../components/surfaces.css" layer(components);
19
+ @import "../components/typography.css" layer(components);
20
+ @import "../components/badges.css" layer(components);
21
+ @import "../components/buttons.css" layer(components);
22
+ @import "../components/forms.css" layer(components);
23
+ @import "../components/tables.css" layer(components);
24
+ @import "../components/progress.css" layer(components);
25
+ @import "../components/states.css" layer(components);
26
+
27
+ /* Utilities */
28
+ @import "../components/utilities.css" layer(utilities);
29
+
30
+ /* Pages (for shared formatters) */
31
+ @import "../pages/detail.css" layer(pages);
32
+
33
+ /* Slide views */
34
+ @import "../views/slide-animations.css" layer(slides);
35
+ @import "../views/slide-base.css" layer(slides);
36
+ @import "../views/slide-sections.css" layer(slides);
37
+ @import "../views/slide-tables.css" layer(slides);
38
+
39
+ /* Print */
40
+ @import "../views/print.css" layer(print);