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
@@ -37,6 +37,10 @@ const { getToolsForPhase } = require('./lib/tool-schemas');
37
37
  const { createMockRegistry, createPersonaRegistry } = require('./lib/mock-responses');
38
38
  const { SimulationTracer } = require('./lib/simulation-tracer');
39
39
 
40
+ // Usage & timeline logging (ESM — loaded dynamically)
41
+ let _usageMod = null;
42
+ const _usageReady = import('./lib/usage.js').then(mod => { _usageMod = mod; }).catch(() => {});
43
+
40
44
  // ─── Configuration ───────────────────────────────────────────────────────────
41
45
 
42
46
  const ROOT_DIR = path.join(__dirname, '..');
@@ -182,6 +186,9 @@ class HeadlessRunner {
182
186
  // Initialize tracer
183
187
  this.tracer = new SimulationTracer(this.workspaceDir, options.scenario || 'headless');
184
188
 
189
+ // Usage log path
190
+ this.usageLogPath = path.join(this.workspaceDir, '.jumpstart', 'usage-log.json');
191
+
185
192
  // Initialize timeline for event recording
186
193
  this.timeline = null;
187
194
  try {
@@ -199,6 +206,10 @@ class HeadlessRunner {
199
206
  captureSubagents: true,
200
207
  captureResearch: true
201
208
  });
209
+ // Connect timeline to usage logger so usage events appear in timeline
210
+ if (_usageMod && typeof _usageMod.setUsageTimelineHook === 'function') {
211
+ _usageMod.setUsageTimelineHook(this.timeline);
212
+ }
202
213
  }).catch(() => { /* timeline module not available — ok */ });
203
214
  } catch {
204
215
  this._timelineReady = Promise.resolve();
@@ -234,8 +245,9 @@ class HeadlessRunner {
234
245
  }
235
246
 
236
247
  async setup() {
237
- // Ensure timeline is ready
248
+ // Ensure timeline and usage modules are ready
238
249
  if (this._timelineReady) await this._timelineReady;
250
+ await _usageReady;
239
251
 
240
252
  // Create workspace directory structure
241
253
  const dirs = [
@@ -518,6 +530,20 @@ Be brief and supportive.`;
518
530
  const agentPrompt = this.loadAgentPrompt(agentName);
519
531
  const personaPrompt = this.loadPersonaPrompt();
520
532
 
533
+ // Log agent system prompt to timeline
534
+ if (this.timeline) {
535
+ this.timeline.recordEvent({
536
+ event_type: 'prompt_logged',
537
+ action: `System prompt loaded for ${agentName} (${agentPrompt.length} chars)`,
538
+ metadata: {
539
+ prompt_type: 'system',
540
+ agent: agentName,
541
+ prompt_length: agentPrompt.length,
542
+ prompt_preview: agentPrompt.substring(0, 200) + (agentPrompt.length > 200 ? '…' : '')
543
+ }
544
+ });
545
+ }
546
+
521
547
  // Initialize conversation histories
522
548
  this.conversationHistory = [
523
549
  { role: 'system', content: agentPrompt }
@@ -534,6 +560,20 @@ Be brief and supportive.`;
534
560
  const startMessage = this.getAgentStartMessage(agentName);
535
561
  this.conversationHistory.push({ role: 'user', content: startMessage });
536
562
 
563
+ // Log activation prompt to timeline
564
+ if (this.timeline) {
565
+ this.timeline.recordEvent({
566
+ event_type: 'prompt_logged',
567
+ action: `Activation prompt sent to ${agentName}`,
568
+ metadata: {
569
+ prompt_type: 'activation',
570
+ agent: agentName,
571
+ prompt_length: startMessage.length,
572
+ prompt_content: startMessage
573
+ }
574
+ });
575
+ }
576
+
537
577
  // Main conversation loop
538
578
  this.turnCount = 0;
539
579
  let sessionActive = true;
@@ -620,10 +660,30 @@ Be brief and supportive.`;
620
660
  this.timeline.flush();
621
661
  }
622
662
 
623
- // Log usage
663
+ // Log usage to usage-log.json and console
624
664
  const usage = this.agentProvider.getUsage();
625
665
  this.log(`Usage: ${usage.totalTokens} tokens, ${usage.calls} calls`, 'info');
626
666
 
667
+ if (_usageMod && typeof _usageMod.logUsage === 'function') {
668
+ try {
669
+ _usageMod.logUsage(this.usageLogPath, {
670
+ phase: agentName,
671
+ agent: agentName.charAt(0).toUpperCase() + agentName.slice(1),
672
+ action: 'generation',
673
+ estimated_tokens: usage.totalTokens || 0,
674
+ estimated_cost_usd: (usage.totalTokens || 0) * 0.000002,
675
+ model: this.options.model || 'unknown',
676
+ metadata: {
677
+ turns: this.turnCount,
678
+ calls: usage.calls || 0,
679
+ status: finalStatus
680
+ }
681
+ });
682
+ } catch (err) {
683
+ this.log(`Warning: Failed to write usage log: ${err.message}`, 'warn');
684
+ }
685
+ }
686
+
627
687
  return finalStatus;
628
688
  }
629
689
 
@@ -0,0 +1,168 @@
1
+ /**
2
+ * agent-checkpoint.js — Agent Self-Checkpoint & Resume (Item 57)
3
+ *
4
+ * Recover gracefully from interrupted runs or model failures.
5
+ *
6
+ * Usage:
7
+ * node bin/lib/agent-checkpoint.js save|restore|list|clean [options]
8
+ *
9
+ * State file: .jumpstart/state/agent-checkpoints.json
10
+ */
11
+
12
+ 'use strict';
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+
17
+ const DEFAULT_STATE_FILE = path.join('.jumpstart', 'state', 'agent-checkpoints.json');
18
+
19
+ const CHECKPOINT_TYPES = ['phase-start', 'phase-end', 'task-start', 'task-end', 'error-recovery', 'manual'];
20
+
21
+ function defaultState() {
22
+ return {
23
+ version: '1.0.0',
24
+ created_at: new Date().toISOString(),
25
+ last_updated: null,
26
+ checkpoints: [],
27
+ recovery_log: []
28
+ };
29
+ }
30
+
31
+ function loadState(stateFile) {
32
+ const filePath = stateFile || DEFAULT_STATE_FILE;
33
+ if (!fs.existsSync(filePath)) return defaultState();
34
+ try { return JSON.parse(fs.readFileSync(filePath, 'utf8')); }
35
+ catch { return defaultState(); }
36
+ }
37
+
38
+ function saveState(state, stateFile) {
39
+ const filePath = stateFile || DEFAULT_STATE_FILE;
40
+ const dir = path.dirname(filePath);
41
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
42
+ state.last_updated = new Date().toISOString();
43
+ fs.writeFileSync(filePath, JSON.stringify(state, null, 2) + '\n', 'utf8');
44
+ }
45
+
46
+ /**
47
+ * Save a checkpoint.
48
+ *
49
+ * @param {object} checkpoint - { agent, phase?, task?, context, type? }
50
+ * @param {object} [options]
51
+ * @returns {object}
52
+ */
53
+ function saveCheckpoint(checkpoint, options = {}) {
54
+ if (!checkpoint || !checkpoint.agent) {
55
+ return { success: false, error: 'agent is required' };
56
+ }
57
+
58
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
59
+ const state = loadState(stateFile);
60
+
61
+ const cp = {
62
+ id: `CP-${Date.now().toString(36).toUpperCase()}`,
63
+ agent: checkpoint.agent,
64
+ phase: checkpoint.phase || null,
65
+ task: checkpoint.task || null,
66
+ type: checkpoint.type || 'manual',
67
+ context: checkpoint.context || {},
68
+ files_snapshot: checkpoint.files_snapshot || [],
69
+ saved_at: new Date().toISOString()
70
+ };
71
+
72
+ state.checkpoints.push(cp);
73
+
74
+ // Keep only last 50 checkpoints
75
+ if (state.checkpoints.length > 50) {
76
+ state.checkpoints = state.checkpoints.slice(-50);
77
+ }
78
+
79
+ saveState(state, stateFile);
80
+
81
+ return { success: true, checkpoint: cp };
82
+ }
83
+
84
+ /**
85
+ * Restore from a checkpoint.
86
+ *
87
+ * @param {string} [checkpointId] - If omitted, restores from latest.
88
+ * @param {object} [options]
89
+ * @returns {object}
90
+ */
91
+ function restoreCheckpoint(checkpointId, options = {}) {
92
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
93
+ const state = loadState(stateFile);
94
+
95
+ let checkpoint;
96
+ if (checkpointId) {
97
+ checkpoint = state.checkpoints.find(c => c.id === checkpointId);
98
+ } else {
99
+ checkpoint = state.checkpoints[state.checkpoints.length - 1];
100
+ }
101
+
102
+ if (!checkpoint) {
103
+ return { success: false, error: checkpointId ? `Checkpoint not found: ${checkpointId}` : 'No checkpoints available' };
104
+ }
105
+
106
+ state.recovery_log.push({
107
+ checkpoint_id: checkpoint.id,
108
+ restored_at: new Date().toISOString()
109
+ });
110
+ saveState(state, stateFile);
111
+
112
+ return {
113
+ success: true,
114
+ checkpoint,
115
+ agent: checkpoint.agent,
116
+ phase: checkpoint.phase,
117
+ task: checkpoint.task,
118
+ context: checkpoint.context
119
+ };
120
+ }
121
+
122
+ /**
123
+ * List available checkpoints.
124
+ *
125
+ * @param {object} [filter] - { agent?, phase?, type? }
126
+ * @param {object} [options]
127
+ * @returns {object}
128
+ */
129
+ function listCheckpoints(filter = {}, options = {}) {
130
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
131
+ const state = loadState(stateFile);
132
+ let checkpoints = state.checkpoints;
133
+
134
+ if (filter.agent) checkpoints = checkpoints.filter(c => c.agent === filter.agent);
135
+ if (filter.phase) checkpoints = checkpoints.filter(c => c.phase === filter.phase);
136
+ if (filter.type) checkpoints = checkpoints.filter(c => c.type === filter.type);
137
+
138
+ return { success: true, checkpoints, total: checkpoints.length };
139
+ }
140
+
141
+ /**
142
+ * Clean old checkpoints.
143
+ *
144
+ * @param {object} [options] - { keep? }
145
+ * @returns {object}
146
+ */
147
+ function cleanCheckpoints(options = {}) {
148
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
149
+ const state = loadState(stateFile);
150
+ const keep = options.keep || 10;
151
+ const removed = Math.max(0, state.checkpoints.length - keep);
152
+
153
+ state.checkpoints = state.checkpoints.slice(-keep);
154
+ saveState(state, stateFile);
155
+
156
+ return { success: true, removed, remaining: state.checkpoints.length };
157
+ }
158
+
159
+ module.exports = {
160
+ defaultState,
161
+ loadState,
162
+ saveState,
163
+ saveCheckpoint,
164
+ restoreCheckpoint,
165
+ listCheckpoints,
166
+ cleanCheckpoints,
167
+ CHECKPOINT_TYPES
168
+ };
@@ -0,0 +1,104 @@
1
+ /**
2
+ * ai-evaluation.js — Evaluation Framework for AI Systems (Item 90)
3
+ *
4
+ * Groundedness, hallucination, safety, latency, cost,
5
+ * and business KPI eval packs.
6
+ *
7
+ * Usage:
8
+ * node bin/lib/ai-evaluation.js evaluate|report|configure [options]
9
+ *
10
+ * State file: .jumpstart/state/ai-evaluation.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', 'ai-evaluation.json');
19
+
20
+ const EVAL_DIMENSIONS = ['groundedness', 'hallucination', 'safety', 'latency', 'cost', 'relevance', 'coherence'];
21
+
22
+ function defaultState() {
23
+ return { version: '1.0.0', evaluations: [], benchmarks: [], last_updated: null };
24
+ }
25
+
26
+ function loadState(stateFile) {
27
+ const fp = stateFile || DEFAULT_STATE_FILE;
28
+ if (!fs.existsSync(fp)) return defaultState();
29
+ try { return JSON.parse(fs.readFileSync(fp, 'utf8')); }
30
+ catch { return defaultState(); }
31
+ }
32
+
33
+ function saveState(state, stateFile) {
34
+ const fp = stateFile || DEFAULT_STATE_FILE;
35
+ const dir = path.dirname(fp);
36
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
37
+ state.last_updated = new Date().toISOString();
38
+ fs.writeFileSync(fp, JSON.stringify(state, null, 2) + '\n', 'utf8');
39
+ }
40
+
41
+ function evaluate(name, scores, options = {}) {
42
+ if (!name || !scores) return { success: false, error: 'name and scores are required' };
43
+
44
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
45
+ const state = loadState(stateFile);
46
+
47
+ const evaluation = {
48
+ id: `EVAL-${Date.now()}`,
49
+ name,
50
+ scores,
51
+ overall: Math.round(Object.values(scores).reduce((a, b) => a + b, 0) / Object.keys(scores).length),
52
+ model: options.model || null,
53
+ use_case: options.use_case || null,
54
+ evaluated_at: new Date().toISOString()
55
+ };
56
+
57
+ state.evaluations.push(evaluation);
58
+ saveState(state, stateFile);
59
+
60
+ return { success: true, evaluation };
61
+ }
62
+
63
+ function generateReport(options = {}) {
64
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
65
+ const state = loadState(stateFile);
66
+
67
+ const avgScores = {};
68
+ for (const dim of EVAL_DIMENSIONS) {
69
+ const vals = state.evaluations.filter(e => e.scores[dim] !== undefined).map(e => e.scores[dim]);
70
+ avgScores[dim] = vals.length > 0 ? Math.round(vals.reduce((a, b) => a + b, 0) / vals.length) : null;
71
+ }
72
+
73
+ return {
74
+ success: true,
75
+ total_evaluations: state.evaluations.length,
76
+ average_scores: avgScores,
77
+ evaluations: state.evaluations
78
+ };
79
+ }
80
+
81
+ function configureBenchmark(name, thresholds, options = {}) {
82
+ if (!name || !thresholds) return { success: false, error: 'name and thresholds are required' };
83
+
84
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
85
+ const state = loadState(stateFile);
86
+
87
+ const benchmark = {
88
+ id: `BENCH-${Date.now()}`,
89
+ name,
90
+ thresholds,
91
+ created_at: new Date().toISOString()
92
+ };
93
+
94
+ state.benchmarks.push(benchmark);
95
+ saveState(state, stateFile);
96
+
97
+ return { success: true, benchmark };
98
+ }
99
+
100
+ module.exports = {
101
+ evaluate, generateReport, configureBenchmark,
102
+ loadState, saveState, defaultState,
103
+ EVAL_DIMENSIONS
104
+ };
@@ -0,0 +1,152 @@
1
+ /**
2
+ * ai-intake.js — AI Use Case Intake Templates (Item 34)
3
+ *
4
+ * Standardize enterprise intake for business value, data sensitivity,
5
+ * model risk, and operating model.
6
+ *
7
+ * Usage:
8
+ * node bin/lib/ai-intake.js create|list|assess [options]
9
+ *
10
+ * State file: .jumpstart/state/ai-intake.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', 'ai-intake.json');
19
+
20
+ const INTAKE_SECTIONS = ['business-value', 'data-sensitivity', 'model-risk', 'operating-model', 'ethical-review', 'compliance-requirements'];
21
+
22
+ const RISK_TIERS = [
23
+ { tier: 1, label: 'Low Risk', description: 'No PII, internal tools, low business impact' },
24
+ { tier: 2, label: 'Medium Risk', description: 'Some PII, customer-facing, moderate impact' },
25
+ { tier: 3, label: 'High Risk', description: 'Sensitive data, critical decisions, high impact' },
26
+ { tier: 4, label: 'Critical Risk', description: 'Regulated domain, autonomous decisions, severe impact' }
27
+ ];
28
+
29
+ function defaultState() {
30
+ return {
31
+ version: '1.0.0',
32
+ created_at: new Date().toISOString(),
33
+ last_updated: null,
34
+ intakes: []
35
+ };
36
+ }
37
+
38
+ function loadState(stateFile) {
39
+ const filePath = stateFile || DEFAULT_STATE_FILE;
40
+ if (!fs.existsSync(filePath)) return defaultState();
41
+ try { return JSON.parse(fs.readFileSync(filePath, 'utf8')); }
42
+ catch { return defaultState(); }
43
+ }
44
+
45
+ function saveState(state, stateFile) {
46
+ const filePath = stateFile || DEFAULT_STATE_FILE;
47
+ const dir = path.dirname(filePath);
48
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
49
+ state.last_updated = new Date().toISOString();
50
+ fs.writeFileSync(filePath, JSON.stringify(state, null, 2) + '\n', 'utf8');
51
+ }
52
+
53
+ /**
54
+ * Create a new AI use case intake.
55
+ *
56
+ * @param {object} intake - { name, description, sponsor, business_value, data_types[], model_type? }
57
+ * @param {object} [options]
58
+ * @returns {object}
59
+ */
60
+ function createIntake(intake, options = {}) {
61
+ if (!intake || !intake.name || !intake.description) {
62
+ return { success: false, error: 'name and description are required' };
63
+ }
64
+
65
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
66
+ const state = loadState(stateFile);
67
+
68
+ // Auto-assess risk tier
69
+ const dataTypes = intake.data_types || [];
70
+ let riskTier = 1;
71
+ if (dataTypes.some(dt => ['PHI', 'PCI'].includes(dt))) riskTier = 4;
72
+ else if (dataTypes.some(dt => ['PII', 'credentials'].includes(dt))) riskTier = 3;
73
+ else if (dataTypes.some(dt => ['business-sensitive', 'internal'].includes(dt))) riskTier = 2;
74
+
75
+ const newIntake = {
76
+ id: `AI-${(state.intakes.length + 1).toString().padStart(3, '0')}`,
77
+ name: intake.name,
78
+ description: intake.description,
79
+ sponsor: intake.sponsor || null,
80
+ business_value: intake.business_value || '',
81
+ data_types: dataTypes,
82
+ model_type: intake.model_type || null,
83
+ risk_tier: riskTier,
84
+ risk_label: RISK_TIERS[riskTier - 1].label,
85
+ status: 'draft',
86
+ sections_completed: [],
87
+ created_at: new Date().toISOString()
88
+ };
89
+
90
+ state.intakes.push(newIntake);
91
+ saveState(state, stateFile);
92
+
93
+ return { success: true, intake: newIntake };
94
+ }
95
+
96
+ /**
97
+ * List intakes with optional filter.
98
+ *
99
+ * @param {object} [filter]
100
+ * @param {object} [options]
101
+ * @returns {object}
102
+ */
103
+ function listIntakes(filter = {}, options = {}) {
104
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
105
+ const state = loadState(stateFile);
106
+ let intakes = state.intakes;
107
+
108
+ if (filter.status) intakes = intakes.filter(i => i.status === filter.status);
109
+ if (filter.risk_tier) intakes = intakes.filter(i => i.risk_tier === filter.risk_tier);
110
+
111
+ return { success: true, intakes, total: intakes.length };
112
+ }
113
+
114
+ /**
115
+ * Assess intake completeness.
116
+ *
117
+ * @param {string} intakeId
118
+ * @param {object} [options]
119
+ * @returns {object}
120
+ */
121
+ function assessIntake(intakeId, options = {}) {
122
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
123
+ const state = loadState(stateFile);
124
+
125
+ const intake = state.intakes.find(i => i.id === intakeId);
126
+ if (!intake) return { success: false, error: `Intake not found: ${intakeId}` };
127
+
128
+ const completedSections = intake.sections_completed || [];
129
+ const missingSections = INTAKE_SECTIONS.filter(s => !completedSections.includes(s));
130
+ const completeness = Math.round((completedSections.length / INTAKE_SECTIONS.length) * 100);
131
+
132
+ return {
133
+ success: true,
134
+ intake_id: intakeId,
135
+ completeness,
136
+ completed_sections: completedSections,
137
+ missing_sections: missingSections,
138
+ risk_tier: intake.risk_tier,
139
+ ready_for_review: completeness >= 80
140
+ };
141
+ }
142
+
143
+ module.exports = {
144
+ defaultState,
145
+ loadState,
146
+ saveState,
147
+ createIntake,
148
+ listIntakes,
149
+ assessIntake,
150
+ INTAKE_SECTIONS,
151
+ RISK_TIERS
152
+ };
@@ -0,0 +1,152 @@
1
+ /**
2
+ * ambiguity-heatmap.js — Requirement Ambiguity Heatmap (Item 71)
3
+ *
4
+ * Highlight vague language, missing constraints, undefined terms,
5
+ * and assumption density.
6
+ *
7
+ * Usage:
8
+ * node bin/lib/ambiguity-heatmap.js scan|report [options]
9
+ */
10
+
11
+ 'use strict';
12
+
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+
16
+ const VAGUE_TERMS = [
17
+ 'should', 'could', 'might', 'possibly', 'maybe', 'approximately',
18
+ 'reasonable', 'appropriate', 'adequate', 'sufficient', 'as needed',
19
+ 'etc', 'and so on', 'as appropriate', 'in a timely manner',
20
+ 'user-friendly', 'intuitive', 'seamless', 'robust', 'scalable',
21
+ 'performant', 'efficient', 'flexible', 'simple', 'easy'
22
+ ];
23
+
24
+ const MISSING_CONSTRAINT_PATTERNS = [
25
+ { pattern: /\bfast\b/gi, suggestion: 'Define specific latency target (e.g., <200ms p95)' },
26
+ { pattern: /\bsecure\b/gi, suggestion: 'Specify security controls (encryption, auth, audit)' },
27
+ { pattern: /\bhigh availability\b/gi, suggestion: 'Define uptime SLA (e.g., 99.9%)' },
28
+ { pattern: /\blarge scale\b/gi, suggestion: 'Quantify expected load (users, requests/sec)' },
29
+ { pattern: /\breal[- ]?time\b/gi, suggestion: 'Define latency requirement (e.g., <1s, <100ms)' }
30
+ ];
31
+
32
+ /**
33
+ * Scan text for ambiguity indicators.
34
+ */
35
+ function scanAmbiguity(text, options = {}) {
36
+ if (!text) return { success: false, error: 'Text content is required' };
37
+
38
+ const lines = text.split('\n');
39
+ const findings = [];
40
+
41
+ for (let i = 0; i < lines.length; i++) {
42
+ const line = lines[i];
43
+ const lineNum = i + 1;
44
+
45
+ // Check vague terms
46
+ for (const term of VAGUE_TERMS) {
47
+ const regex = new RegExp(`\\b${term}\\b`, 'gi');
48
+ const matches = line.match(regex);
49
+ if (matches) {
50
+ findings.push({
51
+ type: 'vague_language',
52
+ term,
53
+ line: lineNum,
54
+ severity: 'medium',
55
+ context: line.trim().substring(0, 100)
56
+ });
57
+ }
58
+ }
59
+
60
+ // Check missing constraints
61
+ for (const { pattern, suggestion } of MISSING_CONSTRAINT_PATTERNS) {
62
+ if (pattern.test(line)) {
63
+ findings.push({
64
+ type: 'missing_constraint',
65
+ line: lineNum,
66
+ severity: 'high',
67
+ suggestion,
68
+ context: line.trim().substring(0, 100)
69
+ });
70
+ // Reset regex lastIndex
71
+ pattern.lastIndex = 0;
72
+ }
73
+ }
74
+ }
75
+
76
+ // Check for undefined terms (capitalized terms that may be domain concepts)
77
+ const definedTerms = new Set();
78
+ const usedTerms = new Set();
79
+ for (const line of lines) {
80
+ const defs = line.match(/^#+\s+(.+)/);
81
+ if (defs) definedTerms.add(defs[1].trim().toLowerCase());
82
+
83
+ const caps = line.match(/\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+)+\b/g);
84
+ if (caps) caps.forEach(t => usedTerms.add(t.toLowerCase()));
85
+ }
86
+
87
+ // Assumption density
88
+ const assumptions = (text.match(/\bassume[ds]?\b|\bassuming\b|\bassumption/gi) || []).length;
89
+
90
+ const totalLines = lines.filter(l => l.trim().length > 0).length;
91
+
92
+ return {
93
+ success: true,
94
+ total_findings: findings.length,
95
+ findings: findings.slice(0, options.limit || 50),
96
+ metrics: {
97
+ vague_terms: findings.filter(f => f.type === 'vague_language').length,
98
+ missing_constraints: findings.filter(f => f.type === 'missing_constraint').length,
99
+ assumption_count: assumptions,
100
+ ambiguity_density: totalLines > 0 ? Math.round((findings.length / totalLines) * 100) : 0
101
+ }
102
+ };
103
+ }
104
+
105
+ /**
106
+ * Scan a file for ambiguity.
107
+ */
108
+ function scanFile(filePath, options = {}) {
109
+ if (!fs.existsSync(filePath)) {
110
+ return { success: false, error: `File not found: ${filePath}` };
111
+ }
112
+
113
+ const content = fs.readFileSync(filePath, 'utf8');
114
+ const result = scanAmbiguity(content, options);
115
+ result.file = filePath;
116
+ return result;
117
+ }
118
+
119
+ /**
120
+ * Generate ambiguity heatmap report.
121
+ */
122
+ function generateHeatmap(root, options = {}) {
123
+ const specsDir = path.join(root, 'specs');
124
+ const results = [];
125
+
126
+ if (fs.existsSync(specsDir)) {
127
+ for (const f of fs.readdirSync(specsDir).filter(f => f.endsWith('.md'))) {
128
+ const fp = path.join(specsDir, f);
129
+ const result = scanFile(fp, options);
130
+ if (result.success) {
131
+ results.push({ file: f, ...result.metrics, total_findings: result.total_findings });
132
+ }
133
+ }
134
+ }
135
+
136
+ results.sort((a, b) => b.ambiguity_density - a.ambiguity_density);
137
+
138
+ return {
139
+ success: true,
140
+ files_scanned: results.length,
141
+ results,
142
+ overall: {
143
+ total_findings: results.reduce((s, r) => s + r.total_findings, 0),
144
+ highest_density_file: results.length > 0 ? results[0].file : null
145
+ }
146
+ };
147
+ }
148
+
149
+ module.exports = {
150
+ scanAmbiguity, scanFile, generateHeatmap,
151
+ VAGUE_TERMS, MISSING_CONSTRAINT_PATTERNS
152
+ };