@forwardimpact/pathway 0.13.0 → 0.14.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/bin/fit-pathway.js +26 -7
- package/package.json +2 -2
- package/src/commands/build.js +98 -2
- package/src/commands/index.js +1 -0
- package/src/commands/job.js +1 -0
- package/src/commands/stage.js +8 -8
- package/src/commands/update.js +133 -0
- package/src/components/command-prompt.js +85 -0
- package/src/components/nav.js +2 -2
- package/src/components/top-bar.js +97 -0
- package/src/css/bundles/app.css +2 -0
- package/src/css/components/command-prompt.css +98 -0
- package/src/css/components/layout.css +0 -3
- package/src/css/components/nav.css +121 -81
- package/src/css/components/surfaces.css +1 -1
- package/src/css/components/top-bar.css +180 -0
- package/src/css/pages/agent-builder.css +0 -9
- package/src/css/pages/landing.css +4 -0
- package/src/css/pages/lifecycle.css +5 -2
- package/src/css/reset.css +1 -1
- package/src/css/tokens.css +4 -2
- package/src/css/views/slide-base.css +2 -1
- package/src/formatters/agent/dom.js +0 -26
- package/src/formatters/agent/profile.js +13 -7
- package/src/formatters/agent/skill.js +4 -4
- package/src/formatters/stage/dom.js +13 -10
- package/src/formatters/stage/microdata.js +14 -8
- package/src/formatters/stage/shared.js +4 -4
- package/src/index.html +69 -44
- package/src/lib/cli-command.js +145 -0
- package/src/lib/state.js +2 -0
- package/src/main.js +47 -26
- package/src/pages/agent-builder.js +0 -28
- package/src/pages/behaviour.js +3 -1
- package/src/pages/discipline.js +3 -1
- package/src/pages/driver.js +6 -1
- package/src/pages/grade.js +6 -1
- package/src/pages/job.js +1 -0
- package/src/pages/landing.js +7 -0
- package/src/pages/skill.js +9 -2
- package/src/pages/track.js +6 -1
- package/src/slides/job.js +1 -0
- package/templates/agent.template.md +17 -10
- package/templates/install.template.sh +33 -0
- package/templates/skill.template.md +15 -7
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
* @typedef {Object}
|
|
26
|
+
* @typedef {Object} ConfirmChecklistEntry
|
|
27
27
|
* @property {{id: string, name: string}} skill - Skill info
|
|
28
28
|
* @property {{id: string, name: string, emojiIcon: string}} capability - Capability info
|
|
29
29
|
* @property {string[]} items - Checklist items
|
|
@@ -41,12 +41,15 @@ import {
|
|
|
41
41
|
* @param {Object} params.bodyData - Structured body data
|
|
42
42
|
* @param {string} params.bodyData.title - Agent title
|
|
43
43
|
* @param {string} params.bodyData.stageDescription - Stage description text
|
|
44
|
+
* @param {string} params.bodyData.stageId - Stage identifier (e.g. "plan", "code", "onboard")
|
|
45
|
+
* @param {string} params.bodyData.stageName - Human-readable stage name (e.g. "Plan", "Code", "Onboard")
|
|
46
|
+
* @param {boolean} params.bodyData.isOnboard - Whether this is the onboard stage
|
|
44
47
|
* @param {string} params.bodyData.identity - Core identity text
|
|
45
48
|
* @param {string} [params.bodyData.priority] - Priority/philosophy statement
|
|
46
49
|
* @param {Array<{name: string, dirname: string, useWhen: string}>} params.bodyData.skillIndex - Skill index entries
|
|
47
50
|
* @param {string} params.bodyData.roleContext - Role context text
|
|
48
51
|
* @param {WorkingStyleEntry[]} params.bodyData.workingStyles - Working style entries
|
|
49
|
-
* @param {
|
|
52
|
+
* @param {ConfirmChecklistEntry[]} [params.bodyData.confirmChecklist] - Confirm Checklist entries
|
|
50
53
|
* @param {string[]} params.bodyData.constraints - List of constraints
|
|
51
54
|
* @param {Array<{id: string, name: string, description: string}>} [params.bodyData.agentIndex] - List of all available agents
|
|
52
55
|
* @param {boolean} [params.bodyData.hasAgentIndex] - Whether agent index is available
|
|
@@ -77,8 +80,8 @@ function prepareAgentProfileData({ frontmatter, bodyData }) {
|
|
|
77
80
|
content: "required",
|
|
78
81
|
});
|
|
79
82
|
|
|
80
|
-
// Process
|
|
81
|
-
const
|
|
83
|
+
// Process confirmChecklist: trim items in each entry
|
|
84
|
+
const confirmChecklist = (bodyData.confirmChecklist || []).map((entry) => ({
|
|
82
85
|
skill: entry.skill,
|
|
83
86
|
capability: entry.capability,
|
|
84
87
|
items: (entry.items || []).map((item) => trimRequired(item)),
|
|
@@ -94,6 +97,9 @@ function prepareAgentProfileData({ frontmatter, bodyData }) {
|
|
|
94
97
|
// Body data - trim all string fields
|
|
95
98
|
title: bodyData.title,
|
|
96
99
|
stageDescription: trimValue(bodyData.stageDescription),
|
|
100
|
+
stageId: bodyData.stageId,
|
|
101
|
+
stageName: bodyData.stageName,
|
|
102
|
+
isOnboard: bodyData.isOnboard,
|
|
97
103
|
identity: trimValue(bodyData.identity),
|
|
98
104
|
priority: trimValue(bodyData.priority),
|
|
99
105
|
skillIndex,
|
|
@@ -101,8 +107,8 @@ function prepareAgentProfileData({ frontmatter, bodyData }) {
|
|
|
101
107
|
roleContext: trimValue(bodyData.roleContext),
|
|
102
108
|
workingStyles,
|
|
103
109
|
hasWorkingStyles: workingStyles.length > 0,
|
|
104
|
-
|
|
105
|
-
|
|
110
|
+
confirmChecklist,
|
|
111
|
+
hasConfirmChecklist: confirmChecklist.length > 0,
|
|
106
112
|
constraints,
|
|
107
113
|
hasConstraints: constraints.length > 0,
|
|
108
114
|
agentIndex,
|
|
@@ -127,7 +133,7 @@ function prepareAgentProfileData({ frontmatter, bodyData }) {
|
|
|
127
133
|
* @param {Array<{name: string, dirname: string, useWhen: string}>} profile.bodyData.skillIndex - Skill index entries
|
|
128
134
|
* @param {string} profile.bodyData.roleContext - Role context text
|
|
129
135
|
* @param {WorkingStyleEntry[]} profile.bodyData.workingStyles - Working style entries
|
|
130
|
-
* @param {
|
|
136
|
+
* @param {ConfirmChecklistEntry[]} [profile.bodyData.confirmChecklist] - Confirm Checklist entries (optional)
|
|
131
137
|
* @param {string[]} profile.bodyData.constraints - List of constraints
|
|
132
138
|
* @param {string} template - Mustache template string
|
|
133
139
|
* @returns {string} Complete .agent.md file content
|
|
@@ -22,7 +22,7 @@ import { flattenToLine } from "../template-preprocess.js";
|
|
|
22
22
|
* @param {string} params.frontmatter.description - Skill description (required)
|
|
23
23
|
* @param {string} [params.frontmatter.useWhen] - When to use this skill
|
|
24
24
|
* @param {string} params.title - Human-readable skill title for heading
|
|
25
|
-
* @param {Array} params.stages - Array of stage objects with stageName, focus,
|
|
25
|
+
* @param {Array} params.stages - Array of stage objects with stageName, focus, readChecklist, confirmChecklist
|
|
26
26
|
* @param {string} params.reference - Reference content (markdown)
|
|
27
27
|
* @param {Array} [params.toolReferences] - Array of tool reference objects
|
|
28
28
|
* @returns {Object} Data object ready for Mustache template
|
|
@@ -37,8 +37,8 @@ function prepareAgentSkillData({
|
|
|
37
37
|
// Process stages - trim focus and array values
|
|
38
38
|
const processedStages = trimFields(stages, {
|
|
39
39
|
focus: "required",
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
readChecklist: "array",
|
|
41
|
+
confirmChecklist: "array",
|
|
42
42
|
});
|
|
43
43
|
|
|
44
44
|
// Flatten multi-line strings to single line for front matter compatibility
|
|
@@ -77,7 +77,7 @@ function prepareAgentSkillData({
|
|
|
77
77
|
* @param {string} skill.frontmatter.name - Skill name (required)
|
|
78
78
|
* @param {string} skill.frontmatter.description - Skill description (required)
|
|
79
79
|
* @param {string} skill.title - Human-readable skill title for heading
|
|
80
|
-
* @param {Array} skill.stages - Array of stage objects with stageName, focus,
|
|
80
|
+
* @param {Array} skill.stages - Array of stage objects with stageName, focus, readChecklist, confirmChecklist
|
|
81
81
|
* @param {string} skill.reference - Reference content (markdown)
|
|
82
82
|
* @param {Array} [skill.toolReferences] - Array of tool reference objects
|
|
83
83
|
* @param {string} template - Mustache template string
|
|
@@ -34,33 +34,36 @@ export function stageToDOM(stage, { stages = [], showBackLink = true } = {}) {
|
|
|
34
34
|
p({ className: "page-description" }, view.description),
|
|
35
35
|
),
|
|
36
36
|
|
|
37
|
-
//
|
|
38
|
-
view.
|
|
37
|
+
// Read/Confirm Checklists
|
|
38
|
+
view.readChecklist.length > 0 || view.confirmChecklist.length > 0
|
|
39
39
|
? div(
|
|
40
40
|
{ className: "section section-detail" },
|
|
41
41
|
div(
|
|
42
42
|
{ className: "content-columns" },
|
|
43
|
-
//
|
|
44
|
-
view.
|
|
43
|
+
// Read checklist column
|
|
44
|
+
view.readChecklist.length > 0
|
|
45
45
|
? div(
|
|
46
46
|
{ className: "column" },
|
|
47
|
-
h2({ className: "section-title" }, "
|
|
47
|
+
h2({ className: "section-title" }, "Read-Then-Do Checklist"),
|
|
48
48
|
ul(
|
|
49
49
|
{ className: "criteria-list" },
|
|
50
|
-
...view.
|
|
50
|
+
...view.readChecklist.map((item) =>
|
|
51
51
|
li({ className: "criteria-item" }, item),
|
|
52
52
|
),
|
|
53
53
|
),
|
|
54
54
|
)
|
|
55
55
|
: null,
|
|
56
|
-
//
|
|
57
|
-
view.
|
|
56
|
+
// Confirm checklist column
|
|
57
|
+
view.confirmChecklist.length > 0
|
|
58
58
|
? div(
|
|
59
59
|
{ className: "column" },
|
|
60
|
-
h2(
|
|
60
|
+
h2(
|
|
61
|
+
{ className: "section-title" },
|
|
62
|
+
"Do-Then-Confirm Checklist",
|
|
63
|
+
),
|
|
61
64
|
ul(
|
|
62
65
|
{ className: "criteria-list" },
|
|
63
|
-
...view.
|
|
66
|
+
...view.confirmChecklist.map((item) =>
|
|
64
67
|
li({ className: "criteria-item" }, item),
|
|
65
68
|
),
|
|
66
69
|
),
|
|
@@ -60,11 +60,11 @@ export function stageToMicrodata(stage) {
|
|
|
60
60
|
|
|
61
61
|
const sections = [];
|
|
62
62
|
|
|
63
|
-
//
|
|
64
|
-
if (view.
|
|
65
|
-
const
|
|
63
|
+
// Read checklist
|
|
64
|
+
if (view.readChecklist.length > 0) {
|
|
65
|
+
const readItems = view.readChecklist.map((c) => escapeHtml(c));
|
|
66
66
|
sections.push(
|
|
67
|
-
section("
|
|
67
|
+
section("Read-Then-Do Checklist", ul(readItems, "readChecklist"), 2),
|
|
68
68
|
);
|
|
69
69
|
}
|
|
70
70
|
|
|
@@ -76,10 +76,16 @@ export function stageToMicrodata(stage) {
|
|
|
76
76
|
);
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
//
|
|
80
|
-
if (view.
|
|
81
|
-
const
|
|
82
|
-
sections.push(
|
|
79
|
+
// Confirm checklist
|
|
80
|
+
if (view.confirmChecklist.length > 0) {
|
|
81
|
+
const confirmItems = view.confirmChecklist.map((c) => escapeHtml(c));
|
|
82
|
+
sections.push(
|
|
83
|
+
section(
|
|
84
|
+
"Do-Then-Confirm Checklist",
|
|
85
|
+
ul(confirmItems, "confirmChecklist"),
|
|
86
|
+
2,
|
|
87
|
+
),
|
|
88
|
+
);
|
|
83
89
|
}
|
|
84
90
|
|
|
85
91
|
// Handoffs - using Handoff itemtype
|
|
@@ -46,8 +46,8 @@ export function prepareStagesList(stages, descriptionLimit = 150) {
|
|
|
46
46
|
* @property {string} name
|
|
47
47
|
* @property {string} description
|
|
48
48
|
* @property {string[]} constraints
|
|
49
|
-
* @property {string[]}
|
|
50
|
-
* @property {string[]}
|
|
49
|
+
* @property {string[]} readChecklist
|
|
50
|
+
* @property {string[]} confirmChecklist
|
|
51
51
|
* @property {Array<{target: string, label: string, prompt: string}>} handoffs
|
|
52
52
|
*/
|
|
53
53
|
|
|
@@ -62,8 +62,8 @@ export function prepareStageDetail(stage) {
|
|
|
62
62
|
name: stage.name,
|
|
63
63
|
description: stage.description,
|
|
64
64
|
constraints: stage.constraints || [],
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
readChecklist: stage.readChecklist || [],
|
|
66
|
+
confirmChecklist: stage.confirmChecklist || [],
|
|
67
67
|
handoffs: (stage.handoffs || []).map((h) => ({
|
|
68
68
|
target: h.targetStage,
|
|
69
69
|
label: h.label,
|
package/src/index.html
CHANGED
|
@@ -43,56 +43,81 @@
|
|
|
43
43
|
</script>
|
|
44
44
|
</head>
|
|
45
45
|
<body>
|
|
46
|
-
<div id="app">
|
|
47
|
-
<
|
|
48
|
-
<div class="nav-brand">
|
|
49
|
-
<a href="#/">Engineering Pathway</a>
|
|
50
|
-
<span class="brand-tag">#BenchTools</span>
|
|
51
|
-
</div>
|
|
46
|
+
<div id="app" class="drawer-open">
|
|
47
|
+
<header class="top-bar" id="top-bar">
|
|
52
48
|
<button
|
|
53
|
-
class="
|
|
54
|
-
id="
|
|
55
|
-
aria-label="Toggle
|
|
49
|
+
class="top-bar__toggle"
|
|
50
|
+
id="sidebar-toggle"
|
|
51
|
+
aria-label="Toggle sidebar"
|
|
56
52
|
>
|
|
57
|
-
<
|
|
58
|
-
|
|
59
|
-
|
|
53
|
+
<svg viewBox="0 0 24 24">
|
|
54
|
+
<rect x="3" y="3" width="18" height="18" rx="3" />
|
|
55
|
+
<line x1="9" y1="3" x2="9" y2="21" />
|
|
56
|
+
</svg>
|
|
60
57
|
</button>
|
|
61
|
-
<
|
|
62
|
-
<
|
|
63
|
-
<
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
<
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
<a href="#/career-progress" class="nav-cta">Career Progress</a>
|
|
75
|
-
</li>
|
|
76
|
-
<li>
|
|
77
|
-
<a href="#/self-assessment" class="nav-cta">Self-Assessment</a>
|
|
78
|
-
</li>
|
|
79
|
-
</ul>
|
|
80
|
-
</nav>
|
|
81
|
-
|
|
82
|
-
<main id="app-content">
|
|
83
|
-
<div class="loading">
|
|
84
|
-
<div class="loading-spinner"></div>
|
|
85
|
-
<p>Loading...</p>
|
|
58
|
+
<div class="top-bar__command">
|
|
59
|
+
<span class="top-bar__prompt">$</span>
|
|
60
|
+
<span class="top-bar__command-text" id="cli-command"
|
|
61
|
+
>npx fit-pathway</span
|
|
62
|
+
>
|
|
63
|
+
<button class="top-bar__copy" id="cli-copy" aria-label="Copy command">
|
|
64
|
+
<svg viewBox="0 0 24 24">
|
|
65
|
+
<rect x="9" y="9" width="13" height="13" rx="2" />
|
|
66
|
+
<path
|
|
67
|
+
d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"
|
|
68
|
+
/>
|
|
69
|
+
</svg>
|
|
70
|
+
</button>
|
|
86
71
|
</div>
|
|
87
|
-
</
|
|
72
|
+
</header>
|
|
88
73
|
|
|
89
|
-
<
|
|
90
|
-
<
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
74
|
+
<aside id="app-drawer">
|
|
75
|
+
<div class="drawer-header">
|
|
76
|
+
<a href="#/" class="nav-brand">
|
|
77
|
+
<span class="brand-emoji">🧭</span>
|
|
78
|
+
<span class="brand-title">Engineering Pathway</span>
|
|
79
|
+
<span class="brand-tag">#BenchTools</span>
|
|
80
|
+
</a>
|
|
81
|
+
</div>
|
|
82
|
+
<nav class="drawer-nav" id="drawer-nav">
|
|
83
|
+
<div class="drawer-section">
|
|
84
|
+
<span class="drawer-section-label">Browse</span>
|
|
85
|
+
<a href="#/discipline">Disciplines</a>
|
|
86
|
+
<a href="#/grade">Grades</a>
|
|
87
|
+
<a href="#/track">Tracks</a>
|
|
88
|
+
<a href="#/behaviour">Behaviours</a>
|
|
89
|
+
<a href="#/skill">Skills</a>
|
|
90
|
+
<a href="#/driver">Drivers</a>
|
|
91
|
+
<a href="#/stage">Stages</a>
|
|
92
|
+
<a href="#/tool">Tools</a>
|
|
93
|
+
</div>
|
|
94
|
+
<div class="drawer-section">
|
|
95
|
+
<span class="drawer-section-label">Work</span>
|
|
96
|
+
<a href="#/job-builder" class="drawer-cta">Build a Job</a>
|
|
97
|
+
<a href="#/agent-builder" class="drawer-cta">Build an Agent</a>
|
|
98
|
+
<a href="#/interview-prep" class="drawer-cta">Interview Prep</a>
|
|
99
|
+
<a href="#/career-progress" class="drawer-cta">Career Progress</a>
|
|
100
|
+
<a href="#/self-assessment" class="drawer-cta">Self-Assessment</a>
|
|
101
|
+
</div>
|
|
94
102
|
</nav>
|
|
95
|
-
</
|
|
103
|
+
</aside>
|
|
104
|
+
|
|
105
|
+
<div class="app-body">
|
|
106
|
+
<main id="app-content">
|
|
107
|
+
<div class="loading">
|
|
108
|
+
<div class="loading-spinner"></div>
|
|
109
|
+
<p>Loading...</p>
|
|
110
|
+
</div>
|
|
111
|
+
</main>
|
|
112
|
+
|
|
113
|
+
<footer id="app-footer">
|
|
114
|
+
<p>Engineering Pathway</p>
|
|
115
|
+
<nav class="footer-links">
|
|
116
|
+
<a href="slides.html">Slides</a>
|
|
117
|
+
<a href="handout.html">Handouts</a>
|
|
118
|
+
</nav>
|
|
119
|
+
</footer>
|
|
120
|
+
</div>
|
|
96
121
|
</div>
|
|
97
122
|
|
|
98
123
|
<script type="module" src="main.js"></script>
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Command Mapping
|
|
3
|
+
*
|
|
4
|
+
* Maps hash routes to their equivalent `npx fit-pathway` CLI commands.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Route-to-CLI command rules, checked in order.
|
|
9
|
+
* Each rule has a pattern (regex) and a function that produces the CLI string.
|
|
10
|
+
* @type {Array<{ pattern: RegExp, toCommand: (match: RegExpMatchArray) => string }>}
|
|
11
|
+
*/
|
|
12
|
+
const ROUTE_COMMANDS = [
|
|
13
|
+
// Landing
|
|
14
|
+
{ pattern: /^\/$/, toCommand: () => "npx fit-pathway" },
|
|
15
|
+
|
|
16
|
+
// Entity lists
|
|
17
|
+
{ pattern: /^\/skill$/, toCommand: () => "npx fit-pathway skill" },
|
|
18
|
+
{ pattern: /^\/behaviour$/, toCommand: () => "npx fit-pathway behaviour" },
|
|
19
|
+
{
|
|
20
|
+
pattern: /^\/discipline$/,
|
|
21
|
+
toCommand: () => "npx fit-pathway discipline",
|
|
22
|
+
},
|
|
23
|
+
{ pattern: /^\/track$/, toCommand: () => "npx fit-pathway track" },
|
|
24
|
+
{ pattern: /^\/grade$/, toCommand: () => "npx fit-pathway grade" },
|
|
25
|
+
{ pattern: /^\/driver$/, toCommand: () => "npx fit-pathway driver" },
|
|
26
|
+
{ pattern: /^\/stage$/, toCommand: () => "npx fit-pathway stage" },
|
|
27
|
+
{ pattern: /^\/tool$/, toCommand: () => "npx fit-pathway tool" },
|
|
28
|
+
|
|
29
|
+
// Entity details
|
|
30
|
+
{
|
|
31
|
+
pattern: /^\/skill\/(.+)$/,
|
|
32
|
+
toCommand: (m) => `npx fit-pathway skill ${m[1]}`,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
pattern: /^\/behaviour\/(.+)$/,
|
|
36
|
+
toCommand: (m) => `npx fit-pathway behaviour ${m[1]}`,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
pattern: /^\/discipline\/(.+)$/,
|
|
40
|
+
toCommand: (m) => `npx fit-pathway discipline ${m[1]}`,
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
pattern: /^\/track\/(.+)$/,
|
|
44
|
+
toCommand: (m) => `npx fit-pathway track ${m[1]}`,
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
pattern: /^\/grade\/(.+)$/,
|
|
48
|
+
toCommand: (m) => `npx fit-pathway grade ${m[1]}`,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
pattern: /^\/driver\/(.+)$/,
|
|
52
|
+
toCommand: (m) => `npx fit-pathway driver ${m[1]}`,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
pattern: /^\/stage\/(.+)$/,
|
|
56
|
+
toCommand: (m) => `npx fit-pathway stage ${m[1]}`,
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
// Job builder + detail
|
|
60
|
+
{
|
|
61
|
+
pattern: /^\/job-builder$/,
|
|
62
|
+
toCommand: () => "npx fit-pathway job --list",
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
pattern: /^\/job\/([^/]+)\/([^/]+)\/([^/]+)$/,
|
|
66
|
+
toCommand: (m) => `npx fit-pathway job ${m[1]} ${m[2]} --track=${m[3]}`,
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
pattern: /^\/job\/([^/]+)\/([^/]+)$/,
|
|
70
|
+
toCommand: (m) => `npx fit-pathway job ${m[1]} ${m[2]}`,
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
// Interview builder + detail
|
|
74
|
+
{
|
|
75
|
+
pattern: /^\/interview-prep$/,
|
|
76
|
+
toCommand: () => "npx fit-pathway interview --list",
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
pattern: /^\/interview\/([^/]+)\/([^/]+)\/([^/]+)$/,
|
|
80
|
+
toCommand: (m) =>
|
|
81
|
+
`npx fit-pathway interview ${m[1]} ${m[2]} --track=${m[3]}`,
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
pattern: /^\/interview\/([^/]+)\/([^/]+)$/,
|
|
85
|
+
toCommand: (m) => `npx fit-pathway interview ${m[1]} ${m[2]}`,
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
// Career progress builder + detail
|
|
89
|
+
{
|
|
90
|
+
pattern: /^\/career-progress$/,
|
|
91
|
+
toCommand: () => "npx fit-pathway progress --list",
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
pattern: /^\/progress\/([^/]+)\/([^/]+)\/([^/]+)$/,
|
|
95
|
+
toCommand: (m) =>
|
|
96
|
+
`npx fit-pathway progress ${m[1]} ${m[2]} --track=${m[3]}`,
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
pattern: /^\/progress\/([^/]+)\/([^/]+)$/,
|
|
100
|
+
toCommand: (m) => `npx fit-pathway progress ${m[1]} ${m[2]}`,
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
// Self-assessment
|
|
104
|
+
{
|
|
105
|
+
pattern: /^\/self-assessment$/,
|
|
106
|
+
toCommand: () => "npx fit-pathway self-assessment",
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
pattern: /^\/self-assessment\/results$/,
|
|
110
|
+
toCommand: () => "npx fit-pathway self-assessment",
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
// Agent builder + detail
|
|
114
|
+
{
|
|
115
|
+
pattern: /^\/agent-builder$/,
|
|
116
|
+
toCommand: () => "npx fit-pathway agent --list",
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
pattern: /^\/agent\/([^/]+)\/([^/]+)\/([^/]+)$/,
|
|
120
|
+
toCommand: (m) =>
|
|
121
|
+
`npx fit-pathway agent ${m[1]} --track=${m[2]} --stage=${m[3]}`,
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
pattern: /^\/agent\/([^/]+)\/([^/]+)$/,
|
|
125
|
+
toCommand: (m) => `npx fit-pathway agent ${m[1]} --track=${m[2]}`,
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
pattern: /^\/agent\/([^/]+)$/,
|
|
129
|
+
toCommand: (m) => `npx fit-pathway agent ${m[1]}`,
|
|
130
|
+
},
|
|
131
|
+
];
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Get the CLI command equivalent for a given hash path
|
|
135
|
+
* @param {string} hashPath - The hash path (without #), e.g. "/skill/testing"
|
|
136
|
+
* @returns {string} The CLI command string
|
|
137
|
+
*/
|
|
138
|
+
export function getCliCommand(hashPath) {
|
|
139
|
+
const path = hashPath.split("?")[0] || "/";
|
|
140
|
+
for (const { pattern, toCommand } of ROUTE_COMMANDS) {
|
|
141
|
+
const match = path.match(pattern);
|
|
142
|
+
if (match) return toCommand(match);
|
|
143
|
+
}
|
|
144
|
+
return "npx fit-pathway";
|
|
145
|
+
}
|
package/src/lib/state.js
CHANGED
|
@@ -132,6 +132,7 @@ export function getFilters(entity) {
|
|
|
132
132
|
* @property {string} title - Application title
|
|
133
133
|
* @property {string} tag - Brand hashtag/tag
|
|
134
134
|
* @property {string} description - Application description
|
|
135
|
+
* @property {string} emojiIcon - Emoji icon for the framework
|
|
135
136
|
*/
|
|
136
137
|
|
|
137
138
|
/**
|
|
@@ -144,5 +145,6 @@ export function getBranding() {
|
|
|
144
145
|
title: framework.title || "Engineering Pathway",
|
|
145
146
|
tag: framework.tag || "#BenchTools",
|
|
146
147
|
description: framework.description || "",
|
|
148
|
+
emojiIcon: framework.emojiIcon || "🧭",
|
|
147
149
|
};
|
|
148
150
|
}
|
package/src/main.js
CHANGED
|
@@ -6,6 +6,8 @@ import { createPagesRouter } from "./lib/router-pages.js";
|
|
|
6
6
|
import { setData, setError, getBranding } from "./lib/state.js";
|
|
7
7
|
import { loadAllData } from "./lib/yaml-loader.js";
|
|
8
8
|
import { render, div, h1, p, showError } from "./lib/render.js";
|
|
9
|
+
import { updateActiveNav } from "./components/nav.js";
|
|
10
|
+
import { setupTopBar, updateCommand } from "./components/top-bar.js";
|
|
9
11
|
|
|
10
12
|
const router = createPagesRouter({
|
|
11
13
|
onNotFound: renderNotFound,
|
|
@@ -41,8 +43,9 @@ import { renderAgentBuilder } from "./pages/agent-builder.js";
|
|
|
41
43
|
* Initialize the application
|
|
42
44
|
*/
|
|
43
45
|
async function init() {
|
|
44
|
-
// Set up
|
|
45
|
-
|
|
46
|
+
// Set up top bar (sidebar toggle + CLI command display)
|
|
47
|
+
setupTopBar();
|
|
48
|
+
setupDrawerOverlay();
|
|
46
49
|
|
|
47
50
|
// Load data
|
|
48
51
|
try {
|
|
@@ -63,6 +66,14 @@ async function init() {
|
|
|
63
66
|
|
|
64
67
|
// Start router
|
|
65
68
|
router.start();
|
|
69
|
+
|
|
70
|
+
// Update active nav link and CLI command on route changes
|
|
71
|
+
const updateNav = () => {
|
|
72
|
+
updateActiveNav(window.location.hash.slice(1) || "/");
|
|
73
|
+
updateCommand();
|
|
74
|
+
};
|
|
75
|
+
window.addEventListener("hashchange", updateNav);
|
|
76
|
+
updateNav();
|
|
66
77
|
}
|
|
67
78
|
|
|
68
79
|
/**
|
|
@@ -175,13 +186,19 @@ function populateBranding() {
|
|
|
175
186
|
// Update document title
|
|
176
187
|
document.title = branding.title;
|
|
177
188
|
|
|
178
|
-
// Update
|
|
179
|
-
const
|
|
180
|
-
if (
|
|
181
|
-
|
|
189
|
+
// Update drawer brand
|
|
190
|
+
const brandTitle = document.querySelector(".nav-brand .brand-title");
|
|
191
|
+
if (brandTitle) {
|
|
192
|
+
brandTitle.textContent = branding.title;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Update drawer brand emoji
|
|
196
|
+
const brandEmoji = document.querySelector(".nav-brand .brand-emoji");
|
|
197
|
+
if (brandEmoji) {
|
|
198
|
+
brandEmoji.textContent = branding.emojiIcon;
|
|
182
199
|
}
|
|
183
200
|
|
|
184
|
-
// Update
|
|
201
|
+
// Update drawer brand tag
|
|
185
202
|
const brandTag = document.querySelector(".nav-brand .brand-tag");
|
|
186
203
|
if (brandTag) {
|
|
187
204
|
brandTag.textContent = branding.tag;
|
|
@@ -195,26 +212,30 @@ function populateBranding() {
|
|
|
195
212
|
}
|
|
196
213
|
|
|
197
214
|
/**
|
|
198
|
-
* Set up mobile
|
|
215
|
+
* Set up drawer overlay for mobile backdrop and auto-close behavior
|
|
199
216
|
*/
|
|
200
|
-
function
|
|
201
|
-
const
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
if (
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
217
|
+
function setupDrawerOverlay() {
|
|
218
|
+
const app = document.getElementById("app");
|
|
219
|
+
const drawer = document.getElementById("app-drawer");
|
|
220
|
+
|
|
221
|
+
if (!drawer) return;
|
|
222
|
+
|
|
223
|
+
// Create overlay element for mobile backdrop
|
|
224
|
+
const overlay = document.createElement("div");
|
|
225
|
+
overlay.className = "drawer-overlay";
|
|
226
|
+
app.insertBefore(overlay, drawer);
|
|
227
|
+
|
|
228
|
+
// Close drawer when overlay is clicked (mobile)
|
|
229
|
+
overlay.addEventListener("click", () => {
|
|
230
|
+
app.classList.remove("drawer-open");
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
// Close drawer on mobile when a link is clicked
|
|
234
|
+
drawer.addEventListener("click", (e) => {
|
|
235
|
+
if (e.target.tagName === "A" && window.innerWidth <= 768) {
|
|
236
|
+
app.classList.remove("drawer-open");
|
|
237
|
+
}
|
|
238
|
+
});
|
|
218
239
|
}
|
|
219
240
|
|
|
220
241
|
// Start the app
|
|
@@ -562,9 +562,6 @@ function createAllStagesPreview(context) {
|
|
|
562
562
|
content: createToolkitTable(toolkit),
|
|
563
563
|
})
|
|
564
564
|
: null,
|
|
565
|
-
|
|
566
|
-
// CLI hint
|
|
567
|
-
createCliHint(humanDiscipline.id, humanTrack.id),
|
|
568
565
|
);
|
|
569
566
|
}
|
|
570
567
|
|
|
@@ -687,9 +684,6 @@ function createSingleStagePreview(context, stage) {
|
|
|
687
684
|
content: createToolkitTable(toolkit),
|
|
688
685
|
})
|
|
689
686
|
: null,
|
|
690
|
-
|
|
691
|
-
// CLI hint
|
|
692
|
-
createCliHint(humanDiscipline.id, humanTrack.id, stage.id),
|
|
693
687
|
);
|
|
694
688
|
}
|
|
695
689
|
|
|
@@ -936,29 +930,7 @@ async function importJSZip() {
|
|
|
936
930
|
return module.default;
|
|
937
931
|
}
|
|
938
932
|
|
|
939
|
-
/**
|
|
940
|
-
* Create CLI hint section
|
|
941
|
-
* @param {string} disciplineId - Discipline ID
|
|
942
|
-
* @param {string} trackId - Track ID
|
|
943
|
-
* @param {string} [stageId] - Optional stage ID
|
|
944
|
-
* @returns {HTMLElement}
|
|
945
|
-
*/
|
|
946
|
-
function createCliHint(disciplineId, trackId, stageId) {
|
|
947
|
-
const stageArg = stageId ? ` --stage=${stageId}` : "";
|
|
948
|
-
const command = `npx pathway agent ${disciplineId} ${trackId}${stageArg} --output=.github`;
|
|
949
|
-
|
|
950
|
-
const container = section(
|
|
951
|
-
{ className: "agent-section cli-hint" },
|
|
952
|
-
h2({}, "CLI Alternative"),
|
|
953
|
-
p({}, "Generate this agent from the command line:"),
|
|
954
|
-
createCodeDisplay({
|
|
955
|
-
content: command,
|
|
956
|
-
language: "bash",
|
|
957
|
-
}),
|
|
958
|
-
);
|
|
959
933
|
|
|
960
|
-
return container;
|
|
961
|
-
}
|
|
962
934
|
|
|
963
935
|
/**
|
|
964
936
|
* Create help section explaining how agent builder works
|