jumpstart-mode 1.1.12 → 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 (146) hide show
  1. package/.github/agents/jumpstart-adversary.agent.md +2 -1
  2. package/.github/agents/jumpstart-architect.agent.md +5 -6
  3. package/.github/agents/jumpstart-challenger.agent.md +2 -1
  4. package/.github/agents/jumpstart-devops.agent.md +2 -2
  5. package/.github/agents/jumpstart-diagram-verifier.agent.md +2 -1
  6. package/.github/agents/jumpstart-maintenance.agent.md +1 -0
  7. package/.github/agents/jumpstart-performance.agent.md +1 -0
  8. package/.github/agents/jumpstart-pm.agent.md +1 -1
  9. package/.github/agents/jumpstart-refactor.agent.md +1 -0
  10. package/.github/agents/jumpstart-requirements-extractor.agent.md +1 -0
  11. package/.github/agents/jumpstart-researcher.agent.md +1 -0
  12. package/.github/agents/jumpstart-retrospective.agent.md +1 -0
  13. package/.github/agents/jumpstart-reviewer.agent.md +2 -0
  14. package/.github/agents/jumpstart-scout.agent.md +1 -1
  15. package/.github/agents/jumpstart-scrum-master.agent.md +1 -0
  16. package/.github/agents/jumpstart-security.agent.md +2 -1
  17. package/.github/agents/jumpstart-tech-writer.agent.md +1 -0
  18. package/.github/workflows/quality.yml +19 -2
  19. package/.jumpstart/agents/analyst.md +38 -0
  20. package/.jumpstart/agents/architect.md +38 -0
  21. package/.jumpstart/agents/challenger.md +38 -0
  22. package/.jumpstart/agents/developer.md +41 -0
  23. package/.jumpstart/agents/pm.md +38 -0
  24. package/.jumpstart/agents/scout.md +33 -0
  25. package/.jumpstart/agents/ux-designer.md +4 -0
  26. package/.jumpstart/config.yaml +24 -0
  27. package/.jumpstart/schemas/timeline.schema.json +1 -0
  28. package/.jumpstart/skills/skill-creator/SKILL.md +485 -357
  29. package/.jumpstart/skills/skill-creator/agents/analyzer.md +274 -0
  30. package/.jumpstart/skills/skill-creator/agents/comparator.md +202 -0
  31. package/.jumpstart/skills/skill-creator/agents/grader.md +223 -0
  32. package/.jumpstart/skills/skill-creator/assets/eval_review.html +146 -0
  33. package/.jumpstart/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  34. package/.jumpstart/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  35. package/.jumpstart/skills/skill-creator/references/schemas.md +430 -0
  36. package/.jumpstart/skills/skill-creator/scripts/__init__.py +0 -0
  37. package/.jumpstart/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  38. package/.jumpstart/skills/skill-creator/scripts/generate_report.py +326 -0
  39. package/.jumpstart/skills/skill-creator/scripts/improve_description.py +247 -0
  40. package/.jumpstart/skills/skill-creator/scripts/package_skill.py +136 -110
  41. package/.jumpstart/skills/skill-creator/scripts/run_eval.py +310 -0
  42. package/.jumpstart/skills/skill-creator/scripts/run_loop.py +328 -0
  43. package/.jumpstart/skills/skill-creator/scripts/utils.py +47 -0
  44. package/.jumpstart/state/timeline.json +659 -0
  45. package/.jumpstart/usage-log.json +74 -3
  46. package/README.md +62 -1
  47. package/bin/cli.js +3217 -1
  48. package/bin/headless-runner.js +62 -2
  49. package/bin/lib/agent-checkpoint.js +168 -0
  50. package/bin/lib/ai-evaluation.js +104 -0
  51. package/bin/lib/ai-intake.js +152 -0
  52. package/bin/lib/ambiguity-heatmap.js +152 -0
  53. package/bin/lib/artifact-comparison.js +104 -0
  54. package/bin/lib/ast-edit-engine.js +157 -0
  55. package/bin/lib/backlog-sync.js +338 -0
  56. package/bin/lib/bcdr-planning.js +158 -0
  57. package/bin/lib/bidirectional-trace.js +199 -0
  58. package/bin/lib/branch-workflow.js +266 -0
  59. package/bin/lib/cab-output.js +119 -0
  60. package/bin/lib/chat-integration.js +122 -0
  61. package/bin/lib/ci-cd-integration.js +208 -0
  62. package/bin/lib/codebase-retrieval.js +125 -0
  63. package/bin/lib/collaboration.js +168 -0
  64. package/bin/lib/compliance-packs.js +213 -0
  65. package/bin/lib/context-chunker.js +128 -0
  66. package/bin/lib/context-onboarding.js +122 -0
  67. package/bin/lib/contract-first.js +124 -0
  68. package/bin/lib/cost-router.js +148 -0
  69. package/bin/lib/credential-boundary.js +155 -0
  70. package/bin/lib/data-classification.js +180 -0
  71. package/bin/lib/data-contracts.js +129 -0
  72. package/bin/lib/db-evolution.js +158 -0
  73. package/bin/lib/decision-conflicts.js +299 -0
  74. package/bin/lib/delivery-confidence.js +361 -0
  75. package/bin/lib/dependency-upgrade.js +153 -0
  76. package/bin/lib/design-system.js +133 -0
  77. package/bin/lib/deterministic-artifacts.js +151 -0
  78. package/bin/lib/diagram-studio.js +115 -0
  79. package/bin/lib/domain-ontology.js +140 -0
  80. package/bin/lib/ea-review-packet.js +151 -0
  81. package/bin/lib/enterprise-search.js +123 -0
  82. package/bin/lib/enterprise-templates.js +140 -0
  83. package/bin/lib/environment-promotion.js +220 -0
  84. package/bin/lib/estimation-studio.js +130 -0
  85. package/bin/lib/event-modeling.js +133 -0
  86. package/bin/lib/evidence-collector.js +179 -0
  87. package/bin/lib/finops-planner.js +182 -0
  88. package/bin/lib/fitness-functions.js +279 -0
  89. package/bin/lib/focus.js +448 -0
  90. package/bin/lib/governance-dashboard.js +165 -0
  91. package/bin/lib/guided-handoff.js +120 -0
  92. package/bin/lib/impact-analysis.js +190 -0
  93. package/bin/lib/incident-feedback.js +157 -0
  94. package/bin/lib/integrate.js +1 -1
  95. package/bin/lib/knowledge-graph.js +122 -0
  96. package/bin/lib/legacy-modernizer.js +160 -0
  97. package/bin/lib/migration-planner.js +144 -0
  98. package/bin/lib/model-governance.js +185 -0
  99. package/bin/lib/model-router.js +144 -0
  100. package/bin/lib/multi-repo.js +272 -0
  101. package/bin/lib/next-phase.js +53 -8
  102. package/bin/lib/ops-ownership.js +152 -0
  103. package/bin/lib/parallel-agents.js +257 -0
  104. package/bin/lib/pattern-library.js +115 -0
  105. package/bin/lib/persona-packs.js +99 -0
  106. package/bin/lib/plan-executor.js +366 -0
  107. package/bin/lib/platform-engineering.js +119 -0
  108. package/bin/lib/playback-summaries.js +126 -0
  109. package/bin/lib/policy-engine.js +240 -0
  110. package/bin/lib/portfolio-reporting.js +357 -0
  111. package/bin/lib/pr-package.js +197 -0
  112. package/bin/lib/project-memory.js +235 -0
  113. package/bin/lib/prompt-governance.js +130 -0
  114. package/bin/lib/promptless-mode.js +128 -0
  115. package/bin/lib/quality-graph.js +193 -0
  116. package/bin/lib/raci-matrix.js +188 -0
  117. package/bin/lib/refactor-planner.js +167 -0
  118. package/bin/lib/reference-architectures.js +304 -0
  119. package/bin/lib/release-readiness.js +171 -0
  120. package/bin/lib/repo-graph.js +262 -0
  121. package/bin/lib/requirements-baseline.js +358 -0
  122. package/bin/lib/risk-register.js +211 -0
  123. package/bin/lib/role-approval.js +249 -0
  124. package/bin/lib/role-views.js +142 -0
  125. package/bin/lib/root-cause-analysis.js +132 -0
  126. package/bin/lib/runtime-debugger.js +154 -0
  127. package/bin/lib/safe-rename.js +135 -0
  128. package/bin/lib/semantic-diff.js +335 -0
  129. package/bin/lib/sla-slo.js +210 -0
  130. package/bin/lib/spec-comments.js +147 -0
  131. package/bin/lib/spec-maturity.js +287 -0
  132. package/bin/lib/sre-integration.js +154 -0
  133. package/bin/lib/structured-elicitation.js +174 -0
  134. package/bin/lib/telemetry-feedback.js +118 -0
  135. package/bin/lib/test-generator.js +146 -0
  136. package/bin/lib/timeline.js +2 -1
  137. package/bin/lib/tool-bridge.js +107 -0
  138. package/bin/lib/tool-guardrails.js +139 -0
  139. package/bin/lib/tool-schemas.js +172 -3
  140. package/bin/lib/transcript-ingestion.js +150 -0
  141. package/bin/lib/vendor-risk.js +173 -0
  142. package/bin/lib/waiver-workflow.js +174 -0
  143. package/bin/lib/web-dashboard.js +126 -0
  144. package/bin/lib/workshop-mode.js +165 -0
  145. package/bin/lib/workstream-ownership.js +104 -0
  146. package/package.json +1 -1
@@ -0,0 +1,304 @@
1
+ /**
2
+ * reference-architectures.js — Org-wide Reusable Reference Architectures
3
+ *
4
+ * Let teams instantiate approved patterns for RAG, agent apps,
5
+ * API platforms, event-driven systems, etc.
6
+ *
7
+ * Registry: .jumpstart/reference-architectures.json
8
+ *
9
+ * Usage:
10
+ * node bin/lib/reference-architectures.js list|get|register|instantiate [options]
11
+ */
12
+
13
+ 'use strict';
14
+
15
+ const fs = require('fs');
16
+ const path = require('path');
17
+
18
+ const DEFAULT_REGISTRY_FILE = path.join('.jumpstart', 'reference-architectures.json');
19
+
20
+ const PATTERN_CATEGORIES = ['api-platform', 'event-driven', 'rag', 'agent-app', 'microservices', 'monolith', 'serverless', 'data-pipeline', 'other'];
21
+
22
+ /**
23
+ * Built-in reference architecture templates.
24
+ */
25
+ const BUILTIN_PATTERNS = [
26
+ {
27
+ id: 'rag-pipeline',
28
+ name: 'RAG Pipeline',
29
+ category: 'rag',
30
+ description: 'Retrieval-Augmented Generation pipeline with vector store, embeddings, and LLM orchestration.',
31
+ components: ['document-ingestion', 'embedding-service', 'vector-store', 'retrieval-engine', 'llm-orchestrator', 'api-gateway'],
32
+ tech_stack: { suggested: ['langchain', 'pinecone', 'openai', 'fastapi'] },
33
+ structure: {
34
+ 'src/ingestion/': 'Document ingestion and chunking',
35
+ 'src/embeddings/': 'Embedding generation',
36
+ 'src/retrieval/': 'Vector search and retrieval',
37
+ 'src/orchestrator/': 'LLM orchestration and prompt management',
38
+ 'src/api/': 'API endpoints',
39
+ 'tests/': 'Test suites'
40
+ },
41
+ nfrs: ['latency < 2s p95', 'embedding refresh < 1h', 'context window management']
42
+ },
43
+ {
44
+ id: 'agent-app',
45
+ name: 'Agent Application',
46
+ category: 'agent-app',
47
+ description: 'Multi-agent application with tool use, memory, and planning capabilities.',
48
+ components: ['agent-core', 'tool-registry', 'memory-store', 'planner', 'executor', 'api-layer'],
49
+ tech_stack: { suggested: ['openai', 'langchain', 'redis', 'express'] },
50
+ structure: {
51
+ 'src/agents/': 'Agent definitions and personas',
52
+ 'src/tools/': 'Tool implementations',
53
+ 'src/memory/': 'Memory and state management',
54
+ 'src/planner/': 'Planning and orchestration',
55
+ 'src/api/': 'API endpoints',
56
+ 'tests/': 'Test suites'
57
+ },
58
+ nfrs: ['response time < 5s', 'tool execution timeout 30s', 'conversation history management']
59
+ },
60
+ {
61
+ id: 'api-platform',
62
+ name: 'API Platform',
63
+ category: 'api-platform',
64
+ description: 'RESTful API platform with authentication, rate limiting, and monitoring.',
65
+ components: ['api-gateway', 'auth-service', 'rate-limiter', 'business-logic', 'data-layer', 'monitoring'],
66
+ tech_stack: { suggested: ['express', 'postgresql', 'redis', 'jwt'] },
67
+ structure: {
68
+ 'src/routes/': 'API route definitions',
69
+ 'src/middleware/': 'Auth, rate limiting, validation',
70
+ 'src/services/': 'Business logic',
71
+ 'src/models/': 'Data models',
72
+ 'src/config/': 'Configuration',
73
+ 'tests/': 'Test suites'
74
+ },
75
+ nfrs: ['latency < 200ms p95', 'rate limit 1000 req/min', '99.9% availability']
76
+ },
77
+ {
78
+ id: 'event-driven',
79
+ name: 'Event-Driven System',
80
+ category: 'event-driven',
81
+ description: 'Event-driven microservice architecture with message broker and event sourcing.',
82
+ components: ['event-producer', 'message-broker', 'event-consumer', 'event-store', 'projection-service', 'api-layer'],
83
+ tech_stack: { suggested: ['kafka', 'nodejs', 'postgresql', 'elasticsearch'] },
84
+ structure: {
85
+ 'src/events/': 'Event definitions and schemas',
86
+ 'src/producers/': 'Event producers',
87
+ 'src/consumers/': 'Event consumers and handlers',
88
+ 'src/projections/': 'Read model projections',
89
+ 'src/api/': 'Query API',
90
+ 'tests/': 'Test suites'
91
+ },
92
+ nfrs: ['event processing < 100ms', 'at-least-once delivery', 'event ordering guarantees']
93
+ }
94
+ ];
95
+
96
+ /**
97
+ * Default registry.
98
+ * @returns {object}
99
+ */
100
+ function defaultRegistry() {
101
+ return {
102
+ version: '1.0.0',
103
+ created_at: new Date().toISOString(),
104
+ patterns: [...BUILTIN_PATTERNS],
105
+ custom_patterns: [],
106
+ instantiation_history: []
107
+ };
108
+ }
109
+
110
+ /**
111
+ * Load registry from disk.
112
+ * @param {string} [registryFile]
113
+ * @returns {object}
114
+ */
115
+ function loadRegistry(registryFile) {
116
+ const filePath = registryFile || DEFAULT_REGISTRY_FILE;
117
+ if (!fs.existsSync(filePath)) {
118
+ return defaultRegistry();
119
+ }
120
+ try {
121
+ const data = JSON.parse(fs.readFileSync(filePath, 'utf8'));
122
+ // Ensure built-in patterns are always present
123
+ if (!data.patterns) data.patterns = [...BUILTIN_PATTERNS];
124
+ return data;
125
+ } catch {
126
+ return defaultRegistry();
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Save registry to disk.
132
+ * @param {object} registry
133
+ * @param {string} [registryFile]
134
+ */
135
+ function saveRegistry(registry, registryFile) {
136
+ const filePath = registryFile || DEFAULT_REGISTRY_FILE;
137
+ const dir = path.dirname(filePath);
138
+ if (!fs.existsSync(dir)) {
139
+ fs.mkdirSync(dir, { recursive: true });
140
+ }
141
+ fs.writeFileSync(filePath, JSON.stringify(registry, null, 2) + '\n', 'utf8');
142
+ }
143
+
144
+ /**
145
+ * List available reference architectures.
146
+ *
147
+ * @param {object} [filter]
148
+ * @param {object} [options]
149
+ * @returns {object}
150
+ */
151
+ function listPatterns(filter = {}, options = {}) {
152
+ const registryFile = options.registryFile || DEFAULT_REGISTRY_FILE;
153
+ const registry = loadRegistry(registryFile);
154
+
155
+ let allPatterns = [...registry.patterns, ...(registry.custom_patterns || [])];
156
+
157
+ if (filter.category) {
158
+ allPatterns = allPatterns.filter(p => p.category === filter.category);
159
+ }
160
+
161
+ return {
162
+ success: true,
163
+ patterns: allPatterns.map(p => ({
164
+ id: p.id,
165
+ name: p.name,
166
+ category: p.category,
167
+ description: p.description,
168
+ components: p.components.length
169
+ })),
170
+ total: allPatterns.length,
171
+ categories: [...new Set(allPatterns.map(p => p.category))]
172
+ };
173
+ }
174
+
175
+ /**
176
+ * Get detailed reference architecture.
177
+ *
178
+ * @param {string} patternId
179
+ * @param {object} [options]
180
+ * @returns {object}
181
+ */
182
+ function getPattern(patternId, options = {}) {
183
+ const registryFile = options.registryFile || DEFAULT_REGISTRY_FILE;
184
+ const registry = loadRegistry(registryFile);
185
+
186
+ const allPatterns = [...registry.patterns, ...(registry.custom_patterns || [])];
187
+ const pattern = allPatterns.find(p => p.id === patternId);
188
+
189
+ if (!pattern) {
190
+ return { success: false, error: `Pattern not found: ${patternId}` };
191
+ }
192
+
193
+ return { success: true, pattern };
194
+ }
195
+
196
+ /**
197
+ * Register a custom reference architecture.
198
+ *
199
+ * @param {object} pattern
200
+ * @param {object} [options]
201
+ * @returns {object}
202
+ */
203
+ function registerPattern(pattern, options = {}) {
204
+ if (!pattern || !pattern.name || !pattern.description) {
205
+ return { success: false, error: 'name and description are required' };
206
+ }
207
+
208
+ const registryFile = options.registryFile || DEFAULT_REGISTRY_FILE;
209
+ const registry = loadRegistry(registryFile);
210
+
211
+ const id = pattern.id || pattern.name.toLowerCase().replace(/\s+/g, '-');
212
+ const allPatterns = [...registry.patterns, ...(registry.custom_patterns || [])];
213
+ if (allPatterns.find(p => p.id === id)) {
214
+ return { success: false, error: `Pattern "${id}" already exists` };
215
+ }
216
+
217
+ const newPattern = {
218
+ id,
219
+ name: pattern.name.trim(),
220
+ category: PATTERN_CATEGORIES.includes(pattern.category) ? pattern.category : 'other',
221
+ description: pattern.description.trim(),
222
+ components: pattern.components || [],
223
+ tech_stack: pattern.tech_stack || { suggested: [] },
224
+ structure: pattern.structure || {},
225
+ nfrs: pattern.nfrs || [],
226
+ custom: true,
227
+ created_at: new Date().toISOString()
228
+ };
229
+
230
+ if (!registry.custom_patterns) registry.custom_patterns = [];
231
+ registry.custom_patterns.push(newPattern);
232
+ saveRegistry(registry, registryFile);
233
+
234
+ return { success: true, pattern: newPattern };
235
+ }
236
+
237
+ /**
238
+ * Instantiate a reference architecture in a project.
239
+ *
240
+ * @param {string} patternId
241
+ * @param {string} root - Project root.
242
+ * @param {object} [options]
243
+ * @returns {object}
244
+ */
245
+ function instantiatePattern(patternId, root, options = {}) {
246
+ const registryFile = options.registryFile || path.join(root, DEFAULT_REGISTRY_FILE);
247
+ const registry = loadRegistry(registryFile);
248
+ const allPatterns = [...registry.patterns, ...(registry.custom_patterns || [])];
249
+ const pattern = allPatterns.find(p => p.id === patternId);
250
+
251
+ if (!pattern) {
252
+ return { success: false, error: `Pattern not found: ${patternId}` };
253
+ }
254
+
255
+ const created = [];
256
+ const skipped = [];
257
+
258
+ // Create directory structure
259
+ for (const [dir, description] of Object.entries(pattern.structure || {})) {
260
+ const fullDir = path.join(root, dir);
261
+ if (!fs.existsSync(fullDir)) {
262
+ fs.mkdirSync(fullDir, { recursive: true });
263
+ // Create a README.md in each directory
264
+ const readmePath = path.join(fullDir, 'README.md');
265
+ fs.writeFileSync(readmePath, `# ${dir.replace(/\/$/, '').split('/').pop()}\n\n${description}\n\nPart of the **${pattern.name}** reference architecture.\n`, 'utf8');
266
+ created.push(dir);
267
+ } else {
268
+ skipped.push(dir);
269
+ }
270
+ }
271
+
272
+ // Record instantiation
273
+ registry.instantiation_history = registry.instantiation_history || [];
274
+ registry.instantiation_history.push({
275
+ pattern_id: patternId,
276
+ instantiated_at: new Date().toISOString(),
277
+ root: path.resolve(root),
278
+ directories_created: created.length
279
+ });
280
+ saveRegistry(registry, registryFile);
281
+
282
+ return {
283
+ success: true,
284
+ pattern: patternId,
285
+ pattern_name: pattern.name,
286
+ directories_created: created,
287
+ directories_skipped: skipped,
288
+ components: pattern.components,
289
+ suggested_tech_stack: pattern.tech_stack,
290
+ nfrs: pattern.nfrs
291
+ };
292
+ }
293
+
294
+ module.exports = {
295
+ defaultRegistry,
296
+ loadRegistry,
297
+ saveRegistry,
298
+ listPatterns,
299
+ getPattern,
300
+ registerPattern,
301
+ instantiatePattern,
302
+ BUILTIN_PATTERNS,
303
+ PATTERN_CATEGORIES
304
+ };
@@ -0,0 +1,171 @@
1
+ /**
2
+ * release-readiness.js — Release Readiness Reviews (Item 26)
3
+ *
4
+ * Generate go/no-go packets covering quality, incidents, NFRs,
5
+ * dependencies, and rollback plans.
6
+ *
7
+ * Usage:
8
+ * node bin/lib/release-readiness.js assess|report|status [options]
9
+ *
10
+ * State file: .jumpstart/state/release-readiness.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', 'release-readiness.json');
19
+
20
+ const READINESS_CATEGORIES = ['quality', 'security', 'performance', 'dependencies', 'documentation',
21
+ 'rollback', 'monitoring', 'compliance'];
22
+
23
+ const READINESS_LEVELS = [
24
+ { min: 90, label: 'Ready', emoji: '🟢', recommendation: 'go' },
25
+ { min: 70, label: 'Conditionally Ready', emoji: '🟡', recommendation: 'conditional-go' },
26
+ { min: 50, label: 'Not Ready', emoji: '🟠', recommendation: 'no-go' },
27
+ { min: 0, label: 'Blocked', emoji: '🔴', recommendation: 'blocked' }
28
+ ];
29
+
30
+ function defaultState() {
31
+ return {
32
+ version: '1.0.0',
33
+ created_at: new Date().toISOString(),
34
+ last_updated: null,
35
+ assessments: [],
36
+ current_readiness: null
37
+ };
38
+ }
39
+
40
+ function loadState(stateFile) {
41
+ const filePath = stateFile || DEFAULT_STATE_FILE;
42
+ if (!fs.existsSync(filePath)) return defaultState();
43
+ try { return JSON.parse(fs.readFileSync(filePath, 'utf8')); }
44
+ catch { return defaultState(); }
45
+ }
46
+
47
+ function saveState(state, stateFile) {
48
+ const filePath = stateFile || DEFAULT_STATE_FILE;
49
+ const dir = path.dirname(filePath);
50
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
51
+ state.last_updated = new Date().toISOString();
52
+ fs.writeFileSync(filePath, JSON.stringify(state, null, 2) + '\n', 'utf8');
53
+ }
54
+
55
+ /**
56
+ * Assess release readiness across all categories.
57
+ *
58
+ * @param {string} root - Project root.
59
+ * @param {object} [options]
60
+ * @returns {object}
61
+ */
62
+ function assessReadiness(root, options = {}) {
63
+ const scores = {};
64
+
65
+ // Quality: check if tests exist and specs are approved
66
+ const hasTests = fs.existsSync(path.join(root, 'tests')) || fs.existsSync(path.join(root, 'test'));
67
+ const hasSpecs = fs.existsSync(path.join(root, 'specs'));
68
+ scores.quality = hasTests && hasSpecs ? 80 : hasTests ? 60 : 30;
69
+
70
+ // Security: check for secret scanner results, policy engine
71
+ const hasSecurityScan = fs.existsSync(path.join(root, '.jumpstart', 'state', 'secret-scan-results.json'));
72
+ const hasPolicies = fs.existsSync(path.join(root, '.jumpstart', 'policies.json'));
73
+ scores.security = hasSecurityScan && hasPolicies ? 85 : hasPolicies ? 60 : 40;
74
+
75
+ // Performance: check for NFR documentation
76
+ const archFile = path.join(root, 'specs', 'architecture.md');
77
+ let hasNFRs = false;
78
+ if (fs.existsSync(archFile)) {
79
+ try {
80
+ const content = fs.readFileSync(archFile, 'utf8');
81
+ hasNFRs = /\bNFR\b|non-functional|performance/i.test(content);
82
+ } catch { /* ignore */ }
83
+ }
84
+ scores.performance = hasNFRs ? 75 : 40;
85
+
86
+ // Dependencies: check for lock file
87
+ const hasLockFile = fs.existsSync(path.join(root, 'package-lock.json')) ||
88
+ fs.existsSync(path.join(root, 'yarn.lock'));
89
+ scores.dependencies = hasLockFile ? 80 : 50;
90
+
91
+ // Documentation
92
+ const hasReadme = fs.existsSync(path.join(root, 'README.md'));
93
+ scores.documentation = hasReadme && hasSpecs ? 85 : hasReadme ? 60 : 30;
94
+
95
+ // Rollback: check for rollback plan in specs
96
+ scores.rollback = 50; // Default: needs manual assessment
97
+
98
+ // Monitoring: check for ops ownership
99
+ scores.monitoring = 50; // Default: needs manual assessment
100
+
101
+ // Compliance: check for compliance state
102
+ const hasCompliance = fs.existsSync(path.join(root, '.jumpstart', 'state', 'compliance.json'));
103
+ scores.compliance = hasCompliance ? 70 : 40;
104
+
105
+ const totalScore = Math.round(
106
+ Object.values(scores).reduce((sum, s) => sum + s, 0) / READINESS_CATEGORIES.length
107
+ );
108
+
109
+ const level = READINESS_LEVELS.find(l => totalScore >= l.min) || READINESS_LEVELS[READINESS_LEVELS.length - 1];
110
+
111
+ const assessment = {
112
+ id: `rr-${Date.now()}`,
113
+ assessed_at: new Date().toISOString(),
114
+ scores,
115
+ total_score: totalScore,
116
+ level: level.label,
117
+ recommendation: level.recommendation,
118
+ blockers: Object.entries(scores).filter(([, v]) => v < 50).map(([k]) => k),
119
+ risks: Object.entries(scores).filter(([, v]) => v >= 50 && v < 70).map(([k]) => k)
120
+ };
121
+
122
+ const stateFile = options.stateFile || path.join(root, DEFAULT_STATE_FILE);
123
+ const state = loadState(stateFile);
124
+ state.assessments.push(assessment);
125
+ state.current_readiness = assessment;
126
+ saveState(state, stateFile);
127
+
128
+ return { success: true, ...assessment };
129
+ }
130
+
131
+ /**
132
+ * Generate a go/no-go report.
133
+ *
134
+ * @param {object} [options]
135
+ * @returns {object}
136
+ */
137
+ function generateReport(options = {}) {
138
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
139
+ const state = loadState(stateFile);
140
+
141
+ if (!state.current_readiness) {
142
+ return { success: false, error: 'No readiness assessment found. Run assess first.' };
143
+ }
144
+
145
+ const r = state.current_readiness;
146
+
147
+ return {
148
+ success: true,
149
+ recommendation: r.recommendation,
150
+ total_score: r.total_score,
151
+ level: r.level,
152
+ categories: Object.entries(r.scores).map(([name, score]) => ({
153
+ name,
154
+ score,
155
+ status: score >= 70 ? 'pass' : score >= 50 ? 'warning' : 'fail'
156
+ })),
157
+ blockers: r.blockers,
158
+ risks: r.risks,
159
+ assessed_at: r.assessed_at
160
+ };
161
+ }
162
+
163
+ module.exports = {
164
+ defaultState,
165
+ loadState,
166
+ saveState,
167
+ assessReadiness,
168
+ generateReport,
169
+ READINESS_CATEGORIES,
170
+ READINESS_LEVELS
171
+ };