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,193 @@
1
+ /**
2
+ * quality-graph.js — Code Quality Smell Graph (Item 60)
3
+ *
4
+ * Map hotspots across complexity, churn, bugs, ownership gaps,
5
+ * and requirement ambiguity.
6
+ *
7
+ * Usage:
8
+ * node bin/lib/quality-graph.js scan|report [options]
9
+ */
10
+
11
+ 'use strict';
12
+
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+
16
+ const QUALITY_DIMENSIONS = ['complexity', 'churn', 'test-coverage', 'ownership', 'documentation', 'dependencies'];
17
+
18
+ const COMPLEXITY_THRESHOLDS = {
19
+ low: { max_lines: 200, max_functions: 15 },
20
+ medium: { max_lines: 500, max_functions: 30 },
21
+ high: { max_lines: 1000, max_functions: 50 }
22
+ };
23
+
24
+ /**
25
+ * Scan a project for quality hotspots.
26
+ *
27
+ * @param {string} root - Project root.
28
+ * @param {object} [options]
29
+ * @returns {object}
30
+ */
31
+ function scanQuality(root, options = {}) {
32
+ const excludeDirs = options.excludeDirs || ['node_modules', '.git', 'dist', 'build', 'vendor'];
33
+ const extensions = options.extensions || ['.js', '.ts', '.py', '.java', '.go', '.rb'];
34
+ const hotspots = [];
35
+
36
+ function walk(dir) {
37
+ if (!fs.existsSync(dir)) return;
38
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
39
+ if (entry.isDirectory()) {
40
+ if (!excludeDirs.includes(entry.name)) walk(path.join(dir, entry.name));
41
+ } else if (entry.isFile()) {
42
+ const ext = path.extname(entry.name).toLowerCase();
43
+ if (extensions.includes(ext)) {
44
+ try {
45
+ const filePath = path.join(dir, entry.name);
46
+ const content = fs.readFileSync(filePath, 'utf8');
47
+ const relPath = path.relative(root, filePath).replace(/\\/g, '/');
48
+ const metrics = analyzeFileMetrics(content, ext);
49
+
50
+ hotspots.push({
51
+ file: relPath,
52
+ ...metrics,
53
+ overall_score: calculateOverallScore(metrics)
54
+ });
55
+ } catch { /* skip */ }
56
+ }
57
+ }
58
+ }
59
+ }
60
+
61
+ walk(root);
62
+
63
+ // Sort by score (lower = worse)
64
+ hotspots.sort((a, b) => a.overall_score - b.overall_score);
65
+
66
+ return {
67
+ success: true,
68
+ total_files: hotspots.length,
69
+ hotspots: hotspots.slice(0, options.limit || 20),
70
+ all_files: hotspots,
71
+ summary: {
72
+ total_files: hotspots.length,
73
+ average_score: hotspots.length > 0 ? Math.round(hotspots.reduce((s, h) => s + h.overall_score, 0) / hotspots.length) : 0,
74
+ critical_hotspots: hotspots.filter(h => h.overall_score < 30).length,
75
+ high_risk: hotspots.filter(h => h.overall_score < 50).length
76
+ }
77
+ };
78
+ }
79
+
80
+ /**
81
+ * Analyze file-level quality metrics.
82
+ *
83
+ * @param {string} content - File content.
84
+ * @param {string} ext - File extension.
85
+ * @returns {object}
86
+ */
87
+ function analyzeFileMetrics(content, ext) {
88
+ const lines = content.split('\n');
89
+ const totalLines = lines.length;
90
+ const blankLines = lines.filter(l => l.trim() === '').length;
91
+ const commentLines = lines.filter(l => /^\s*(?:\/\/|#|\/\*|\*|""")/.test(l)).length;
92
+ const codeLines = totalLines - blankLines - commentLines;
93
+
94
+ // Function/method count
95
+ const functions = (content.match(/(?:function\s+\w+|(?:const|let|var)\s+\w+\s*=\s*(?:async\s+)?(?:\([^)]*\)|[^=])\s*=>|\bdef\s+\w+|\bfunc\s+\w+)/g) || []).length;
96
+
97
+ // Nested depth (max bracket nesting)
98
+ let maxDepth = 0;
99
+ let currentDepth = 0;
100
+ for (const char of content) {
101
+ if (char === '{' || char === '(') { currentDepth++; maxDepth = Math.max(maxDepth, currentDepth); }
102
+ if (char === '}' || char === ')') currentDepth--;
103
+ }
104
+
105
+ // TODO/FIXME count
106
+ const todos = (content.match(/\b(?:TODO|FIXME|HACK|XXX)\b/gi) || []).length;
107
+
108
+ // Long lines
109
+ const longLines = lines.filter(l => l.length > 120).length;
110
+
111
+ // Import count
112
+ const imports = (content.match(/(?:^import\s|^const\s.*=\s*require|^from\s)/gm) || []).length;
113
+
114
+ return {
115
+ total_lines: totalLines,
116
+ code_lines: codeLines,
117
+ comment_ratio: totalLines > 0 ? Math.round((commentLines / totalLines) * 100) : 0,
118
+ functions,
119
+ max_nesting_depth: maxDepth,
120
+ todos,
121
+ long_lines: longLines,
122
+ imports,
123
+ complexity_level: totalLines > COMPLEXITY_THRESHOLDS.high.max_lines ? 'critical'
124
+ : totalLines > COMPLEXITY_THRESHOLDS.medium.max_lines ? 'high'
125
+ : totalLines > COMPLEXITY_THRESHOLDS.low.max_lines ? 'medium' : 'low'
126
+ };
127
+ }
128
+
129
+ /**
130
+ * Calculate overall quality score (0-100).
131
+ *
132
+ * @param {object} metrics
133
+ * @returns {number}
134
+ */
135
+ function calculateOverallScore(metrics) {
136
+ let score = 100;
137
+
138
+ // Penalize large files
139
+ if (metrics.total_lines > 500) score -= 15;
140
+ if (metrics.total_lines > 1000) score -= 15;
141
+
142
+ // Penalize deep nesting
143
+ if (metrics.max_nesting_depth > 5) score -= 10;
144
+ if (metrics.max_nesting_depth > 8) score -= 10;
145
+
146
+ // Penalize TODOs
147
+ score -= metrics.todos * 3;
148
+
149
+ // Penalize low comment ratio
150
+ if (metrics.comment_ratio < 5) score -= 10;
151
+
152
+ // Penalize many long lines
153
+ if (metrics.long_lines > 10) score -= 10;
154
+
155
+ // Penalize too many functions
156
+ if (metrics.functions > 30) score -= 10;
157
+
158
+ return Math.max(0, Math.min(100, score));
159
+ }
160
+
161
+ /**
162
+ * Generate quality report.
163
+ *
164
+ * @param {object} scanResult
165
+ * @returns {object}
166
+ */
167
+ function generateReport(scanResult) {
168
+ const byComplexity = { low: 0, medium: 0, high: 0, critical: 0 };
169
+ for (const h of scanResult.all_files || []) {
170
+ byComplexity[h.complexity_level] = (byComplexity[h.complexity_level] || 0) + 1;
171
+ }
172
+
173
+ return {
174
+ success: true,
175
+ summary: scanResult.summary,
176
+ by_complexity: byComplexity,
177
+ top_hotspots: (scanResult.hotspots || []).slice(0, 10),
178
+ recommendations: [
179
+ ...(scanResult.summary.critical_hotspots > 0 ? ['Refactor critical hotspots with high complexity'] : []),
180
+ ...(scanResult.summary.average_score < 60 ? ['Consider code review standards and complexity limits'] : []),
181
+ 'Add documentation to files with low comment ratios'
182
+ ]
183
+ };
184
+ }
185
+
186
+ module.exports = {
187
+ scanQuality,
188
+ analyzeFileMetrics,
189
+ calculateOverallScore,
190
+ generateReport,
191
+ QUALITY_DIMENSIONS,
192
+ COMPLEXITY_THRESHOLDS
193
+ };
@@ -0,0 +1,188 @@
1
+ /**
2
+ * raci-matrix.js — RACI-Aware Approvals (Item 23)
3
+ *
4
+ * Define who is Responsible, Accountable, Consulted, Informed
5
+ * for each phase and artifact.
6
+ *
7
+ * Usage:
8
+ * node bin/lib/raci-matrix.js define|check|report [options]
9
+ *
10
+ * State file: .jumpstart/state/raci-matrix.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', 'raci-matrix.json');
19
+
20
+ const RACI_ROLES = ['responsible', 'accountable', 'consulted', 'informed'];
21
+
22
+ const DEFAULT_PHASES = ['scout', 'challenger', 'analyst', 'pm', 'architect', 'developer'];
23
+
24
+ const DEFAULT_ARTIFACTS = [
25
+ 'specs/codebase-context.md',
26
+ 'specs/challenger-brief.md',
27
+ 'specs/product-brief.md',
28
+ 'specs/prd.md',
29
+ 'specs/architecture.md',
30
+ 'specs/implementation-plan.md'
31
+ ];
32
+
33
+ /**
34
+ * Default RACI state.
35
+ * @returns {object}
36
+ */
37
+ function defaultState() {
38
+ return {
39
+ version: '1.0.0',
40
+ created_at: new Date().toISOString(),
41
+ last_updated: null,
42
+ assignments: {},
43
+ stakeholders: []
44
+ };
45
+ }
46
+
47
+ function loadState(stateFile) {
48
+ const filePath = stateFile || DEFAULT_STATE_FILE;
49
+ if (!fs.existsSync(filePath)) return defaultState();
50
+ try { return JSON.parse(fs.readFileSync(filePath, 'utf8')); }
51
+ catch { return defaultState(); }
52
+ }
53
+
54
+ function saveState(state, stateFile) {
55
+ const filePath = stateFile || DEFAULT_STATE_FILE;
56
+ const dir = path.dirname(filePath);
57
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
58
+ state.last_updated = new Date().toISOString();
59
+ fs.writeFileSync(filePath, JSON.stringify(state, null, 2) + '\n', 'utf8');
60
+ }
61
+
62
+ /**
63
+ * Define a RACI assignment for an artifact/phase.
64
+ *
65
+ * @param {string} artifact - Artifact or phase identifier.
66
+ * @param {object} assignment - { responsible, accountable, consulted[], informed[] }
67
+ * @param {object} [options]
68
+ * @returns {object}
69
+ */
70
+ function defineAssignment(artifact, assignment, options = {}) {
71
+ if (!artifact) return { success: false, error: 'artifact is required' };
72
+ if (!assignment || !assignment.accountable) {
73
+ return { success: false, error: 'assignment.accountable is required' };
74
+ }
75
+
76
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
77
+ const state = loadState(stateFile);
78
+
79
+ state.assignments[artifact] = {
80
+ artifact,
81
+ responsible: assignment.responsible || assignment.accountable,
82
+ accountable: assignment.accountable,
83
+ consulted: Array.isArray(assignment.consulted) ? assignment.consulted : [],
84
+ informed: Array.isArray(assignment.informed) ? assignment.informed : [],
85
+ defined_at: new Date().toISOString()
86
+ };
87
+
88
+ // Track stakeholders
89
+ const allPeople = [
90
+ assignment.responsible,
91
+ assignment.accountable,
92
+ ...(assignment.consulted || []),
93
+ ...(assignment.informed || [])
94
+ ].filter(Boolean);
95
+
96
+ for (const person of allPeople) {
97
+ if (!state.stakeholders.includes(person)) {
98
+ state.stakeholders.push(person);
99
+ }
100
+ }
101
+
102
+ saveState(state, stateFile);
103
+
104
+ return { success: true, artifact, assignment: state.assignments[artifact] };
105
+ }
106
+
107
+ /**
108
+ * Check RACI compliance for approval actions.
109
+ *
110
+ * @param {string} artifact
111
+ * @param {string} actor - Who is performing the action.
112
+ * @param {string} action - 'approve' | 'review' | 'inform'
113
+ * @param {object} [options]
114
+ * @returns {object}
115
+ */
116
+ function checkPermission(artifact, actor, action, options = {}) {
117
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
118
+ const state = loadState(stateFile);
119
+
120
+ const assignment = state.assignments[artifact];
121
+ if (!assignment) {
122
+ return { success: true, allowed: true, reason: 'No RACI assignment defined — unrestricted' };
123
+ }
124
+
125
+ if (action === 'approve') {
126
+ const allowed = assignment.accountable === actor || assignment.responsible === actor;
127
+ return {
128
+ success: true,
129
+ allowed,
130
+ reason: allowed
131
+ ? `${actor} is ${assignment.accountable === actor ? 'Accountable' : 'Responsible'}`
132
+ : `${actor} is not Responsible or Accountable. Need: ${assignment.accountable}`
133
+ };
134
+ }
135
+
136
+ if (action === 'review') {
137
+ const allowed = assignment.consulted.includes(actor) ||
138
+ assignment.responsible === actor ||
139
+ assignment.accountable === actor;
140
+ return { success: true, allowed, reason: allowed ? 'Actor has review rights' : 'Actor is not in C/R/A' };
141
+ }
142
+
143
+ return { success: true, allowed: true, reason: 'Action permitted' };
144
+ }
145
+
146
+ /**
147
+ * Generate a RACI matrix report.
148
+ *
149
+ * @param {object} [options]
150
+ * @returns {object}
151
+ */
152
+ function generateReport(options = {}) {
153
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
154
+ const state = loadState(stateFile);
155
+
156
+ const matrix = Object.entries(state.assignments).map(([artifact, a]) => ({
157
+ artifact,
158
+ R: a.responsible,
159
+ A: a.accountable,
160
+ C: a.consulted.join(', '),
161
+ I: a.informed.join(', ')
162
+ }));
163
+
164
+ const gaps = DEFAULT_ARTIFACTS.filter(a => !state.assignments[a]);
165
+
166
+ return {
167
+ success: true,
168
+ matrix,
169
+ stakeholders: state.stakeholders,
170
+ total_assignments: matrix.length,
171
+ gaps,
172
+ coverage: DEFAULT_ARTIFACTS.length > 0
173
+ ? Math.round(((DEFAULT_ARTIFACTS.length - gaps.length) / DEFAULT_ARTIFACTS.length) * 100)
174
+ : 100
175
+ };
176
+ }
177
+
178
+ module.exports = {
179
+ defaultState,
180
+ loadState,
181
+ saveState,
182
+ defineAssignment,
183
+ checkPermission,
184
+ generateReport,
185
+ RACI_ROLES,
186
+ DEFAULT_PHASES,
187
+ DEFAULT_ARTIFACTS
188
+ };
@@ -0,0 +1,167 @@
1
+ /**
2
+ * refactor-planner.js — Refactor Planner with Dependency Safety (Item 43)
3
+ *
4
+ * Model safe sequencing for large refactors and migrations.
5
+ *
6
+ * Usage:
7
+ * node bin/lib/refactor-planner.js plan|validate|report [options]
8
+ *
9
+ * State file: .jumpstart/state/refactor-plan.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', 'refactor-plan.json');
18
+
19
+ const REFACTOR_TYPES = ['rename', 'move', 'extract', 'inline', 'restructure', 'migrate', 'upgrade'];
20
+ const RISK_LEVELS = ['low', 'medium', 'high', 'critical'];
21
+
22
+ function defaultState() {
23
+ return {
24
+ version: '1.0.0',
25
+ created_at: new Date().toISOString(),
26
+ last_updated: null,
27
+ plans: [],
28
+ completed: []
29
+ };
30
+ }
31
+
32
+ function loadState(stateFile) {
33
+ const filePath = stateFile || DEFAULT_STATE_FILE;
34
+ if (!fs.existsSync(filePath)) return defaultState();
35
+ try { return JSON.parse(fs.readFileSync(filePath, 'utf8')); }
36
+ catch { return defaultState(); }
37
+ }
38
+
39
+ function saveState(state, stateFile) {
40
+ const filePath = stateFile || DEFAULT_STATE_FILE;
41
+ const dir = path.dirname(filePath);
42
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
43
+ state.last_updated = new Date().toISOString();
44
+ fs.writeFileSync(filePath, JSON.stringify(state, null, 2) + '\n', 'utf8');
45
+ }
46
+
47
+ /**
48
+ * Create a refactor plan.
49
+ *
50
+ * @param {object} plan - { name, type, description, steps[], affected_files[] }
51
+ * @param {object} [options]
52
+ * @returns {object}
53
+ */
54
+ function createPlan(plan, options = {}) {
55
+ if (!plan || !plan.name || !plan.type) {
56
+ return { success: false, error: 'name and type are required' };
57
+ }
58
+
59
+ if (!REFACTOR_TYPES.includes(plan.type)) {
60
+ return { success: false, error: `Invalid type. Must be one of: ${REFACTOR_TYPES.join(', ')}` };
61
+ }
62
+
63
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
64
+ const state = loadState(stateFile);
65
+
66
+ const steps = (plan.steps || []).map((step, i) => ({
67
+ order: i + 1,
68
+ description: step.description || step,
69
+ status: 'pending',
70
+ dependencies: step.dependencies || [],
71
+ risk: step.risk || 'low',
72
+ rollback: step.rollback || null
73
+ }));
74
+
75
+ const newPlan = {
76
+ id: `REF-${(state.plans.length + 1).toString().padStart(3, '0')}`,
77
+ name: plan.name,
78
+ type: plan.type,
79
+ description: plan.description || '',
80
+ steps,
81
+ affected_files: plan.affected_files || [],
82
+ status: 'draft',
83
+ risk_level: steps.some(s => s.risk === 'critical') ? 'critical' : steps.some(s => s.risk === 'high') ? 'high' : 'medium',
84
+ created_at: new Date().toISOString()
85
+ };
86
+
87
+ state.plans.push(newPlan);
88
+ saveState(state, stateFile);
89
+
90
+ return { success: true, plan: newPlan };
91
+ }
92
+
93
+ /**
94
+ * Validate refactor plan dependencies.
95
+ *
96
+ * @param {string} planId
97
+ * @param {object} [options]
98
+ * @returns {object}
99
+ */
100
+ function validatePlan(planId, options = {}) {
101
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
102
+ const state = loadState(stateFile);
103
+
104
+ const plan = state.plans.find(p => p.id === planId);
105
+ if (!plan) return { success: false, error: `Plan not found: ${planId}` };
106
+
107
+ const issues = [];
108
+
109
+ // Check circular dependencies
110
+ for (const step of plan.steps) {
111
+ for (const dep of step.dependencies) {
112
+ const depStep = plan.steps.find(s => s.order === dep);
113
+ if (depStep && depStep.dependencies.includes(step.order)) {
114
+ issues.push({ type: 'circular-dependency', steps: [step.order, dep] });
115
+ }
116
+ }
117
+ }
118
+
119
+ // Check dependency ordering
120
+ for (const step of plan.steps) {
121
+ for (const dep of step.dependencies) {
122
+ if (dep >= step.order) {
123
+ issues.push({ type: 'invalid-order', step: step.order, depends_on: dep });
124
+ }
125
+ }
126
+ }
127
+
128
+ return {
129
+ success: true,
130
+ plan_id: planId,
131
+ valid: issues.length === 0,
132
+ issues,
133
+ total_steps: plan.steps.length,
134
+ risk_level: plan.risk_level
135
+ };
136
+ }
137
+
138
+ /**
139
+ * Generate refactor report.
140
+ *
141
+ * @param {object} [options]
142
+ * @returns {object}
143
+ */
144
+ function generateReport(options = {}) {
145
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
146
+ const state = loadState(stateFile);
147
+
148
+ return {
149
+ success: true,
150
+ total_plans: state.plans.length,
151
+ completed: state.completed.length,
152
+ active: state.plans.filter(p => p.status !== 'completed').length,
153
+ by_type: state.plans.reduce((acc, p) => { acc[p.type] = (acc[p.type] || 0) + 1; return acc; }, {}),
154
+ plans: state.plans
155
+ };
156
+ }
157
+
158
+ module.exports = {
159
+ defaultState,
160
+ loadState,
161
+ saveState,
162
+ createPlan,
163
+ validatePlan,
164
+ generateReport,
165
+ REFACTOR_TYPES,
166
+ RISK_LEVELS
167
+ };