@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.
Files changed (45) hide show
  1. package/bin/fit-pathway.js +26 -7
  2. package/package.json +2 -2
  3. package/src/commands/build.js +98 -2
  4. package/src/commands/index.js +1 -0
  5. package/src/commands/job.js +1 -0
  6. package/src/commands/stage.js +8 -8
  7. package/src/commands/update.js +133 -0
  8. package/src/components/command-prompt.js +85 -0
  9. package/src/components/nav.js +2 -2
  10. package/src/components/top-bar.js +97 -0
  11. package/src/css/bundles/app.css +2 -0
  12. package/src/css/components/command-prompt.css +98 -0
  13. package/src/css/components/layout.css +0 -3
  14. package/src/css/components/nav.css +121 -81
  15. package/src/css/components/surfaces.css +1 -1
  16. package/src/css/components/top-bar.css +180 -0
  17. package/src/css/pages/agent-builder.css +0 -9
  18. package/src/css/pages/landing.css +4 -0
  19. package/src/css/pages/lifecycle.css +5 -2
  20. package/src/css/reset.css +1 -1
  21. package/src/css/tokens.css +4 -2
  22. package/src/css/views/slide-base.css +2 -1
  23. package/src/formatters/agent/dom.js +0 -26
  24. package/src/formatters/agent/profile.js +13 -7
  25. package/src/formatters/agent/skill.js +4 -4
  26. package/src/formatters/stage/dom.js +13 -10
  27. package/src/formatters/stage/microdata.js +14 -8
  28. package/src/formatters/stage/shared.js +4 -4
  29. package/src/index.html +69 -44
  30. package/src/lib/cli-command.js +145 -0
  31. package/src/lib/state.js +2 -0
  32. package/src/main.js +47 -26
  33. package/src/pages/agent-builder.js +0 -28
  34. package/src/pages/behaviour.js +3 -1
  35. package/src/pages/discipline.js +3 -1
  36. package/src/pages/driver.js +6 -1
  37. package/src/pages/grade.js +6 -1
  38. package/src/pages/job.js +1 -0
  39. package/src/pages/landing.js +7 -0
  40. package/src/pages/skill.js +9 -2
  41. package/src/pages/track.js +6 -1
  42. package/src/slides/job.js +1 -0
  43. package/templates/agent.template.md +17 -10
  44. package/templates/install.template.sh +33 -0
  45. package/templates/skill.template.md +15 -7
@@ -23,7 +23,7 @@ import {
23
23
  */
24
24
 
25
25
  /**
26
- * @typedef {Object} BeforeHandoffEntry
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 {BeforeHandoffEntry[]} [params.bodyData.beforeHandoff] - Before handoff checklist entries
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 beforeHandoff: trim items in each entry
81
- const beforeHandoff = (bodyData.beforeHandoff || []).map((entry) => ({
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
- beforeHandoff,
105
- hasBeforeHandoff: beforeHandoff.length > 0,
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 {BeforeHandoffEntry[]} [profile.bodyData.beforeHandoff] - Before handoff checklist entries (optional)
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, activities, ready
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
- activities: "array",
41
- ready: "array",
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, activities, ready
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
- // Entry/Exit Criteria
38
- view.entryCriteria.length > 0 || view.exitCriteria.length > 0
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
- // Entry criteria column
44
- view.entryCriteria.length > 0
43
+ // Read checklist column
44
+ view.readChecklist.length > 0
45
45
  ? div(
46
46
  { className: "column" },
47
- h2({ className: "section-title" }, "Entry Criteria"),
47
+ h2({ className: "section-title" }, "Read-Then-Do Checklist"),
48
48
  ul(
49
49
  { className: "criteria-list" },
50
- ...view.entryCriteria.map((item) =>
50
+ ...view.readChecklist.map((item) =>
51
51
  li({ className: "criteria-item" }, item),
52
52
  ),
53
53
  ),
54
54
  )
55
55
  : null,
56
- // Exit criteria column
57
- view.exitCriteria.length > 0
56
+ // Confirm checklist column
57
+ view.confirmChecklist.length > 0
58
58
  ? div(
59
59
  { className: "column" },
60
- h2({ className: "section-title" }, "Exit Criteria"),
60
+ h2(
61
+ { className: "section-title" },
62
+ "Do-Then-Confirm Checklist",
63
+ ),
61
64
  ul(
62
65
  { className: "criteria-list" },
63
- ...view.exitCriteria.map((item) =>
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
- // Entry criteria
64
- if (view.entryCriteria.length > 0) {
65
- const criteriaItems = view.entryCriteria.map((c) => escapeHtml(c));
63
+ // Read checklist
64
+ if (view.readChecklist.length > 0) {
65
+ const readItems = view.readChecklist.map((c) => escapeHtml(c));
66
66
  sections.push(
67
- section("Entry Criteria", ul(criteriaItems, "entryCriteria"), 2),
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
- // Exit criteria
80
- if (view.exitCriteria.length > 0) {
81
- const exitItems = view.exitCriteria.map((c) => escapeHtml(c));
82
- sections.push(section("Exit Criteria", ul(exitItems, "exitCriteria"), 2));
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[]} entryCriteria
50
- * @property {string[]} exitCriteria
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
- entryCriteria: stage.entryCriteria || [],
66
- exitCriteria: stage.exitCriteria || [],
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
- <nav id="app-nav">
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="nav-toggle"
54
- id="nav-toggle"
55
- aria-label="Toggle navigation"
49
+ class="top-bar__toggle"
50
+ id="sidebar-toggle"
51
+ aria-label="Toggle sidebar"
56
52
  >
57
- <span></span>
58
- <span></span>
59
- <span></span>
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
- <ul class="nav-links" id="nav-links">
62
- <li><a href="#/discipline">Disciplines</a></li>
63
- <li><a href="#/grade">Grades</a></li>
64
- <li><a href="#/track">Tracks</a></li>
65
- <li><a href="#/behaviour">Behaviours</a></li>
66
- <li><a href="#/skill">Skills</a></li>
67
- <li><a href="#/driver">Drivers</a></li>
68
- <li><a href="#/stage">Stages</a></li>
69
- <li><a href="#/tool">Tools</a></li>
70
- <li><a href="#/job-builder" class="nav-cta">Build a Job</a></li>
71
- <li><a href="#/agent-builder" class="nav-cta">Build an Agent</a></li>
72
- <li><a href="#/interview-prep" class="nav-cta">Interview Prep</a></li>
73
- <li>
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
- </main>
72
+ </header>
88
73
 
89
- <footer id="app-footer">
90
- <p>Engineering Pathway</p>
91
- <nav class="footer-links">
92
- <a href="slides.html">Slides</a>
93
- <a href="handout.html">Handouts</a>
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
- </footer>
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 navigation toggle for mobile
45
- setupMobileNav();
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 nav brand
179
- const navBrand = document.querySelector(".nav-brand a");
180
- if (navBrand) {
181
- navBrand.textContent = branding.title;
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 nav brand tag
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 navigation toggle
215
+ * Set up drawer overlay for mobile backdrop and auto-close behavior
199
216
  */
200
- function setupMobileNav() {
201
- const toggle = document.getElementById("nav-toggle");
202
- const links = document.getElementById("nav-links");
203
-
204
- if (toggle && links) {
205
- toggle.addEventListener("click", () => {
206
- links.classList.toggle("nav-open");
207
- toggle.classList.toggle("nav-toggle-active");
208
- });
209
-
210
- // Close menu when a link is clicked
211
- links.addEventListener("click", (e) => {
212
- if (e.target.tagName === "A") {
213
- links.classList.remove("nav-open");
214
- toggle.classList.remove("nav-toggle-active");
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