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,366 @@
1
+ /**
2
+ * plan-executor.js — Rich Plan Execution Engine
3
+ *
4
+ * Turn implementation-plan tasks into executable, resumable,
5
+ * verifiable agent jobs.
6
+ *
7
+ * State file: .jumpstart/state/plan-execution.json
8
+ *
9
+ * Usage:
10
+ * node bin/lib/plan-executor.js start|status|resume|verify|reset [options]
11
+ */
12
+
13
+ 'use strict';
14
+
15
+ const fs = require('fs');
16
+ const path = require('path');
17
+
18
+ const DEFAULT_EXECUTION_FILE = path.join('.jumpstart', 'state', 'plan-execution.json');
19
+
20
+ const TASK_STATUSES = ['pending', 'in_progress', 'completed', 'failed', 'skipped', 'blocked'];
21
+
22
+ /**
23
+ * Default execution state.
24
+ * @returns {object}
25
+ */
26
+ function defaultExecutionState() {
27
+ return {
28
+ version: '1.0.0',
29
+ created_at: new Date().toISOString(),
30
+ last_updated: null,
31
+ plan_source: null,
32
+ jobs: [],
33
+ execution_log: []
34
+ };
35
+ }
36
+
37
+ /**
38
+ * Load execution state from disk.
39
+ * @param {string} [stateFile]
40
+ * @returns {object}
41
+ */
42
+ function loadExecutionState(stateFile) {
43
+ const filePath = stateFile || DEFAULT_EXECUTION_FILE;
44
+ if (!fs.existsSync(filePath)) {
45
+ return defaultExecutionState();
46
+ }
47
+ try {
48
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
49
+ } catch {
50
+ return defaultExecutionState();
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Save execution state to disk.
56
+ * @param {object} state
57
+ * @param {string} [stateFile]
58
+ */
59
+ function saveExecutionState(state, stateFile) {
60
+ const filePath = stateFile || DEFAULT_EXECUTION_FILE;
61
+ const dir = path.dirname(filePath);
62
+ if (!fs.existsSync(dir)) {
63
+ fs.mkdirSync(dir, { recursive: true });
64
+ }
65
+ state.last_updated = new Date().toISOString();
66
+ fs.writeFileSync(filePath, JSON.stringify(state, null, 2) + '\n', 'utf8');
67
+ }
68
+
69
+ /**
70
+ * Parse implementation plan into executable jobs.
71
+ *
72
+ * @param {string} planContent - Implementation plan markdown content.
73
+ * @returns {object[]}
74
+ */
75
+ function parsePlanToJobs(planContent) {
76
+ const jobs = [];
77
+ const lines = planContent.split('\n');
78
+ let currentMilestone = null;
79
+
80
+ for (const line of lines) {
81
+ // Detect milestones like "## Milestone 1: ...", "## M01: ..."
82
+ const milestoneMatch = line.match(/^#{2,3}\s+(?:Milestone\s+)?(\d+|M\d+)[:\s—–-]+\s*(.+)$/i);
83
+ if (milestoneMatch) {
84
+ currentMilestone = milestoneMatch[1].startsWith('M') ? milestoneMatch[1] : `M${milestoneMatch[1].padStart(2, '0')}`;
85
+ continue;
86
+ }
87
+
88
+ // Detect tasks like "- M01-T01: ...", "### M01-T01 — ..."
89
+ const taskMatch = line.match(/(?:^#{2,4}\s+|^[-*]\s+\*{0,2})(M\d+-T\d+)(?:\*{0,2})[:\s—–-]+\s*(.+)/i);
90
+ if (taskMatch) {
91
+ const taskId = taskMatch[1];
92
+ const title = taskMatch[2].trim().replace(/\*{1,2}/g, '');
93
+
94
+ // Extract story refs
95
+ const storyRefs = line.match(/E\d+-S\d+/g) || [];
96
+
97
+ // Extract dependency refs (depends on: M01-T02)
98
+ const depMatch = line.match(/depends?\s+on[:\s]+([^.]+)/i);
99
+ const dependencies = depMatch
100
+ ? (depMatch[1].match(/M\d+-T\d+/g) || [])
101
+ : [];
102
+
103
+ jobs.push({
104
+ id: taskId,
105
+ title,
106
+ milestone: currentMilestone || taskId.split('-')[0],
107
+ status: 'pending',
108
+ story_refs: [...new Set(storyRefs)],
109
+ dependencies,
110
+ started_at: null,
111
+ completed_at: null,
112
+ verification: null,
113
+ output_files: [],
114
+ error: null
115
+ });
116
+ }
117
+ }
118
+
119
+ return jobs;
120
+ }
121
+
122
+ /**
123
+ * Initialize execution from implementation plan.
124
+ *
125
+ * @param {string} root - Project root.
126
+ * @param {object} [options]
127
+ * @returns {object}
128
+ */
129
+ function initializeExecution(root, options = {}) {
130
+ const planPath = options.planPath || path.join(root, 'specs', 'implementation-plan.md');
131
+ const stateFile = options.stateFile || path.join(root, DEFAULT_EXECUTION_FILE);
132
+
133
+ if (!fs.existsSync(planPath)) {
134
+ return { success: false, error: `Implementation plan not found: ${planPath}` };
135
+ }
136
+
137
+ const planContent = fs.readFileSync(planPath, 'utf8');
138
+ const jobs = parsePlanToJobs(planContent);
139
+
140
+ if (jobs.length === 0) {
141
+ return { success: false, error: 'No tasks found in implementation plan' };
142
+ }
143
+
144
+ const state = defaultExecutionState();
145
+ state.plan_source = path.relative(root, planPath);
146
+ state.jobs = jobs;
147
+ state.execution_log.push({
148
+ event: 'initialized',
149
+ timestamp: new Date().toISOString(),
150
+ total_jobs: jobs.length
151
+ });
152
+
153
+ saveExecutionState(state, stateFile);
154
+
155
+ return {
156
+ success: true,
157
+ total_jobs: jobs.length,
158
+ milestones: [...new Set(jobs.map(j => j.milestone))],
159
+ jobs: jobs.map(j => ({ id: j.id, title: j.title, milestone: j.milestone, dependencies: j.dependencies }))
160
+ };
161
+ }
162
+
163
+ /**
164
+ * Get execution status.
165
+ *
166
+ * @param {object} [options]
167
+ * @returns {object}
168
+ */
169
+ function getExecutionStatus(options = {}) {
170
+ const stateFile = options.stateFile || DEFAULT_EXECUTION_FILE;
171
+ const state = loadExecutionState(stateFile);
172
+
173
+ if (state.jobs.length === 0) {
174
+ return { success: true, initialized: false, message: 'No execution plan loaded' };
175
+ }
176
+
177
+ const statusCounts = {};
178
+ for (const status of TASK_STATUSES) {
179
+ statusCounts[status] = state.jobs.filter(j => j.status === status).length;
180
+ }
181
+
182
+ const completedPct = state.jobs.length > 0
183
+ ? Math.round((statusCounts.completed / state.jobs.length) * 100)
184
+ : 0;
185
+
186
+ // Find next actionable tasks (pending with all dependencies completed)
187
+ const completedIds = new Set(state.jobs.filter(j => j.status === 'completed').map(j => j.id));
188
+ const nextTasks = state.jobs.filter(j =>
189
+ j.status === 'pending' &&
190
+ j.dependencies.every(dep => completedIds.has(dep))
191
+ );
192
+
193
+ return {
194
+ success: true,
195
+ initialized: true,
196
+ plan_source: state.plan_source,
197
+ total_jobs: state.jobs.length,
198
+ progress: completedPct,
199
+ status_counts: statusCounts,
200
+ next_tasks: nextTasks.map(j => ({ id: j.id, title: j.title, milestone: j.milestone })),
201
+ jobs: state.jobs.map(j => ({
202
+ id: j.id,
203
+ title: j.title,
204
+ status: j.status,
205
+ milestone: j.milestone
206
+ }))
207
+ };
208
+ }
209
+
210
+ /**
211
+ * Update a job's status.
212
+ *
213
+ * @param {string} jobId - Task ID (e.g., M01-T01).
214
+ * @param {string} status - New status.
215
+ * @param {object} [options]
216
+ * @returns {object}
217
+ */
218
+ function updateJobStatus(jobId, status, options = {}) {
219
+ if (!TASK_STATUSES.includes(status)) {
220
+ return { success: false, error: `Invalid status: ${status}. Must be one of: ${TASK_STATUSES.join(', ')}` };
221
+ }
222
+
223
+ const stateFile = options.stateFile || DEFAULT_EXECUTION_FILE;
224
+ const state = loadExecutionState(stateFile);
225
+ const job = state.jobs.find(j => j.id === jobId);
226
+
227
+ if (!job) {
228
+ return { success: false, error: `Job not found: ${jobId}` };
229
+ }
230
+
231
+ const previousStatus = job.status;
232
+ job.status = status;
233
+
234
+ if (status === 'in_progress' && !job.started_at) {
235
+ job.started_at = new Date().toISOString();
236
+ }
237
+ if (status === 'completed') {
238
+ job.completed_at = new Date().toISOString();
239
+ }
240
+ if (status === 'failed' && options.error) {
241
+ job.error = options.error;
242
+ }
243
+ if (options.output_files) {
244
+ job.output_files = options.output_files;
245
+ }
246
+
247
+ state.execution_log.push({
248
+ event: 'status_change',
249
+ job_id: jobId,
250
+ from: previousStatus,
251
+ to: status,
252
+ timestamp: new Date().toISOString()
253
+ });
254
+
255
+ saveExecutionState(state, stateFile);
256
+
257
+ return {
258
+ success: true,
259
+ job_id: jobId,
260
+ previous_status: previousStatus,
261
+ new_status: status,
262
+ started_at: job.started_at,
263
+ completed_at: job.completed_at
264
+ };
265
+ }
266
+
267
+ /**
268
+ * Verify a completed job.
269
+ *
270
+ * @param {string} jobId - Task ID.
271
+ * @param {string} root - Project root.
272
+ * @param {object} [options]
273
+ * @returns {object}
274
+ */
275
+ function verifyJob(jobId, root, options = {}) {
276
+ const stateFile = options.stateFile || path.join(root, DEFAULT_EXECUTION_FILE);
277
+ const state = loadExecutionState(stateFile);
278
+ const job = state.jobs.find(j => j.id === jobId);
279
+
280
+ if (!job) {
281
+ return { success: false, error: `Job not found: ${jobId}` };
282
+ }
283
+
284
+ const checks = [];
285
+
286
+ // Check if job has output files
287
+ if (job.output_files && job.output_files.length > 0) {
288
+ for (const file of job.output_files) {
289
+ const exists = fs.existsSync(path.join(root, file));
290
+ checks.push({ check: `file_exists: ${file}`, passed: exists });
291
+ }
292
+ }
293
+
294
+ // Check status
295
+ checks.push({ check: 'status_completed', passed: job.status === 'completed' });
296
+ checks.push({ check: 'has_completion_time', passed: !!job.completed_at });
297
+ checks.push({ check: 'no_errors', passed: !job.error });
298
+
299
+ const allPassed = checks.every(c => c.passed);
300
+
301
+ job.verification = {
302
+ verified_at: new Date().toISOString(),
303
+ passed: allPassed,
304
+ checks
305
+ };
306
+
307
+ saveExecutionState(state, stateFile);
308
+
309
+ return {
310
+ success: true,
311
+ job_id: jobId,
312
+ verified: allPassed,
313
+ checks,
314
+ summary: {
315
+ total_checks: checks.length,
316
+ passed: checks.filter(c => c.passed).length,
317
+ failed: checks.filter(c => !c.passed).length
318
+ }
319
+ };
320
+ }
321
+
322
+ /**
323
+ * Reset execution state.
324
+ *
325
+ * @param {object} [options]
326
+ * @returns {object}
327
+ */
328
+ function resetExecution(options = {}) {
329
+ const stateFile = options.stateFile || DEFAULT_EXECUTION_FILE;
330
+ const state = loadExecutionState(stateFile);
331
+ const previousCount = state.jobs.length;
332
+
333
+ for (const job of state.jobs) {
334
+ job.status = 'pending';
335
+ job.started_at = null;
336
+ job.completed_at = null;
337
+ job.verification = null;
338
+ job.error = null;
339
+ }
340
+
341
+ state.execution_log.push({
342
+ event: 'reset',
343
+ timestamp: new Date().toISOString(),
344
+ jobs_reset: previousCount
345
+ });
346
+
347
+ saveExecutionState(state, stateFile);
348
+
349
+ return {
350
+ success: true,
351
+ jobs_reset: previousCount
352
+ };
353
+ }
354
+
355
+ module.exports = {
356
+ defaultExecutionState,
357
+ loadExecutionState,
358
+ saveExecutionState,
359
+ parsePlanToJobs,
360
+ initializeExecution,
361
+ getExecutionStatus,
362
+ updateJobStatus,
363
+ verifyJob,
364
+ resetExecution,
365
+ TASK_STATUSES
366
+ };
@@ -0,0 +1,119 @@
1
+ /**
2
+ * platform-engineering.js — Platform Engineering Integration (Item 86)
3
+ *
4
+ * Connect to golden paths, service templates, IDP portals,
5
+ * and paved-road tooling.
6
+ *
7
+ * Usage:
8
+ * node bin/lib/platform-engineering.js register|list|instantiate|report [options]
9
+ *
10
+ * State file: .jumpstart/state/platform-engineering.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', 'platform-engineering.json');
19
+
20
+ const TEMPLATE_TYPES = ['service', 'library', 'worker', 'api-gateway', 'frontend'];
21
+ const GOLDEN_PATH_STAGES = ['scaffold', 'ci-cd', 'observability', 'security', 'deployment'];
22
+
23
+ function defaultState() {
24
+ return { version: '1.0.0', templates: [], golden_paths: [], instances: [], last_updated: null };
25
+ }
26
+
27
+ function loadState(stateFile) {
28
+ const fp = stateFile || DEFAULT_STATE_FILE;
29
+ if (!fs.existsSync(fp)) return defaultState();
30
+ try { return JSON.parse(fs.readFileSync(fp, 'utf8')); }
31
+ catch { return defaultState(); }
32
+ }
33
+
34
+ function saveState(state, stateFile) {
35
+ const fp = stateFile || DEFAULT_STATE_FILE;
36
+ const dir = path.dirname(fp);
37
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
38
+ state.last_updated = new Date().toISOString();
39
+ fs.writeFileSync(fp, JSON.stringify(state, null, 2) + '\n', 'utf8');
40
+ }
41
+
42
+ function registerTemplate(name, type, options = {}) {
43
+ if (!name || !type) return { success: false, error: 'name and type are required' };
44
+ if (!TEMPLATE_TYPES.includes(type)) {
45
+ return { success: false, error: `Unknown type: ${type}. Valid: ${TEMPLATE_TYPES.join(', ')}` };
46
+ }
47
+
48
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
49
+ const state = loadState(stateFile);
50
+
51
+ const template = {
52
+ id: `PLAT-${Date.now()}`,
53
+ name,
54
+ type,
55
+ tech_stack: options.tech_stack || [],
56
+ golden_path_stages: options.stages || GOLDEN_PATH_STAGES,
57
+ version: options.version || '1.0.0',
58
+ created_at: new Date().toISOString()
59
+ };
60
+
61
+ state.templates.push(template);
62
+ saveState(state, stateFile);
63
+
64
+ return { success: true, template };
65
+ }
66
+
67
+ function listTemplates(options = {}) {
68
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
69
+ const state = loadState(stateFile);
70
+
71
+ let templates = state.templates;
72
+ if (options.type) templates = templates.filter(t => t.type === options.type);
73
+
74
+ return { success: true, total: templates.length, templates };
75
+ }
76
+
77
+ function instantiateTemplate(templateId, projectName, options = {}) {
78
+ if (!templateId || !projectName) return { success: false, error: 'templateId and projectName are required' };
79
+
80
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
81
+ const state = loadState(stateFile);
82
+
83
+ const template = state.templates.find(t => t.id === templateId);
84
+ if (!template) return { success: false, error: `Template ${templateId} not found` };
85
+
86
+ const instance = {
87
+ id: `INST-${Date.now()}`,
88
+ template_id: templateId,
89
+ template_name: template.name,
90
+ project_name: projectName,
91
+ status: 'created',
92
+ created_at: new Date().toISOString()
93
+ };
94
+
95
+ state.instances.push(instance);
96
+ saveState(state, stateFile);
97
+
98
+ return { success: true, instance };
99
+ }
100
+
101
+ function generateReport(options = {}) {
102
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
103
+ const state = loadState(stateFile);
104
+
105
+ return {
106
+ success: true,
107
+ total_templates: state.templates.length,
108
+ total_instances: state.instances.length,
109
+ by_type: state.templates.reduce((acc, t) => { acc[t.type] = (acc[t.type] || 0) + 1; return acc; }, {}),
110
+ templates: state.templates,
111
+ instances: state.instances
112
+ };
113
+ }
114
+
115
+ module.exports = {
116
+ registerTemplate, listTemplates, instantiateTemplate, generateReport,
117
+ loadState, saveState, defaultState,
118
+ TEMPLATE_TYPES, GOLDEN_PATH_STAGES
119
+ };
@@ -0,0 +1,126 @@
1
+ /**
2
+ * playback-summaries.js — Stakeholder Playback Summaries (Item 68)
3
+ *
4
+ * Translate technical outputs into business-language summaries
5
+ * for each audience.
6
+ *
7
+ * Usage:
8
+ * node bin/lib/playback-summaries.js generate|list [options]
9
+ */
10
+
11
+ 'use strict';
12
+
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+
16
+ const AUDIENCES = ['executive', 'technical', 'product', 'operations', 'compliance'];
17
+
18
+ const AUDIENCE_CONFIG = {
19
+ executive: {
20
+ label: 'Executive Summary',
21
+ focus: ['business_value', 'timeline', 'budget', 'risks', 'decisions'],
22
+ tone: 'strategic',
23
+ max_length: 500
24
+ },
25
+ technical: {
26
+ label: 'Technical Summary',
27
+ focus: ['architecture', 'tech_stack', 'api_design', 'data_model', 'nfrs'],
28
+ tone: 'technical',
29
+ max_length: 1000
30
+ },
31
+ product: {
32
+ label: 'Product Summary',
33
+ focus: ['user_stories', 'acceptance_criteria', 'personas', 'scope', 'prioritization'],
34
+ tone: 'user-centric',
35
+ max_length: 800
36
+ },
37
+ operations: {
38
+ label: 'Operations Summary',
39
+ focus: ['deployment', 'monitoring', 'runbooks', 'sla_slo', 'incident_response'],
40
+ tone: 'operational',
41
+ max_length: 700
42
+ },
43
+ compliance: {
44
+ label: 'Compliance Summary',
45
+ focus: ['regulations', 'controls', 'evidence', 'audit_trail', 'data_classification'],
46
+ tone: 'regulatory',
47
+ max_length: 600
48
+ }
49
+ };
50
+
51
+ /**
52
+ * Generate a playback summary for a target audience.
53
+ */
54
+ function generateSummary(root, audience, options = {}) {
55
+ if (!AUDIENCES.includes(audience)) {
56
+ return { success: false, error: `Unknown audience: ${audience}. Valid: ${AUDIENCES.join(', ')}` };
57
+ }
58
+
59
+ const config = AUDIENCE_CONFIG[audience];
60
+ const summary = {
61
+ audience,
62
+ label: config.label,
63
+ tone: config.tone,
64
+ focus_areas: config.focus,
65
+ generated_at: new Date().toISOString(),
66
+ sections: {}
67
+ };
68
+
69
+ // Gather data from available specs
70
+ const specsDir = path.join(root, 'specs');
71
+ const availableSpecs = [];
72
+ if (fs.existsSync(specsDir)) {
73
+ for (const f of fs.readdirSync(specsDir).filter(f => f.endsWith('.md'))) {
74
+ availableSpecs.push(f);
75
+ try {
76
+ const content = fs.readFileSync(path.join(specsDir, f), 'utf8');
77
+ summary.sections[f] = {
78
+ available: true,
79
+ size: content.length,
80
+ has_approval: content.includes('Phase Gate Approval')
81
+ };
82
+ } catch { summary.sections[f] = { available: true, size: 0 }; }
83
+ }
84
+ }
85
+
86
+ // Phase status
87
+ const stateFile = path.join(root, '.jumpstart', 'state', 'state.json');
88
+ if (fs.existsSync(stateFile)) {
89
+ try {
90
+ const state = JSON.parse(fs.readFileSync(stateFile, 'utf8'));
91
+ summary.project_status = {
92
+ current_phase: state.current_phase || 0,
93
+ last_agent: state.current_agent || null
94
+ };
95
+ } catch { summary.project_status = { current_phase: 0 }; }
96
+ } else {
97
+ summary.project_status = { current_phase: 0 };
98
+ }
99
+
100
+ summary.specs_available = availableSpecs;
101
+ summary.max_length = config.max_length;
102
+
103
+ return { success: true, summary };
104
+ }
105
+
106
+ /**
107
+ * List available audience types.
108
+ */
109
+ function listAudiences() {
110
+ return {
111
+ success: true,
112
+ audiences: AUDIENCES.map(a => ({
113
+ id: a,
114
+ label: AUDIENCE_CONFIG[a].label,
115
+ tone: AUDIENCE_CONFIG[a].tone,
116
+ focus: AUDIENCE_CONFIG[a].focus
117
+ }))
118
+ };
119
+ }
120
+
121
+ module.exports = {
122
+ generateSummary,
123
+ listAudiences,
124
+ AUDIENCES,
125
+ AUDIENCE_CONFIG
126
+ };