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,129 @@
1
+ /**
2
+ * data-contracts.js — Data Contract Governance (Item 84)
3
+ *
4
+ * Schema evolution, versioning, lineage, and producer-consumer compatibility.
5
+ *
6
+ * Usage:
7
+ * node bin/lib/data-contracts.js register|validate|lineage|report [options]
8
+ *
9
+ * State file: .jumpstart/state/data-contracts.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', 'data-contracts.json');
18
+
19
+ const COMPATIBILITY_MODES = ['backward', 'forward', 'full', 'none'];
20
+
21
+ function defaultState() {
22
+ return { version: '1.0.0', contracts: [], lineage: [], last_updated: null };
23
+ }
24
+
25
+ function loadState(stateFile) {
26
+ const fp = stateFile || DEFAULT_STATE_FILE;
27
+ if (!fs.existsSync(fp)) return defaultState();
28
+ try { return JSON.parse(fs.readFileSync(fp, 'utf8')); }
29
+ catch { return defaultState(); }
30
+ }
31
+
32
+ function saveState(state, stateFile) {
33
+ const fp = stateFile || DEFAULT_STATE_FILE;
34
+ const dir = path.dirname(fp);
35
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
36
+ state.last_updated = new Date().toISOString();
37
+ fs.writeFileSync(fp, JSON.stringify(state, null, 2) + '\n', 'utf8');
38
+ }
39
+
40
+ function registerContract(name, schema, options = {}) {
41
+ if (!name || !schema) return { success: false, error: 'name and schema are required' };
42
+
43
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
44
+ const state = loadState(stateFile);
45
+
46
+ const contract = {
47
+ id: `DC-${Date.now()}`,
48
+ name,
49
+ version: options.version || '1.0.0',
50
+ schema,
51
+ producer: options.producer || null,
52
+ consumers: options.consumers || [],
53
+ compatibility: options.compatibility || 'backward',
54
+ created_at: new Date().toISOString()
55
+ };
56
+
57
+ state.contracts.push(contract);
58
+ saveState(state, stateFile);
59
+
60
+ return { success: true, contract };
61
+ }
62
+
63
+ function validateCompatibility(contractId, newSchema, options = {}) {
64
+ if (!contractId || !newSchema) return { success: false, error: 'contractId and newSchema are required' };
65
+
66
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
67
+ const state = loadState(stateFile);
68
+
69
+ const contract = state.contracts.find(c => c.id === contractId);
70
+ if (!contract) return { success: false, error: `Contract ${contractId} not found` };
71
+
72
+ const oldFields = new Set(Object.keys(contract.schema));
73
+ const newFields = new Set(Object.keys(newSchema));
74
+
75
+ const added = [...newFields].filter(f => !oldFields.has(f));
76
+ const removed = [...oldFields].filter(f => !newFields.has(f));
77
+
78
+ let compatible = true;
79
+ const issues = [];
80
+
81
+ if (contract.compatibility === 'backward' && removed.length > 0) {
82
+ compatible = false;
83
+ issues.push({ type: 'breaking_removal', fields: removed });
84
+ }
85
+ if (contract.compatibility === 'forward' && added.length > 0) {
86
+ compatible = false;
87
+ issues.push({ type: 'forward_incompatible', fields: added });
88
+ }
89
+
90
+ return { success: true, compatible, issues, added, removed };
91
+ }
92
+
93
+ function trackLineage(sourceContract, targetContract, options = {}) {
94
+ if (!sourceContract || !targetContract) return { success: false, error: 'source and target contracts are required' };
95
+
96
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
97
+ const state = loadState(stateFile);
98
+
99
+ const entry = {
100
+ source: sourceContract,
101
+ target: targetContract,
102
+ transformation: options.transformation || 'direct',
103
+ created_at: new Date().toISOString()
104
+ };
105
+
106
+ state.lineage.push(entry);
107
+ saveState(state, stateFile);
108
+
109
+ return { success: true, lineage: entry };
110
+ }
111
+
112
+ function generateReport(options = {}) {
113
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
114
+ const state = loadState(stateFile);
115
+
116
+ return {
117
+ success: true,
118
+ total_contracts: state.contracts.length,
119
+ total_lineage: state.lineage.length,
120
+ contracts: state.contracts.map(c => ({ id: c.id, name: c.name, version: c.version, compatibility: c.compatibility })),
121
+ lineage: state.lineage
122
+ };
123
+ }
124
+
125
+ module.exports = {
126
+ registerContract, validateCompatibility, trackLineage, generateReport,
127
+ loadState, saveState, defaultState,
128
+ COMPATIBILITY_MODES
129
+ };
@@ -0,0 +1,158 @@
1
+ /**
2
+ * db-evolution.js — Database Evolution Planner (Item 49)
3
+ *
4
+ * Generate migration strategy, backward compatibility plan,
5
+ * data validation, and rollback approach.
6
+ *
7
+ * Usage:
8
+ * node bin/lib/db-evolution.js plan|validate|report [options]
9
+ *
10
+ * State file: .jumpstart/state/db-evolution.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', 'db-evolution.json');
19
+
20
+ const MIGRATION_TYPES = ['add-column', 'drop-column', 'rename-column', 'add-table', 'drop-table',
21
+ 'add-index', 'drop-index', 'modify-type', 'add-constraint', 'data-migration'];
22
+
23
+ const RISK_LEVELS = {
24
+ 'add-column': 'low', 'add-table': 'low', 'add-index': 'low',
25
+ 'rename-column': 'medium', 'modify-type': 'medium', 'add-constraint': 'medium',
26
+ 'drop-column': 'high', 'drop-table': 'high', 'drop-index': 'medium',
27
+ 'data-migration': 'high'
28
+ };
29
+
30
+ function defaultState() {
31
+ return {
32
+ version: '1.0.0',
33
+ created_at: new Date().toISOString(),
34
+ last_updated: null,
35
+ migrations: [],
36
+ rollback_scripts: []
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
+ * Plan a database migration.
57
+ *
58
+ * @param {object} migration - { name, type, description, table?, column?, backward_compatible? }
59
+ * @param {object} [options]
60
+ * @returns {object}
61
+ */
62
+ function planMigration(migration, options = {}) {
63
+ if (!migration || !migration.name || !migration.type) {
64
+ return { success: false, error: 'name and type are required' };
65
+ }
66
+
67
+ if (!MIGRATION_TYPES.includes(migration.type)) {
68
+ return { success: false, error: `Invalid type. Must be one of: ${MIGRATION_TYPES.join(', ')}` };
69
+ }
70
+
71
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
72
+ const state = loadState(stateFile);
73
+
74
+ const risk = RISK_LEVELS[migration.type] || 'medium';
75
+
76
+ const mig = {
77
+ id: `DB-${(state.migrations.length + 1).toString().padStart(3, '0')}`,
78
+ name: migration.name,
79
+ type: migration.type,
80
+ description: migration.description || '',
81
+ table: migration.table || null,
82
+ column: migration.column || null,
83
+ risk_level: risk,
84
+ backward_compatible: migration.backward_compatible !== false,
85
+ rollback_strategy: migration.rollback_strategy || (risk === 'low' ? 'reverse-migration' : 'backup-restore'),
86
+ validation_steps: migration.validation_steps || ['row-count', 'schema-compare'],
87
+ status: 'planned',
88
+ created_at: new Date().toISOString()
89
+ };
90
+
91
+ state.migrations.push(mig);
92
+ saveState(state, stateFile);
93
+
94
+ return { success: true, migration: mig };
95
+ }
96
+
97
+ /**
98
+ * Validate migration plan for safety.
99
+ *
100
+ * @param {string} migrationId
101
+ * @param {object} [options]
102
+ * @returns {object}
103
+ */
104
+ function validateMigration(migrationId, options = {}) {
105
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
106
+ const state = loadState(stateFile);
107
+
108
+ const mig = state.migrations.find(m => m.id === migrationId);
109
+ if (!mig) return { success: false, error: `Migration not found: ${migrationId}` };
110
+
111
+ const warnings = [];
112
+ if (!mig.backward_compatible) warnings.push('Migration is not backward compatible');
113
+ if (mig.risk_level === 'high') warnings.push('High-risk migration — requires explicit approval');
114
+ if (!mig.rollback_strategy) warnings.push('No rollback strategy defined');
115
+ if (mig.type === 'drop-table' || mig.type === 'drop-column') {
116
+ warnings.push('Destructive operation — ensure data backup exists');
117
+ }
118
+
119
+ return {
120
+ success: true,
121
+ migration_id: migrationId,
122
+ safe: warnings.length === 0,
123
+ warnings,
124
+ risk_level: mig.risk_level,
125
+ backward_compatible: mig.backward_compatible
126
+ };
127
+ }
128
+
129
+ /**
130
+ * Generate DB evolution report.
131
+ *
132
+ * @param {object} [options]
133
+ * @returns {object}
134
+ */
135
+ function generateReport(options = {}) {
136
+ const stateFile = options.stateFile || DEFAULT_STATE_FILE;
137
+ const state = loadState(stateFile);
138
+
139
+ return {
140
+ success: true,
141
+ total_migrations: state.migrations.length,
142
+ by_type: state.migrations.reduce((acc, m) => { acc[m.type] = (acc[m.type] || 0) + 1; return acc; }, {}),
143
+ by_risk: state.migrations.reduce((acc, m) => { acc[m.risk_level] = (acc[m.risk_level] || 0) + 1; return acc; }, {}),
144
+ high_risk: state.migrations.filter(m => m.risk_level === 'high'),
145
+ migrations: state.migrations
146
+ };
147
+ }
148
+
149
+ module.exports = {
150
+ defaultState,
151
+ loadState,
152
+ saveState,
153
+ planMigration,
154
+ validateMigration,
155
+ generateReport,
156
+ MIGRATION_TYPES,
157
+ RISK_LEVELS
158
+ };
@@ -0,0 +1,299 @@
1
+ /**
2
+ * decision-conflicts.js — Decision Conflict Detection
3
+ *
4
+ * Detect when ADRs, PRD decisions, architecture docs, and code
5
+ * choices contradict one another.
6
+ *
7
+ * Usage:
8
+ * node bin/lib/decision-conflicts.js detect|report [options]
9
+ */
10
+
11
+ 'use strict';
12
+
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+
16
+ const CONFLICT_TYPES = ['technology', 'pattern', 'constraint', 'requirement', 'terminology'];
17
+
18
+ /**
19
+ * Extract decisions from ADR files.
20
+ * @param {string} decisionsDir - Path to specs/decisions/.
21
+ * @returns {object[]}
22
+ */
23
+ function extractADRDecisions(decisionsDir) {
24
+ const decisions = [];
25
+
26
+ if (!fs.existsSync(decisionsDir)) return decisions;
27
+
28
+ const files = fs.readdirSync(decisionsDir).filter(f => f.endsWith('.md'));
29
+ for (const file of files) {
30
+ const content = fs.readFileSync(path.join(decisionsDir, file), 'utf8');
31
+ const titleMatch = content.match(/^#\s+(.+)$/m);
32
+ const statusMatch = content.match(/\*\*Status[:\s]*\*?\*?\s*(.+)/i) || content.match(/Status[:\s]+(.+)/i);
33
+ const decisionMatch = content.match(/##\s+Decision\s*\n([\s\S]*?)(?=\n##|\n$|$)/i);
34
+
35
+ decisions.push({
36
+ source: `specs/decisions/${file}`,
37
+ type: 'adr',
38
+ title: titleMatch ? titleMatch[1].trim() : file,
39
+ status: statusMatch ? statusMatch[1].trim().toLowerCase() : 'unknown',
40
+ decision_text: decisionMatch ? decisionMatch[1].trim() : '',
41
+ technologies: extractTechReferences(content),
42
+ patterns: extractPatternReferences(content)
43
+ });
44
+ }
45
+
46
+ return decisions;
47
+ }
48
+
49
+ /**
50
+ * Extract decisions from architecture doc.
51
+ * @param {string} archPath
52
+ * @returns {object[]}
53
+ */
54
+ function extractArchDecisions(archPath) {
55
+ if (!fs.existsSync(archPath)) return [];
56
+
57
+ const content = fs.readFileSync(archPath, 'utf8');
58
+ const decisions = [];
59
+ const sections = content.split(/^##\s+/m);
60
+
61
+ for (const section of sections) {
62
+ if (section.trim().length === 0) continue;
63
+ const titleLine = section.split('\n')[0].trim();
64
+ const sectionContent = section.split('\n').slice(1).join('\n');
65
+
66
+ decisions.push({
67
+ source: 'specs/architecture.md',
68
+ type: 'architecture',
69
+ title: titleLine,
70
+ decision_text: sectionContent.trim().slice(0, 500),
71
+ technologies: extractTechReferences(sectionContent),
72
+ patterns: extractPatternReferences(sectionContent)
73
+ });
74
+ }
75
+
76
+ return decisions;
77
+ }
78
+
79
+ /**
80
+ * Extract decisions from PRD.
81
+ * @param {string} prdPath
82
+ * @returns {object[]}
83
+ */
84
+ function extractPRDDecisions(prdPath) {
85
+ if (!fs.existsSync(prdPath)) return [];
86
+
87
+ const content = fs.readFileSync(prdPath, 'utf8');
88
+ const decisions = [];
89
+
90
+ // Extract NFRs and constraints
91
+ const nfrSection = content.match(/##\s+(?:Non-Functional|NFR|Constraints).*?\n([\s\S]*?)(?=\n##|\n$|$)/i);
92
+ if (nfrSection) {
93
+ decisions.push({
94
+ source: 'specs/prd.md',
95
+ type: 'prd',
96
+ title: 'Non-Functional Requirements',
97
+ decision_text: nfrSection[1].trim().slice(0, 500),
98
+ technologies: extractTechReferences(nfrSection[1]),
99
+ patterns: extractPatternReferences(nfrSection[1])
100
+ });
101
+ }
102
+
103
+ // Extract technology decisions mentioned in PRD
104
+ const techSection = content.match(/##\s+(?:Tech|Technology|Stack|Technical).*?\n([\s\S]*?)(?=\n##|\n$|$)/i);
105
+ if (techSection) {
106
+ decisions.push({
107
+ source: 'specs/prd.md',
108
+ type: 'prd',
109
+ title: 'Technology Decisions',
110
+ decision_text: techSection[1].trim().slice(0, 500),
111
+ technologies: extractTechReferences(techSection[1]),
112
+ patterns: extractPatternReferences(techSection[1])
113
+ });
114
+ }
115
+
116
+ return decisions;
117
+ }
118
+
119
+ /**
120
+ * Extract technology references from text.
121
+ * @param {string} text
122
+ * @returns {string[]}
123
+ */
124
+ function extractTechReferences(text) {
125
+ const techTerms = [
126
+ 'react', 'vue', 'angular', 'svelte', 'next\\.js', 'nuxt',
127
+ 'express', 'fastify', 'koa', 'hapi', 'nestjs',
128
+ 'postgresql', 'mysql', 'mongodb', 'redis', 'sqlite', 'dynamodb',
129
+ 'docker', 'kubernetes', 'aws', 'azure', 'gcp',
130
+ 'graphql', 'rest', 'grpc', 'websocket',
131
+ 'typescript', 'javascript', 'python', 'go', 'rust', 'java',
132
+ 'kafka', 'rabbitmq', 'sqs', 'nats',
133
+ 'openai', 'langchain', 'pinecone', 'chromadb'
134
+ ];
135
+
136
+ const found = [];
137
+ for (const tech of techTerms) {
138
+ const pattern = new RegExp(`\\b${tech}\\b`, 'gi');
139
+ if (pattern.test(text)) {
140
+ found.push(tech.replace(/\\\./g, '.'));
141
+ }
142
+ }
143
+ return [...new Set(found)];
144
+ }
145
+
146
+ /**
147
+ * Extract architectural pattern references from text.
148
+ * @param {string} text
149
+ * @returns {string[]}
150
+ */
151
+ function extractPatternReferences(text) {
152
+ const patternTerms = [
153
+ 'microservice', 'monolith', 'serverless', 'event-driven', 'event sourcing',
154
+ 'cqrs', 'saga', 'domain-driven', 'hexagonal', 'clean architecture',
155
+ 'mvc', 'mvvm', 'repository pattern', 'factory', 'singleton',
156
+ 'pub-sub', 'message queue', 'circuit breaker', 'api gateway',
157
+ 'rag', 'agent', 'multi-agent'
158
+ ];
159
+
160
+ const found = [];
161
+ for (const pattern of patternTerms) {
162
+ const regex = new RegExp(`\\b${pattern}s?\\b`, 'gi');
163
+ if (regex.test(text)) {
164
+ found.push(pattern);
165
+ }
166
+ }
167
+ return [...new Set(found)];
168
+ }
169
+
170
+ /**
171
+ * Detect conflicts between decisions.
172
+ *
173
+ * @param {object[]} decisions - Array of extracted decisions.
174
+ * @returns {object[]}
175
+ */
176
+ function findConflicts(decisions) {
177
+ const conflicts = [];
178
+
179
+ // Technology conflicts: same category but different choices
180
+ const techBySource = {};
181
+ for (const d of decisions) {
182
+ for (const tech of d.technologies) {
183
+ if (!techBySource[tech]) techBySource[tech] = [];
184
+ techBySource[tech].push(d);
185
+ }
186
+ }
187
+
188
+ // Check for competing technologies in the same category
189
+ const competing = {
190
+ frontend: ['react', 'vue', 'angular', 'svelte'],
191
+ backend: ['express', 'fastify', 'koa', 'hapi', 'nestjs'],
192
+ database: ['postgresql', 'mysql', 'mongodb', 'sqlite', 'dynamodb'],
193
+ messaging: ['kafka', 'rabbitmq', 'sqs', 'nats'],
194
+ cloud: ['aws', 'azure', 'gcp']
195
+ };
196
+
197
+ for (const [category, techs] of Object.entries(competing)) {
198
+ const usedTechs = techs.filter(t => techBySource[t]);
199
+ if (usedTechs.length > 1) {
200
+ const sources = usedTechs.flatMap(t => techBySource[t].map(d => d.source));
201
+ conflicts.push({
202
+ type: 'technology',
203
+ category,
204
+ description: `Competing ${category} technologies referenced: ${usedTechs.join(', ')}`,
205
+ technologies: usedTechs,
206
+ sources: [...new Set(sources)],
207
+ severity: 'warning'
208
+ });
209
+ }
210
+ }
211
+
212
+ // Pattern conflicts: contradictory patterns
213
+ const patternBySource = {};
214
+ for (const d of decisions) {
215
+ for (const pattern of d.patterns) {
216
+ if (!patternBySource[pattern]) patternBySource[pattern] = [];
217
+ patternBySource[pattern].push(d);
218
+ }
219
+ }
220
+
221
+ const contradictory = [
222
+ ['microservice', 'monolith'],
223
+ ['event sourcing', 'cqrs'],
224
+ ['serverless', 'kubernetes']
225
+ ];
226
+
227
+ for (const [p1, p2] of contradictory) {
228
+ if (patternBySource[p1] && patternBySource[p2]) {
229
+ const sources = [
230
+ ...patternBySource[p1].map(d => d.source),
231
+ ...patternBySource[p2].map(d => d.source)
232
+ ];
233
+ conflicts.push({
234
+ type: 'pattern',
235
+ description: `Potentially contradictory patterns: "${p1}" and "${p2}"`,
236
+ patterns: [p1, p2],
237
+ sources: [...new Set(sources)],
238
+ severity: 'warning'
239
+ });
240
+ }
241
+ }
242
+
243
+ return conflicts;
244
+ }
245
+
246
+ /**
247
+ * Detect decision conflicts across all project artifacts.
248
+ *
249
+ * @param {string} root - Project root.
250
+ * @param {object} [options]
251
+ * @returns {object}
252
+ */
253
+ function detectConflicts(root, options = {}) {
254
+ const decisions = [];
255
+
256
+ // Collect decisions from all sources
257
+ const decisionsDir = path.join(root, 'specs', 'decisions');
258
+ decisions.push(...extractADRDecisions(decisionsDir));
259
+
260
+ const archPath = path.join(root, 'specs', 'architecture.md');
261
+ decisions.push(...extractArchDecisions(archPath));
262
+
263
+ const prdPath = path.join(root, 'specs', 'prd.md');
264
+ decisions.push(...extractPRDDecisions(prdPath));
265
+
266
+ if (decisions.length === 0) {
267
+ return { success: true, conflicts: [], message: 'No decisions found to analyze' };
268
+ }
269
+
270
+ const conflicts = findConflicts(decisions);
271
+
272
+ return {
273
+ success: true,
274
+ total_decisions: decisions.length,
275
+ decisions_by_source: {
276
+ adr: decisions.filter(d => d.type === 'adr').length,
277
+ architecture: decisions.filter(d => d.type === 'architecture').length,
278
+ prd: decisions.filter(d => d.type === 'prd').length
279
+ },
280
+ conflicts,
281
+ summary: {
282
+ total_conflicts: conflicts.length,
283
+ technology_conflicts: conflicts.filter(c => c.type === 'technology').length,
284
+ pattern_conflicts: conflicts.filter(c => c.type === 'pattern').length,
285
+ has_conflicts: conflicts.length > 0
286
+ }
287
+ };
288
+ }
289
+
290
+ module.exports = {
291
+ extractADRDecisions,
292
+ extractArchDecisions,
293
+ extractPRDDecisions,
294
+ extractTechReferences,
295
+ extractPatternReferences,
296
+ findConflicts,
297
+ detectConflicts,
298
+ CONFLICT_TYPES
299
+ };