jumpstart-mode 1.1.11 → 1.1.13

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 (188) hide show
  1. package/.github/agents/jumpstart-adversary.agent.md +2 -1
  2. package/.github/agents/jumpstart-architect.agent.md +6 -7
  3. package/.github/agents/jumpstart-challenger.agent.md +2 -1
  4. package/.github/agents/jumpstart-developer.agent.md +1 -1
  5. package/.github/agents/jumpstart-devops.agent.md +2 -2
  6. package/.github/agents/jumpstart-diagram-verifier.agent.md +2 -1
  7. package/.github/agents/jumpstart-maintenance.agent.md +1 -0
  8. package/.github/agents/jumpstart-performance.agent.md +1 -0
  9. package/.github/agents/jumpstart-pm.agent.md +1 -1
  10. package/.github/agents/jumpstart-refactor.agent.md +1 -0
  11. package/.github/agents/jumpstart-requirements-extractor.agent.md +1 -0
  12. package/.github/agents/jumpstart-researcher.agent.md +1 -0
  13. package/.github/agents/jumpstart-retrospective.agent.md +1 -0
  14. package/.github/agents/jumpstart-reviewer.agent.md +2 -0
  15. package/.github/agents/jumpstart-scout.agent.md +1 -1
  16. package/.github/agents/jumpstart-scrum-master.agent.md +1 -0
  17. package/.github/agents/jumpstart-security.agent.md +2 -1
  18. package/.github/agents/jumpstart-tech-writer.agent.md +1 -0
  19. package/.github/agents/jumpstart-uiux-designer.agent.md +66 -0
  20. package/.github/workflows/quality.yml +19 -2
  21. package/.jumpstart/agents/analyst.md +38 -0
  22. package/.jumpstart/agents/architect.md +39 -1
  23. package/.jumpstart/agents/challenger.md +38 -0
  24. package/.jumpstart/agents/developer.md +41 -0
  25. package/.jumpstart/agents/pm.md +38 -0
  26. package/.jumpstart/agents/scout.md +33 -0
  27. package/.jumpstart/agents/ux-designer.md +29 -9
  28. package/.jumpstart/commands/commands.md +6 -5
  29. package/.jumpstart/config.yaml +25 -1
  30. package/.jumpstart/roadmap.md +1 -1
  31. package/.jumpstart/schemas/timeline.schema.json +1 -0
  32. package/.jumpstart/skills/README.md +1 -0
  33. package/.jumpstart/skills/quality-gates/SKILL.md +126 -0
  34. package/.jumpstart/skills/skill-creator/SKILL.md +485 -357
  35. package/.jumpstart/skills/skill-creator/agents/analyzer.md +274 -0
  36. package/.jumpstart/skills/skill-creator/agents/comparator.md +202 -0
  37. package/.jumpstart/skills/skill-creator/agents/grader.md +223 -0
  38. package/.jumpstart/skills/skill-creator/assets/eval_review.html +146 -0
  39. package/.jumpstart/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  40. package/.jumpstart/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  41. package/.jumpstart/skills/skill-creator/references/schemas.md +430 -0
  42. package/.jumpstart/skills/skill-creator/scripts/__init__.py +0 -0
  43. package/.jumpstart/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  44. package/.jumpstart/skills/skill-creator/scripts/generate_report.py +326 -0
  45. package/.jumpstart/skills/skill-creator/scripts/improve_description.py +247 -0
  46. package/.jumpstart/skills/skill-creator/scripts/package_skill.py +136 -110
  47. package/.jumpstart/skills/skill-creator/scripts/run_eval.py +310 -0
  48. package/.jumpstart/skills/skill-creator/scripts/run_loop.py +328 -0
  49. package/.jumpstart/skills/skill-creator/scripts/utils.py +47 -0
  50. package/.jumpstart/skills/ui-ux-pro-max/SKILL.md +266 -0
  51. package/.jumpstart/skills/ui-ux-pro-max/data/charts.csv +26 -0
  52. package/.jumpstart/skills/ui-ux-pro-max/data/colors.csv +97 -0
  53. package/.jumpstart/skills/ui-ux-pro-max/data/icons.csv +101 -0
  54. package/.jumpstart/skills/ui-ux-pro-max/data/landing.csv +31 -0
  55. package/.jumpstart/skills/ui-ux-pro-max/data/products.csv +97 -0
  56. package/.jumpstart/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
  57. package/.jumpstart/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
  58. package/.jumpstart/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  59. package/.jumpstart/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  60. package/.jumpstart/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
  61. package/.jumpstart/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  62. package/.jumpstart/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  63. package/.jumpstart/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  64. package/.jumpstart/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  65. package/.jumpstart/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
  66. package/.jumpstart/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
  67. package/.jumpstart/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  68. package/.jumpstart/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  69. package/.jumpstart/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  70. package/.jumpstart/skills/ui-ux-pro-max/data/styles.csv +68 -0
  71. package/.jumpstart/skills/ui-ux-pro-max/data/typography.csv +58 -0
  72. package/.jumpstart/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
  73. package/.jumpstart/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  74. package/.jumpstart/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
  75. package/.jumpstart/skills/ui-ux-pro-max/scripts/core.py +253 -0
  76. package/.jumpstart/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
  77. package/.jumpstart/skills/ui-ux-pro-max/scripts/search.py +114 -0
  78. package/.jumpstart/state/timeline.json +659 -0
  79. package/.jumpstart/templates/model-map.md +1 -1
  80. package/.jumpstart/templates/ux-design.md +3 -3
  81. package/.jumpstart/usage-log.json +74 -3
  82. package/AGENTS.md +1 -1
  83. package/README.md +64 -3
  84. package/bin/cli.js +3217 -1
  85. package/bin/headless-runner.js +62 -2
  86. package/bin/lib/agent-checkpoint.js +168 -0
  87. package/bin/lib/ai-evaluation.js +104 -0
  88. package/bin/lib/ai-intake.js +152 -0
  89. package/bin/lib/ambiguity-heatmap.js +152 -0
  90. package/bin/lib/artifact-comparison.js +104 -0
  91. package/bin/lib/ast-edit-engine.js +157 -0
  92. package/bin/lib/backlog-sync.js +338 -0
  93. package/bin/lib/bcdr-planning.js +158 -0
  94. package/bin/lib/bidirectional-trace.js +199 -0
  95. package/bin/lib/branch-workflow.js +266 -0
  96. package/bin/lib/cab-output.js +119 -0
  97. package/bin/lib/chat-integration.js +122 -0
  98. package/bin/lib/ci-cd-integration.js +208 -0
  99. package/bin/lib/codebase-retrieval.js +125 -0
  100. package/bin/lib/collaboration.js +168 -0
  101. package/bin/lib/compliance-packs.js +213 -0
  102. package/bin/lib/context-chunker.js +128 -0
  103. package/bin/lib/context-onboarding.js +122 -0
  104. package/bin/lib/contract-first.js +124 -0
  105. package/bin/lib/cost-router.js +148 -0
  106. package/bin/lib/credential-boundary.js +155 -0
  107. package/bin/lib/data-classification.js +180 -0
  108. package/bin/lib/data-contracts.js +129 -0
  109. package/bin/lib/db-evolution.js +158 -0
  110. package/bin/lib/decision-conflicts.js +299 -0
  111. package/bin/lib/delivery-confidence.js +361 -0
  112. package/bin/lib/dependency-upgrade.js +153 -0
  113. package/bin/lib/design-system.js +133 -0
  114. package/bin/lib/deterministic-artifacts.js +151 -0
  115. package/bin/lib/diagram-studio.js +115 -0
  116. package/bin/lib/domain-ontology.js +140 -0
  117. package/bin/lib/ea-review-packet.js +151 -0
  118. package/bin/lib/enterprise-search.js +123 -0
  119. package/bin/lib/enterprise-templates.js +140 -0
  120. package/bin/lib/environment-promotion.js +220 -0
  121. package/bin/lib/estimation-studio.js +130 -0
  122. package/bin/lib/event-modeling.js +133 -0
  123. package/bin/lib/evidence-collector.js +179 -0
  124. package/bin/lib/finops-planner.js +182 -0
  125. package/bin/lib/fitness-functions.js +279 -0
  126. package/bin/lib/focus.js +448 -0
  127. package/bin/lib/governance-dashboard.js +165 -0
  128. package/bin/lib/guided-handoff.js +120 -0
  129. package/bin/lib/impact-analysis.js +190 -0
  130. package/bin/lib/incident-feedback.js +157 -0
  131. package/bin/lib/integrate.js +1 -1
  132. package/bin/lib/knowledge-graph.js +122 -0
  133. package/bin/lib/legacy-modernizer.js +160 -0
  134. package/bin/lib/migration-planner.js +144 -0
  135. package/bin/lib/model-governance.js +185 -0
  136. package/bin/lib/model-router.js +144 -0
  137. package/bin/lib/multi-repo.js +272 -0
  138. package/bin/lib/next-phase.js +53 -8
  139. package/bin/lib/ops-ownership.js +152 -0
  140. package/bin/lib/parallel-agents.js +257 -0
  141. package/bin/lib/pattern-library.js +115 -0
  142. package/bin/lib/persona-packs.js +99 -0
  143. package/bin/lib/plan-executor.js +366 -0
  144. package/bin/lib/platform-engineering.js +119 -0
  145. package/bin/lib/playback-summaries.js +126 -0
  146. package/bin/lib/policy-engine.js +240 -0
  147. package/bin/lib/portfolio-reporting.js +357 -0
  148. package/bin/lib/pr-package.js +197 -0
  149. package/bin/lib/project-memory.js +235 -0
  150. package/bin/lib/prompt-governance.js +130 -0
  151. package/bin/lib/promptless-mode.js +128 -0
  152. package/bin/lib/quality-graph.js +193 -0
  153. package/bin/lib/raci-matrix.js +188 -0
  154. package/bin/lib/refactor-planner.js +167 -0
  155. package/bin/lib/reference-architectures.js +304 -0
  156. package/bin/lib/release-readiness.js +171 -0
  157. package/bin/lib/repo-graph.js +262 -0
  158. package/bin/lib/requirements-baseline.js +358 -0
  159. package/bin/lib/risk-register.js +211 -0
  160. package/bin/lib/role-approval.js +249 -0
  161. package/bin/lib/role-views.js +142 -0
  162. package/bin/lib/root-cause-analysis.js +132 -0
  163. package/bin/lib/runtime-debugger.js +154 -0
  164. package/bin/lib/safe-rename.js +135 -0
  165. package/bin/lib/secret-scanner.js +313 -0
  166. package/bin/lib/semantic-diff.js +335 -0
  167. package/bin/lib/sla-slo.js +210 -0
  168. package/bin/lib/smoke-tester.js +344 -0
  169. package/bin/lib/spec-comments.js +147 -0
  170. package/bin/lib/spec-maturity.js +287 -0
  171. package/bin/lib/sre-integration.js +154 -0
  172. package/bin/lib/structured-elicitation.js +174 -0
  173. package/bin/lib/telemetry-feedback.js +118 -0
  174. package/bin/lib/test-generator.js +146 -0
  175. package/bin/lib/timeline.js +2 -1
  176. package/bin/lib/tool-bridge.js +159 -0
  177. package/bin/lib/tool-guardrails.js +139 -0
  178. package/bin/lib/tool-schemas.js +281 -3
  179. package/bin/lib/transcript-ingestion.js +150 -0
  180. package/bin/lib/type-checker.js +261 -0
  181. package/bin/lib/uat-coverage.js +411 -0
  182. package/bin/lib/vendor-risk.js +173 -0
  183. package/bin/lib/waiver-workflow.js +174 -0
  184. package/bin/lib/web-dashboard.js +126 -0
  185. package/bin/lib/workshop-mode.js +165 -0
  186. package/bin/lib/workstream-ownership.js +104 -0
  187. package/package.json +1 -1
  188. package/.github/agents/jumpstart-ux-designer.agent.md +0 -45
@@ -0,0 +1,173 @@
1
+ /**
2
+ * vendor-risk.js — Vendor & Dependency Risk Scoring (Item 36)
3
+ *
4
+ * Evaluate OSS and SaaS dependencies for maintenance health,
5
+ * license risk, and supply chain concerns.
6
+ *
7
+ * Usage:
8
+ * node bin/lib/vendor-risk.js scan|assess|report [options]
9
+ *
10
+ * State file: .jumpstart/state/vendor-risk.json
11
+ */
12
+
13
+ 'use strict';
14
+
15
+ const fs = require('fs');
16
+ const path = require('path');
17
+
18
+ const DEFAULT_STATE_FILE = path.join('.jumpstart', 'state', 'vendor-risk.json');
19
+
20
+ const RISK_FACTORS = ['maintenance', 'license', 'security', 'popularity', 'supply-chain'];
21
+
22
+ const LICENSE_RISK = {
23
+ 'MIT': 'low', 'ISC': 'low', 'BSD-2-Clause': 'low', 'BSD-3-Clause': 'low', 'Apache-2.0': 'low',
24
+ 'LGPL-2.1': 'medium', 'LGPL-3.0': 'medium', 'MPL-2.0': 'medium',
25
+ 'GPL-2.0': 'high', 'GPL-3.0': 'high', 'AGPL-3.0': 'high',
26
+ 'SSPL-1.0': 'critical', 'BSL-1.1': 'high', 'UNLICENSED': 'critical', 'unknown': 'high'
27
+ };
28
+
29
+ function defaultState() {
30
+ return {
31
+ version: '1.0.0',
32
+ created_at: new Date().toISOString(),
33
+ last_updated: null,
34
+ assessments: [],
35
+ vendor_catalog: []
36
+ };
37
+ }
38
+
39
+ function loadState(stateFile) {
40
+ const filePath = stateFile || DEFAULT_STATE_FILE;
41
+ if (!fs.existsSync(filePath)) return defaultState();
42
+ try { return JSON.parse(fs.readFileSync(filePath, 'utf8')); }
43
+ catch { return defaultState(); }
44
+ }
45
+
46
+ function saveState(state, stateFile) {
47
+ const filePath = stateFile || DEFAULT_STATE_FILE;
48
+ const dir = path.dirname(filePath);
49
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
50
+ state.last_updated = new Date().toISOString();
51
+ fs.writeFileSync(filePath, JSON.stringify(state, null, 2) + '\n', 'utf8');
52
+ }
53
+
54
+ /**
55
+ * Scan project dependencies.
56
+ *
57
+ * @param {string} root - Project root.
58
+ * @param {object} [options]
59
+ * @returns {object}
60
+ */
61
+ function scanDependencies(root, options = {}) {
62
+ const packageFile = path.join(root, 'package.json');
63
+ const dependencies = [];
64
+
65
+ if (fs.existsSync(packageFile)) {
66
+ try {
67
+ const pkg = JSON.parse(fs.readFileSync(packageFile, 'utf8'));
68
+ const deps = { ...pkg.dependencies };
69
+ const devDeps = options.includeDevDeps ? pkg.devDependencies || {} : {};
70
+
71
+ for (const [name, version] of Object.entries(deps)) {
72
+ dependencies.push({ name, version, type: 'production', ecosystem: 'npm' });
73
+ }
74
+ for (const [name, version] of Object.entries(devDeps)) {
75
+ dependencies.push({ name, version, type: 'development', ecosystem: 'npm' });
76
+ }
77
+ } catch { /* ignore */ }
78
+ }
79
+
80
+ return { success: true, dependencies, total: dependencies.length };
81
+ }
82
+
83
+ /**
84
+ * Assess risk for a dependency.
85
+ *
86
+ * @param {object} dep - { name, version, license?, last_publish?, weekly_downloads? }
87
+ * @param {object} [options]
88
+ * @returns {object}
89
+ */
90
+ function assessDependency(dep, options = {}) {
91
+ if (!dep || !dep.name) return { success: false, error: 'dep.name is required' };
92
+
93
+ const scores = {};
94
+
95
+ // License risk
96
+ const license = dep.license || 'unknown';
97
+ const licenseRisk = LICENSE_RISK[license] || 'high';
98
+ scores.license = licenseRisk === 'low' ? 90 : licenseRisk === 'medium' ? 60 : licenseRisk === 'high' ? 30 : 10;
99
+
100
+ // Maintenance (based on last publish date if available)
101
+ if (dep.last_publish) {
102
+ const daysSince = Math.floor((Date.now() - new Date(dep.last_publish).getTime()) / (1000 * 60 * 60 * 24));
103
+ scores.maintenance = daysSince < 90 ? 90 : daysSince < 365 ? 60 : daysSince < 730 ? 30 : 10;
104
+ } else {
105
+ scores.maintenance = 50; // unknown
106
+ }
107
+
108
+ // Popularity
109
+ const downloads = dep.weekly_downloads || 0;
110
+ scores.popularity = downloads > 1000000 ? 90 : downloads > 100000 ? 70 : downloads > 10000 ? 50 : 30;
111
+
112
+ // Security (default: no known issues)
113
+ scores.security = dep.known_vulnerabilities ? 20 : 80;
114
+
115
+ // Supply chain
116
+ scores['supply-chain'] = dep.has_lockfile ? 80 : 50;
117
+
118
+ const overall = Math.round(Object.values(scores).reduce((a, b) => a + b, 0) / Object.keys(scores).length);
119
+
120
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
121
+ const state = loadState(stateFile);
122
+
123
+ const assessment = {
124
+ name: dep.name,
125
+ version: dep.version,
126
+ license,
127
+ scores,
128
+ overall,
129
+ risk_level: overall >= 70 ? 'low' : overall >= 50 ? 'medium' : overall >= 30 ? 'high' : 'critical',
130
+ assessed_at: new Date().toISOString()
131
+ };
132
+
133
+ state.assessments.push(assessment);
134
+ saveState(state, stateFile);
135
+
136
+ return { success: true, assessment };
137
+ }
138
+
139
+ /**
140
+ * Generate vendor risk report.
141
+ *
142
+ * @param {object} [options]
143
+ * @returns {object}
144
+ */
145
+ function generateReport(options = {}) {
146
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
147
+ const state = loadState(stateFile);
148
+
149
+ const byRisk = { low: 0, medium: 0, high: 0, critical: 0 };
150
+ for (const a of state.assessments) byRisk[a.risk_level]++;
151
+
152
+ return {
153
+ success: true,
154
+ total_assessed: state.assessments.length,
155
+ by_risk: byRisk,
156
+ high_risk: state.assessments.filter(a => a.risk_level === 'high' || a.risk_level === 'critical'),
157
+ average_score: state.assessments.length > 0
158
+ ? Math.round(state.assessments.reduce((s, a) => s + a.overall, 0) / state.assessments.length)
159
+ : 0,
160
+ assessments: state.assessments
161
+ };
162
+ }
163
+
164
+ module.exports = {
165
+ defaultState,
166
+ loadState,
167
+ saveState,
168
+ scanDependencies,
169
+ assessDependency,
170
+ generateReport,
171
+ RISK_FACTORS,
172
+ LICENSE_RISK
173
+ };
@@ -0,0 +1,174 @@
1
+ /**
2
+ * waiver-workflow.js — Exception & Waiver Workflow (Item 27)
3
+ *
4
+ * Allow formal approval of justified deviations from standards
5
+ * with expiration and owner.
6
+ *
7
+ * Usage:
8
+ * node bin/lib/waiver-workflow.js request|approve|list|expire [options]
9
+ *
10
+ * State file: .jumpstart/state/waivers.json
11
+ */
12
+
13
+ 'use strict';
14
+
15
+ const fs = require('fs');
16
+ const path = require('path');
17
+
18
+ const DEFAULT_STATE_FILE = path.join('.jumpstart', 'state', 'waivers.json');
19
+
20
+ const WAIVER_STATUSES = ['pending', 'approved', 'rejected', 'expired', 'revoked'];
21
+ const WAIVER_CATEGORIES = ['security', 'architecture', 'compliance', 'performance', 'testing', 'documentation', 'other'];
22
+
23
+ function defaultState() {
24
+ return {
25
+ version: '1.0.0',
26
+ created_at: new Date().toISOString(),
27
+ last_updated: null,
28
+ waivers: []
29
+ };
30
+ }
31
+
32
+ function loadState(stateFile) {
33
+ const filePath = stateFile || DEFAULT_STATE_FILE;
34
+ if (!fs.existsSync(filePath)) return defaultState();
35
+ try { return JSON.parse(fs.readFileSync(filePath, 'utf8')); }
36
+ catch { return defaultState(); }
37
+ }
38
+
39
+ function saveState(state, stateFile) {
40
+ const filePath = stateFile || DEFAULT_STATE_FILE;
41
+ const dir = path.dirname(filePath);
42
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
43
+ state.last_updated = new Date().toISOString();
44
+ fs.writeFileSync(filePath, JSON.stringify(state, null, 2) + '\n', 'utf8');
45
+ }
46
+
47
+ /**
48
+ * Request a new waiver.
49
+ *
50
+ * @param {object} request - { title, category, justification, owner, expires_in_days? }
51
+ * @param {object} [options]
52
+ * @returns {object}
53
+ */
54
+ function requestWaiver(request, options = {}) {
55
+ if (!request || !request.title || !request.justification || !request.owner) {
56
+ return { success: false, error: 'title, justification, and owner are required' };
57
+ }
58
+
59
+ const category = (request.category || 'other').toLowerCase();
60
+ if (!WAIVER_CATEGORIES.includes(category)) {
61
+ return { success: false, error: `Invalid category. Must be one of: ${WAIVER_CATEGORIES.join(', ')}` };
62
+ }
63
+
64
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
65
+ const state = loadState(stateFile);
66
+
67
+ const expiresInDays = request.expires_in_days || 90;
68
+ const expiresAt = new Date();
69
+ expiresAt.setDate(expiresAt.getDate() + expiresInDays);
70
+
71
+ const waiver = {
72
+ id: `WVR-${Date.now().toString(36).toUpperCase()}`,
73
+ title: request.title,
74
+ category,
75
+ justification: request.justification,
76
+ owner: request.owner,
77
+ status: 'pending',
78
+ requested_at: new Date().toISOString(),
79
+ expires_at: expiresAt.toISOString(),
80
+ approved_by: null,
81
+ approved_at: null,
82
+ conditions: request.conditions || [],
83
+ affected_artifacts: request.affected_artifacts || []
84
+ };
85
+
86
+ state.waivers.push(waiver);
87
+ saveState(state, stateFile);
88
+
89
+ return { success: true, waiver };
90
+ }
91
+
92
+ /**
93
+ * Approve or reject a waiver.
94
+ *
95
+ * @param {string} waiverId
96
+ * @param {string} action - 'approve' or 'reject'
97
+ * @param {object} [options]
98
+ * @returns {object}
99
+ */
100
+ function resolveWaiver(waiverId, action, options = {}) {
101
+ if (!['approve', 'reject'].includes(action)) {
102
+ return { success: false, error: 'action must be "approve" or "reject"' };
103
+ }
104
+
105
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
106
+ const state = loadState(stateFile);
107
+
108
+ const waiver = state.waivers.find(w => w.id === waiverId);
109
+ if (!waiver) return { success: false, error: `Waiver not found: ${waiverId}` };
110
+ if (waiver.status !== 'pending') {
111
+ return { success: false, error: `Waiver is already ${waiver.status}` };
112
+ }
113
+
114
+ waiver.status = action === 'approve' ? 'approved' : 'rejected';
115
+ waiver.approved_by = options.approver || null;
116
+ waiver.approved_at = new Date().toISOString();
117
+
118
+ saveState(state, stateFile);
119
+ return { success: true, waiver };
120
+ }
121
+
122
+ /**
123
+ * Expire outdated waivers.
124
+ *
125
+ * @param {object} [options]
126
+ * @returns {object}
127
+ */
128
+ function expireWaivers(options = {}) {
129
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
130
+ const state = loadState(stateFile);
131
+ const now = new Date();
132
+ let expired = 0;
133
+
134
+ for (const waiver of state.waivers) {
135
+ if (waiver.status === 'approved' && new Date(waiver.expires_at) < now) {
136
+ waiver.status = 'expired';
137
+ expired++;
138
+ }
139
+ }
140
+
141
+ saveState(state, stateFile);
142
+ return { success: true, expired, total_waivers: state.waivers.length };
143
+ }
144
+
145
+ /**
146
+ * List waivers with optional filter.
147
+ *
148
+ * @param {object} [filter] - { status?, category?, owner? }
149
+ * @param {object} [options]
150
+ * @returns {object}
151
+ */
152
+ function listWaivers(filter = {}, options = {}) {
153
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
154
+ const state = loadState(stateFile);
155
+ let waivers = state.waivers;
156
+
157
+ if (filter.status) waivers = waivers.filter(w => w.status === filter.status);
158
+ if (filter.category) waivers = waivers.filter(w => w.category === filter.category);
159
+ if (filter.owner) waivers = waivers.filter(w => w.owner === filter.owner);
160
+
161
+ return { success: true, waivers, total: waivers.length };
162
+ }
163
+
164
+ module.exports = {
165
+ defaultState,
166
+ loadState,
167
+ saveState,
168
+ requestWaiver,
169
+ resolveWaiver,
170
+ expireWaivers,
171
+ listWaivers,
172
+ WAIVER_STATUSES,
173
+ WAIVER_CATEGORIES
174
+ };
@@ -0,0 +1,126 @@
1
+ /**
2
+ * web-dashboard.js — Rich Web UI / Local Dashboard (Item 61)
3
+ *
4
+ * Serve a local web control center for project management,
5
+ * phase tracking, artifact browsing, and governance overview.
6
+ *
7
+ * Usage:
8
+ * node bin/lib/web-dashboard.js start|status|config [options]
9
+ */
10
+
11
+ 'use strict';
12
+
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+
16
+ const DEFAULT_PORT = 3000;
17
+ const DEFAULT_HOST = 'localhost';
18
+
19
+ const DASHBOARD_SECTIONS = ['phases', 'artifacts', 'governance', 'timeline', 'risks', 'metrics'];
20
+
21
+ /**
22
+ * Generate dashboard configuration.
23
+ */
24
+ function generateConfig(root, options = {}) {
25
+ const port = options.port || DEFAULT_PORT;
26
+ const host = options.host || DEFAULT_HOST;
27
+
28
+ const config = {
29
+ port,
30
+ host,
31
+ root: path.resolve(root),
32
+ sections: options.sections || DASHBOARD_SECTIONS,
33
+ theme: options.theme || 'default',
34
+ auth: options.auth || { enabled: false },
35
+ refresh_interval: options.refresh_interval || 30,
36
+ generated_at: new Date().toISOString()
37
+ };
38
+
39
+ return { success: true, config };
40
+ }
41
+
42
+ /**
43
+ * Gather dashboard data from project state.
44
+ */
45
+ function gatherDashboardData(root, options = {}) {
46
+ const data = {
47
+ project_root: root,
48
+ generated_at: new Date().toISOString(),
49
+ sections: {}
50
+ };
51
+
52
+ // Phase status
53
+ const stateFile = path.join(root, '.jumpstart', 'state', 'state.json');
54
+ if (fs.existsSync(stateFile)) {
55
+ try {
56
+ const state = JSON.parse(fs.readFileSync(stateFile, 'utf8'));
57
+ data.sections.phases = {
58
+ current_phase: state.current_phase || 0,
59
+ current_agent: state.current_agent || null,
60
+ last_completed_step: state.last_completed_step || null
61
+ };
62
+ } catch { data.sections.phases = { current_phase: 0 }; }
63
+ } else {
64
+ data.sections.phases = { current_phase: 0 };
65
+ }
66
+
67
+ // Artifacts
68
+ const specsDir = path.join(root, 'specs');
69
+ const artifacts = [];
70
+ if (fs.existsSync(specsDir)) {
71
+ for (const f of fs.readdirSync(specsDir).filter(f => f.endsWith('.md'))) {
72
+ const fp = path.join(specsDir, f);
73
+ const stat = fs.statSync(fp);
74
+ artifacts.push({ name: f, size: stat.size, modified: stat.mtime.toISOString() });
75
+ }
76
+ }
77
+ data.sections.artifacts = { total: artifacts.length, files: artifacts };
78
+
79
+ // Config summary
80
+ const configFile = path.join(root, '.jumpstart', 'config.yaml');
81
+ data.sections.config = { exists: fs.existsSync(configFile) };
82
+
83
+ return { success: true, ...data };
84
+ }
85
+
86
+ /**
87
+ * Generate static HTML dashboard.
88
+ */
89
+ function generateStaticDashboard(data) {
90
+ const html = `<!DOCTYPE html>
91
+ <html lang="en">
92
+ <head><meta charset="UTF-8"><title>Jump Start Dashboard</title></head>
93
+ <body>
94
+ <h1>Jump Start Dashboard</h1>
95
+ <p>Generated: ${data.generated_at}</p>
96
+ <h2>Phase Status</h2>
97
+ <p>Current Phase: ${data.sections.phases.current_phase}</p>
98
+ <h2>Artifacts (${data.sections.artifacts.total})</h2>
99
+ <ul>${data.sections.artifacts.files.map(f => `<li>${f.name} (${f.size} bytes)</li>`).join('')}</ul>
100
+ </body>
101
+ </html>`;
102
+ return { success: true, html };
103
+ }
104
+
105
+ /**
106
+ * Get server status.
107
+ */
108
+ function getServerStatus(options = {}) {
109
+ return {
110
+ success: true,
111
+ running: false,
112
+ port: options.port || DEFAULT_PORT,
113
+ host: options.host || DEFAULT_HOST,
114
+ uptime: null
115
+ };
116
+ }
117
+
118
+ module.exports = {
119
+ generateConfig,
120
+ gatherDashboardData,
121
+ generateStaticDashboard,
122
+ getServerStatus,
123
+ DASHBOARD_SECTIONS,
124
+ DEFAULT_PORT,
125
+ DEFAULT_HOST
126
+ };
@@ -0,0 +1,165 @@
1
+ /**
2
+ * workshop-mode.js — Live Workshop Mode (Item 64)
3
+ *
4
+ * Facilitate discovery sessions and convert outputs directly
5
+ * into challenger brief, product brief, and PRD.
6
+ *
7
+ * Usage:
8
+ * node bin/lib/workshop-mode.js start|capture|convert|status [options]
9
+ *
10
+ * State file: .jumpstart/state/workshop.json
11
+ */
12
+
13
+ 'use strict';
14
+
15
+ const fs = require('fs');
16
+ const path = require('path');
17
+
18
+ const DEFAULT_STATE_FILE = path.join('.jumpstart', 'state', 'workshop.json');
19
+
20
+ const WORKSHOP_TYPES = ['discovery', 'ideation', 'refinement', 'retrospective'];
21
+ const OUTPUT_ARTIFACTS = ['challenger-brief', 'product-brief', 'prd'];
22
+
23
+ function defaultState() {
24
+ return {
25
+ version: '1.0.0',
26
+ sessions: [],
27
+ last_updated: null
28
+ };
29
+ }
30
+
31
+ function loadState(stateFile) {
32
+ const fp = stateFile || DEFAULT_STATE_FILE;
33
+ if (!fs.existsSync(fp)) return defaultState();
34
+ try { return JSON.parse(fs.readFileSync(fp, 'utf8')); }
35
+ catch { return defaultState(); }
36
+ }
37
+
38
+ function saveState(state, stateFile) {
39
+ const fp = stateFile || DEFAULT_STATE_FILE;
40
+ const dir = path.dirname(fp);
41
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
42
+ state.last_updated = new Date().toISOString();
43
+ fs.writeFileSync(fp, JSON.stringify(state, null, 2) + '\n', 'utf8');
44
+ }
45
+
46
+ /**
47
+ * Start a new workshop session.
48
+ */
49
+ function startSession(name, options = {}) {
50
+ if (!name) return { success: false, error: 'Session name is required' };
51
+
52
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
53
+ const state = loadState(stateFile);
54
+
55
+ const session = {
56
+ id: `WS-${Date.now()}`,
57
+ name,
58
+ type: options.type || 'discovery',
59
+ status: 'active',
60
+ facilitator: options.facilitator || null,
61
+ participants: options.participants || [],
62
+ captures: [],
63
+ created_at: new Date().toISOString(),
64
+ ended_at: null
65
+ };
66
+
67
+ if (!WORKSHOP_TYPES.includes(session.type)) {
68
+ return { success: false, error: `Unknown type: ${session.type}. Valid: ${WORKSHOP_TYPES.join(', ')}` };
69
+ }
70
+
71
+ state.sessions.push(session);
72
+ saveState(state, stateFile);
73
+
74
+ return { success: true, session };
75
+ }
76
+
77
+ /**
78
+ * Capture a workshop insight or decision.
79
+ */
80
+ function captureInsight(sessionId, text, options = {}) {
81
+ if (!sessionId || !text) return { success: false, error: 'sessionId and text are required' };
82
+
83
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
84
+ const state = loadState(stateFile);
85
+
86
+ const session = state.sessions.find(s => s.id === sessionId);
87
+ if (!session) return { success: false, error: `Session ${sessionId} not found` };
88
+
89
+ const capture = {
90
+ id: `CAP-${Date.now()}`,
91
+ text,
92
+ category: options.category || 'insight',
93
+ author: options.author || 'anonymous',
94
+ timestamp: new Date().toISOString()
95
+ };
96
+
97
+ session.captures.push(capture);
98
+ saveState(state, stateFile);
99
+
100
+ return { success: true, capture };
101
+ }
102
+
103
+ /**
104
+ * Convert session captures to artifact outline.
105
+ */
106
+ function convertToArtifact(sessionId, artifactType, options = {}) {
107
+ if (!sessionId || !artifactType) return { success: false, error: 'sessionId and artifactType are required' };
108
+ if (!OUTPUT_ARTIFACTS.includes(artifactType)) {
109
+ return { success: false, error: `Unknown artifact type. Valid: ${OUTPUT_ARTIFACTS.join(', ')}` };
110
+ }
111
+
112
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
113
+ const state = loadState(stateFile);
114
+
115
+ const session = state.sessions.find(s => s.id === sessionId);
116
+ if (!session) return { success: false, error: `Session ${sessionId} not found` };
117
+
118
+ const sections = session.captures.reduce((acc, cap) => {
119
+ const cat = cap.category || 'general';
120
+ if (!acc[cat]) acc[cat] = [];
121
+ acc[cat].push(cap.text);
122
+ return acc;
123
+ }, {});
124
+
125
+ return {
126
+ success: true,
127
+ artifact_type: artifactType,
128
+ session_name: session.name,
129
+ sections,
130
+ captures_used: session.captures.length
131
+ };
132
+ }
133
+
134
+ /**
135
+ * Get session status.
136
+ */
137
+ function getSessionStatus(options = {}) {
138
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
139
+ const state = loadState(stateFile);
140
+
141
+ return {
142
+ success: true,
143
+ total_sessions: state.sessions.length,
144
+ active: state.sessions.filter(s => s.status === 'active').length,
145
+ sessions: state.sessions.map(s => ({
146
+ id: s.id,
147
+ name: s.name,
148
+ type: s.type,
149
+ status: s.status,
150
+ captures: s.captures.length
151
+ }))
152
+ };
153
+ }
154
+
155
+ module.exports = {
156
+ startSession,
157
+ captureInsight,
158
+ convertToArtifact,
159
+ getSessionStatus,
160
+ loadState,
161
+ saveState,
162
+ defaultState,
163
+ WORKSHOP_TYPES,
164
+ OUTPUT_ARTIFACTS
165
+ };