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,124 @@
1
+ /**
2
+ * contract-first.js — Contract-First Implementation Assistant (Item 45)
3
+ *
4
+ * Generate code from API and event contracts, then verify code
5
+ * remains compliant.
6
+ *
7
+ * Usage:
8
+ * node bin/lib/contract-first.js extract|verify|report [options]
9
+ */
10
+
11
+ 'use strict';
12
+
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+
16
+ const CONTRACT_TYPES = ['rest-api', 'graphql', 'event', 'grpc', 'message-queue'];
17
+
18
+ /**
19
+ * Extract API contracts from architecture spec.
20
+ *
21
+ * @param {string} root - Project root.
22
+ * @param {object} [options]
23
+ * @returns {object}
24
+ */
25
+ function extractContracts(root, options = {}) {
26
+ const archFile = path.join(root, 'specs', 'architecture.md');
27
+ if (!fs.existsSync(archFile)) {
28
+ return { success: false, error: 'Architecture spec not found at specs/architecture.md' };
29
+ }
30
+
31
+ const content = fs.readFileSync(archFile, 'utf8');
32
+ const contracts = [];
33
+
34
+ // Extract API endpoint definitions
35
+ const endpointPattern = /(?:GET|POST|PUT|PATCH|DELETE)\s+\/[\w/{}:-]+/g;
36
+ let match;
37
+ while ((match = endpointPattern.exec(content)) !== null) {
38
+ const [method, ...pathParts] = match[0].split(/\s+/);
39
+ contracts.push({
40
+ type: 'rest-api',
41
+ method,
42
+ path: pathParts.join(' '),
43
+ line: content.substring(0, match.index).split('\n').length
44
+ });
45
+ }
46
+
47
+ // Extract event definitions
48
+ const eventPattern = /(?:event|topic|queue)[\s:]+["']?([a-zA-Z0-9._-]+)/gi;
49
+ while ((match = eventPattern.exec(content)) !== null) {
50
+ contracts.push({
51
+ type: 'event',
52
+ name: match[1],
53
+ line: content.substring(0, match.index).split('\n').length
54
+ });
55
+ }
56
+
57
+ return {
58
+ success: true,
59
+ total_contracts: contracts.length,
60
+ contracts,
61
+ by_type: contracts.reduce((acc, c) => { acc[c.type] = (acc[c.type] || 0) + 1; return acc; }, {})
62
+ };
63
+ }
64
+
65
+ /**
66
+ * Verify that implementation matches contracts.
67
+ *
68
+ * @param {string} root - Project root.
69
+ * @param {object} [options]
70
+ * @returns {object}
71
+ */
72
+ function verifyCompliance(root, options = {}) {
73
+ const contractResult = extractContracts(root, options);
74
+ if (!contractResult.success) return contractResult;
75
+
76
+ const srcDir = path.join(root, 'src');
77
+ let srcContent = '';
78
+
79
+ if (fs.existsSync(srcDir)) {
80
+ function readDir(dir) {
81
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
82
+ if (entry.isDirectory() && entry.name !== 'node_modules') {
83
+ readDir(path.join(dir, entry.name));
84
+ } else if (entry.isFile() && /\.(js|ts|py)$/.test(entry.name)) {
85
+ try { srcContent += fs.readFileSync(path.join(dir, entry.name), 'utf8') + '\n'; }
86
+ catch { /* skip */ }
87
+ }
88
+ }
89
+ }
90
+ readDir(srcDir);
91
+ }
92
+
93
+ const violations = [];
94
+ const implemented = [];
95
+
96
+ for (const contract of contractResult.contracts) {
97
+ if (contract.type === 'rest-api') {
98
+ const pathPattern = contract.path.replace(/\{[^}]+\}/g, '[^/]+');
99
+ const found = new RegExp(pathPattern).test(srcContent) || srcContent.includes(contract.path);
100
+ if (found) implemented.push(contract);
101
+ else violations.push({ ...contract, issue: 'Endpoint not found in source' });
102
+ } else if (contract.type === 'event') {
103
+ const found = srcContent.includes(contract.name);
104
+ if (found) implemented.push(contract);
105
+ else violations.push({ ...contract, issue: 'Event handler not found in source' });
106
+ }
107
+ }
108
+
109
+ return {
110
+ success: true,
111
+ total_contracts: contractResult.total_contracts,
112
+ implemented: implemented.length,
113
+ violations: violations.length,
114
+ compliance: contractResult.total_contracts > 0
115
+ ? Math.round((implemented.length / contractResult.total_contracts) * 100) : 100,
116
+ findings: violations
117
+ };
118
+ }
119
+
120
+ module.exports = {
121
+ extractContracts,
122
+ verifyCompliance,
123
+ CONTRACT_TYPES
124
+ };
@@ -0,0 +1,148 @@
1
+ /**
2
+ * cost-router.js — Cost-Aware Model Routing (Item 55)
3
+ *
4
+ * Optimize quality and speed against budget targets.
5
+ *
6
+ * Usage:
7
+ * node bin/lib/cost-router.js route|budget|report [options]
8
+ *
9
+ * Config: .jumpstart/cost-routing.json
10
+ */
11
+
12
+ 'use strict';
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+
17
+ const DEFAULT_CONFIG_FILE = path.join('.jumpstart', 'cost-routing.json');
18
+
19
+ const MODEL_COSTS = {
20
+ 'gpt-4o': { input_per_1k: 0.005, output_per_1k: 0.015, quality: 90, speed: 80 },
21
+ 'gpt-4-turbo': { input_per_1k: 0.01, output_per_1k: 0.03, quality: 92, speed: 70 },
22
+ 'gpt-3.5-turbo': { input_per_1k: 0.0005, output_per_1k: 0.0015, quality: 70, speed: 95 },
23
+ 'claude-3-opus': { input_per_1k: 0.015, output_per_1k: 0.075, quality: 95, speed: 60 },
24
+ 'claude-3-sonnet': { input_per_1k: 0.003, output_per_1k: 0.015, quality: 88, speed: 85 },
25
+ 'claude-3-haiku': { input_per_1k: 0.00025, output_per_1k: 0.00125, quality: 75, speed: 95 }
26
+ };
27
+
28
+ const BUDGET_PROFILES = {
29
+ economy: { max_per_task: 0.10, prefer: 'cheapest', min_quality: 65 },
30
+ balanced: { max_per_task: 0.50, prefer: 'balanced', min_quality: 80 },
31
+ premium: { max_per_task: 2.00, prefer: 'best-quality', min_quality: 90 }
32
+ };
33
+
34
+ function loadConfig(configFile) {
35
+ const filePath = configFile || DEFAULT_CONFIG_FILE;
36
+ if (!fs.existsSync(filePath)) return { budget_profile: 'balanced', spending: [] };
37
+ try { return JSON.parse(fs.readFileSync(filePath, 'utf8')); }
38
+ catch { return { budget_profile: 'balanced', spending: [] }; }
39
+ }
40
+
41
+ function saveConfig(config, configFile) {
42
+ const filePath = configFile || DEFAULT_CONFIG_FILE;
43
+ const dir = path.dirname(filePath);
44
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
45
+ fs.writeFileSync(filePath, JSON.stringify(config, null, 2) + '\n', 'utf8');
46
+ }
47
+
48
+ /**
49
+ * Route to the most cost-effective model.
50
+ *
51
+ * @param {object} task - { type, estimated_tokens?, min_quality? }
52
+ * @param {object} [options]
53
+ * @returns {object}
54
+ */
55
+ function routeByCost(task, options = {}) {
56
+ const configFile = options.configFile || DEFAULT_CONFIG_FILE;
57
+ const config = loadConfig(configFile);
58
+ const profile = BUDGET_PROFILES[config.budget_profile] || BUDGET_PROFILES.balanced;
59
+ const minQuality = task.min_quality || profile.min_quality;
60
+
61
+ const candidates = Object.entries(MODEL_COSTS)
62
+ .filter(([, costs]) => costs.quality >= minQuality)
63
+ .map(([model, costs]) => {
64
+ const tokens = task.estimated_tokens || 1000;
65
+ const cost = (tokens / 1000) * costs.input_per_1k + (tokens / 1000) * costs.output_per_1k;
66
+ return { model, cost: Math.round(cost * 10000) / 10000, quality: costs.quality, speed: costs.speed };
67
+ })
68
+ .sort((a, b) => {
69
+ if (profile.prefer === 'cheapest') return a.cost - b.cost;
70
+ if (profile.prefer === 'best-quality') return b.quality - a.quality;
71
+ return (b.quality + b.speed) / 2 - (a.quality + a.speed) / 2; // balanced
72
+ });
73
+
74
+ const selected = candidates[0];
75
+
76
+ return {
77
+ success: true,
78
+ selected_model: selected ? selected.model : null,
79
+ estimated_cost: selected ? selected.cost : 0,
80
+ quality: selected ? selected.quality : 0,
81
+ budget_profile: config.budget_profile,
82
+ alternatives: candidates.slice(1, 3)
83
+ };
84
+ }
85
+
86
+ /**
87
+ * Record spending.
88
+ *
89
+ * @param {string} model
90
+ * @param {number} tokens
91
+ * @param {object} [options]
92
+ * @returns {object}
93
+ */
94
+ function recordSpending(model, tokens, options = {}) {
95
+ const configFile = options.configFile || DEFAULT_CONFIG_FILE;
96
+ const config = loadConfig(configFile);
97
+ const costs = MODEL_COSTS[model];
98
+
99
+ if (!costs) return { success: false, error: `Unknown model: ${model}` };
100
+
101
+ const cost = (tokens / 1000) * (costs.input_per_1k + costs.output_per_1k);
102
+
103
+ if (!config.spending) config.spending = [];
104
+ config.spending.push({
105
+ model,
106
+ tokens,
107
+ cost: Math.round(cost * 10000) / 10000,
108
+ recorded_at: new Date().toISOString()
109
+ });
110
+
111
+ saveConfig(config, configFile);
112
+
113
+ return { success: true, model, tokens, cost: Math.round(cost * 10000) / 10000 };
114
+ }
115
+
116
+ /**
117
+ * Generate cost report.
118
+ *
119
+ * @param {object} [options]
120
+ * @returns {object}
121
+ */
122
+ function generateReport(options = {}) {
123
+ const configFile = options.configFile || DEFAULT_CONFIG_FILE;
124
+ const config = loadConfig(configFile);
125
+ const spending = config.spending || [];
126
+
127
+ const totalCost = spending.reduce((sum, s) => sum + s.cost, 0);
128
+ const byModel = spending.reduce((acc, s) => { acc[s.model] = (acc[s.model] || 0) + s.cost; return acc; }, {});
129
+
130
+ return {
131
+ success: true,
132
+ budget_profile: config.budget_profile,
133
+ total_cost: Math.round(totalCost * 100) / 100,
134
+ total_requests: spending.length,
135
+ by_model: byModel,
136
+ recent: spending.slice(-10)
137
+ };
138
+ }
139
+
140
+ module.exports = {
141
+ loadConfig,
142
+ saveConfig,
143
+ routeByCost,
144
+ recordSpending,
145
+ generateReport,
146
+ MODEL_COSTS,
147
+ BUDGET_PROFILES
148
+ };
@@ -0,0 +1,155 @@
1
+ /**
2
+ * credential-boundary.js — Secrets & Credential Boundary Checks (Item 31)
3
+ *
4
+ * Detect unsafe plans or code paths involving tokens, keys,
5
+ * vault usage, and secrets sprawl.
6
+ *
7
+ * Usage:
8
+ * node bin/lib/credential-boundary.js scan|report [options]
9
+ */
10
+
11
+ 'use strict';
12
+
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+
16
+ const BOUNDARY_PATTERNS = [
17
+ { name: 'Hardcoded secret in spec', pattern: /(?:password|secret|token|api.?key)\s*[:=]\s*["'][^"']{8,}/gi, severity: 'critical' },
18
+ { name: 'Vault reference missing', pattern: /(?:password|secret|token)\s*[:=]\s*(?!.*vault|.*\$\{)/gi, severity: 'warning' },
19
+ { name: 'Inline connection string', pattern: /(?:mongodb|postgres|mysql|redis):\/\/[^\s"']+:[^\s"']+@/gi, severity: 'critical' },
20
+ { name: 'Private key material', pattern: /-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/g, severity: 'critical' },
21
+ { name: 'AWS credential pattern', pattern: /(?:AKIA[0-9A-Z]{16}|aws_secret_access_key)/gi, severity: 'critical' },
22
+ { name: 'Bearer token in spec', pattern: /[Bb]earer\s+[A-Za-z0-9._~+/=-]{20,}/g, severity: 'high' },
23
+ { name: 'Secret in environment variable', pattern: /(?:export\s+)?[A-Z_]*(?:SECRET|TOKEN|PASSWORD|KEY)[A-Z_]*\s*=\s*["']?[A-Za-z0-9+/=]{16,}/g, severity: 'high' }
24
+ ];
25
+
26
+ const SAFE_PATTERNS = [
27
+ /\.env\.example/i,
28
+ /placeholder|changeme|your[_-]?key|replace[_-]?me|TODO/i,
29
+ /\$\{[^}]+\}/,
30
+ /vault:\/\//i,
31
+ /secretsmanager/i,
32
+ /keyvault/i
33
+ ];
34
+
35
+ /**
36
+ * Scan files for credential boundary violations.
37
+ *
38
+ * @param {string[]} files - File paths to scan.
39
+ * @param {string} root - Project root.
40
+ * @param {object} [options]
41
+ * @returns {object}
42
+ */
43
+ function scanBoundaries(files, root, options = {}) {
44
+ const findings = [];
45
+ let filesScanned = 0;
46
+
47
+ for (const file of files) {
48
+ const absPath = path.isAbsolute(file) ? file : path.join(root, file);
49
+ if (!fs.existsSync(absPath)) continue;
50
+
51
+ try {
52
+ const content = fs.readFileSync(absPath, 'utf8');
53
+ filesScanned++;
54
+
55
+ for (const bp of BOUNDARY_PATTERNS) {
56
+ const regex = new RegExp(bp.pattern.source, bp.pattern.flags);
57
+ let match;
58
+ while ((match = regex.exec(content)) !== null) {
59
+ const matchedText = match[0];
60
+
61
+ // Check if it matches safe patterns
62
+ const isSafe = SAFE_PATTERNS.some(sp => sp.test(matchedText));
63
+ if (isSafe) continue;
64
+
65
+ const lineNum = content.substring(0, match.index).split('\n').length;
66
+
67
+ findings.push({
68
+ file: path.relative(root, absPath).replace(/\\/g, '/'),
69
+ line: lineNum,
70
+ pattern: bp.name,
71
+ severity: bp.severity,
72
+ matched: matchedText.substring(0, 50) + (matchedText.length > 50 ? '...' : '')
73
+ });
74
+ }
75
+ }
76
+ } catch { /* skip unreadable */ }
77
+ }
78
+
79
+ return {
80
+ success: true,
81
+ files_scanned: filesScanned,
82
+ findings,
83
+ total_findings: findings.length,
84
+ critical: findings.filter(f => f.severity === 'critical').length,
85
+ high: findings.filter(f => f.severity === 'high').length,
86
+ pass: findings.filter(f => f.severity === 'critical').length === 0
87
+ };
88
+ }
89
+
90
+ /**
91
+ * Scan a project root for credential boundary issues.
92
+ *
93
+ * @param {string} root - Project root.
94
+ * @param {object} [options]
95
+ * @returns {object}
96
+ */
97
+ function scanProject(root, options = {}) {
98
+ const extensions = options.extensions || ['.md', '.yaml', '.yml', '.json', '.js', '.ts', '.env', '.cfg', '.conf'];
99
+ const excludeDirs = options.excludeDirs || ['node_modules', '.git', 'dist', 'build', 'vendor'];
100
+ const files = [];
101
+
102
+ function walk(dir) {
103
+ if (!fs.existsSync(dir)) return;
104
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
105
+ if (entry.isDirectory()) {
106
+ if (!excludeDirs.includes(entry.name)) walk(path.join(dir, entry.name));
107
+ } else if (entry.isFile()) {
108
+ const ext = path.extname(entry.name).toLowerCase();
109
+ if (extensions.includes(ext)) files.push(path.join(dir, entry.name));
110
+ }
111
+ }
112
+ }
113
+
114
+ walk(root);
115
+ return scanBoundaries(files, root, options);
116
+ }
117
+
118
+ /**
119
+ * Generate a credential boundary report.
120
+ *
121
+ * @param {object} scanResult
122
+ * @returns {object}
123
+ */
124
+ function generateReport(scanResult) {
125
+ const bySeverity = {};
126
+ const byPattern = {};
127
+
128
+ for (const f of scanResult.findings) {
129
+ bySeverity[f.severity] = (bySeverity[f.severity] || 0) + 1;
130
+ byPattern[f.pattern] = (byPattern[f.pattern] || 0) + 1;
131
+ }
132
+
133
+ return {
134
+ success: true,
135
+ summary: {
136
+ files_scanned: scanResult.files_scanned,
137
+ total_findings: scanResult.total_findings,
138
+ pass: scanResult.pass
139
+ },
140
+ by_severity: bySeverity,
141
+ by_pattern: byPattern,
142
+ critical_findings: scanResult.findings.filter(f => f.severity === 'critical'),
143
+ recommendations: scanResult.total_findings > 0
144
+ ? ['Use vault references instead of hardcoded secrets', 'Move sensitive values to environment variables', 'Add .env to .gitignore']
145
+ : ['No credential boundary issues detected']
146
+ };
147
+ }
148
+
149
+ module.exports = {
150
+ scanBoundaries,
151
+ scanProject,
152
+ generateReport,
153
+ BOUNDARY_PATTERNS,
154
+ SAFE_PATTERNS
155
+ };
@@ -0,0 +1,180 @@
1
+ /**
2
+ * data-classification.js — Data Classification & Handling Controls (Item 30)
3
+ *
4
+ * Tag systems and features by public/internal/confidential/restricted
5
+ * and adapt prompts and policies.
6
+ *
7
+ * Usage:
8
+ * node bin/lib/data-classification.js classify|check|report [options]
9
+ *
10
+ * State file: .jumpstart/state/data-classification.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', 'data-classification.json');
19
+
20
+ const CLASSIFICATION_LEVELS = ['public', 'internal', 'confidential', 'restricted'];
21
+
22
+ const HANDLING_REQUIREMENTS = {
23
+ public: { encryption_at_rest: false, encryption_in_transit: false, access_logging: false, retention_policy: false },
24
+ internal: { encryption_at_rest: false, encryption_in_transit: true, access_logging: false, retention_policy: true },
25
+ confidential: { encryption_at_rest: true, encryption_in_transit: true, access_logging: true, retention_policy: true },
26
+ restricted: { encryption_at_rest: true, encryption_in_transit: true, access_logging: true, retention_policy: true, mfa_required: true, data_masking: true }
27
+ };
28
+
29
+ const DATA_TYPE_DEFAULTS = {
30
+ PII: 'confidential',
31
+ PHI: 'restricted',
32
+ PCI: 'restricted',
33
+ credentials: 'restricted',
34
+ 'business-sensitive': 'confidential',
35
+ 'public-content': 'public',
36
+ 'internal-docs': 'internal'
37
+ };
38
+
39
+ function defaultState() {
40
+ return {
41
+ version: '1.0.0',
42
+ created_at: new Date().toISOString(),
43
+ last_updated: null,
44
+ classifications: [],
45
+ data_assets: []
46
+ };
47
+ }
48
+
49
+ function loadState(stateFile) {
50
+ const filePath = stateFile || DEFAULT_STATE_FILE;
51
+ if (!fs.existsSync(filePath)) return defaultState();
52
+ try { return JSON.parse(fs.readFileSync(filePath, 'utf8')); }
53
+ catch { return defaultState(); }
54
+ }
55
+
56
+ function saveState(state, stateFile) {
57
+ const filePath = stateFile || DEFAULT_STATE_FILE;
58
+ const dir = path.dirname(filePath);
59
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
60
+ state.last_updated = new Date().toISOString();
61
+ fs.writeFileSync(filePath, JSON.stringify(state, null, 2) + '\n', 'utf8');
62
+ }
63
+
64
+ /**
65
+ * Classify a system or feature.
66
+ *
67
+ * @param {object} asset - { name, type, data_types[], description? }
68
+ * @param {object} [options]
69
+ * @returns {object}
70
+ */
71
+ function classifyAsset(asset, options = {}) {
72
+ if (!asset || !asset.name) {
73
+ return { success: false, error: 'asset.name is required' };
74
+ }
75
+
76
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
77
+ const state = loadState(stateFile);
78
+
79
+ // Auto-classify based on data types
80
+ let level = 'public';
81
+ const dataTypes = asset.data_types || [];
82
+ for (const dt of dataTypes) {
83
+ const defaultLevel = DATA_TYPE_DEFAULTS[dt];
84
+ if (defaultLevel) {
85
+ const idx = CLASSIFICATION_LEVELS.indexOf(defaultLevel);
86
+ const currentIdx = CLASSIFICATION_LEVELS.indexOf(level);
87
+ if (idx > currentIdx) level = defaultLevel;
88
+ }
89
+ }
90
+
91
+ // Allow override
92
+ if (asset.classification && CLASSIFICATION_LEVELS.includes(asset.classification)) {
93
+ level = asset.classification;
94
+ }
95
+
96
+ const classification = {
97
+ id: `DC-${(state.data_assets.length + 1).toString().padStart(3, '0')}`,
98
+ name: asset.name,
99
+ type: asset.type || 'system',
100
+ data_types: dataTypes,
101
+ classification: level,
102
+ handling: HANDLING_REQUIREMENTS[level],
103
+ description: asset.description || '',
104
+ classified_at: new Date().toISOString()
105
+ };
106
+
107
+ state.data_assets.push(classification);
108
+ saveState(state, stateFile);
109
+
110
+ return { success: true, asset: classification };
111
+ }
112
+
113
+ /**
114
+ * Check classification compliance.
115
+ *
116
+ * @param {object} [options]
117
+ * @returns {object}
118
+ */
119
+ function checkCompliance(options = {}) {
120
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
121
+ const state = loadState(stateFile);
122
+
123
+ const violations = [];
124
+ for (const asset of state.data_assets) {
125
+ const requirements = HANDLING_REQUIREMENTS[asset.classification];
126
+ if (!requirements) continue;
127
+
128
+ if (requirements.encryption_at_rest && !asset.encryption_at_rest_verified) {
129
+ violations.push({ asset: asset.name, requirement: 'encryption_at_rest', classification: asset.classification });
130
+ }
131
+ if (requirements.encryption_in_transit && !asset.encryption_in_transit_verified) {
132
+ violations.push({ asset: asset.name, requirement: 'encryption_in_transit', classification: asset.classification });
133
+ }
134
+ }
135
+
136
+ return {
137
+ success: true,
138
+ total_assets: state.data_assets.length,
139
+ violations: violations.length,
140
+ compliant: violations.length === 0,
141
+ findings: violations
142
+ };
143
+ }
144
+
145
+ /**
146
+ * Generate classification report.
147
+ *
148
+ * @param {object} [options]
149
+ * @returns {object}
150
+ */
151
+ function generateReport(options = {}) {
152
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
153
+ const state = loadState(stateFile);
154
+
155
+ const byLevel = {};
156
+ for (const level of CLASSIFICATION_LEVELS) {
157
+ byLevel[level] = state.data_assets.filter(a => a.classification === level).length;
158
+ }
159
+
160
+ return {
161
+ success: true,
162
+ total_assets: state.data_assets.length,
163
+ by_level: byLevel,
164
+ restricted_assets: state.data_assets.filter(a => a.classification === 'restricted'),
165
+ confidential_assets: state.data_assets.filter(a => a.classification === 'confidential'),
166
+ assets: state.data_assets
167
+ };
168
+ }
169
+
170
+ module.exports = {
171
+ defaultState,
172
+ loadState,
173
+ saveState,
174
+ classifyAsset,
175
+ checkCompliance,
176
+ generateReport,
177
+ CLASSIFICATION_LEVELS,
178
+ HANDLING_REQUIREMENTS,
179
+ DATA_TYPE_DEFAULTS
180
+ };