@ktpartners/dgs-platform 2.6.2

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 (256) hide show
  1. package/LICENSE +38 -0
  2. package/README.md +851 -0
  3. package/agents/dgs-codebase-cross-analyzer.md +183 -0
  4. package/agents/dgs-codebase-mapper.md +782 -0
  5. package/agents/dgs-codebase-synthesizer.md +156 -0
  6. package/agents/dgs-debugger.md +1256 -0
  7. package/agents/dgs-executor.md +550 -0
  8. package/agents/dgs-integration-checker.md +481 -0
  9. package/agents/dgs-nyquist-auditor.md +178 -0
  10. package/agents/dgs-phase-researcher.md +563 -0
  11. package/agents/dgs-phase-verifier.md +450 -0
  12. package/agents/dgs-plan-checker.md +708 -0
  13. package/agents/dgs-planner.md +1324 -0
  14. package/agents/dgs-project-researcher.md +631 -0
  15. package/agents/dgs-research-synthesizer.md +249 -0
  16. package/agents/dgs-roadmapper.md +652 -0
  17. package/agents/dgs-verifier.md +607 -0
  18. package/bin/install.js +2073 -0
  19. package/commands/dgs/add-doc.md +45 -0
  20. package/commands/dgs/add-idea.md +38 -0
  21. package/commands/dgs/add-phase.md +43 -0
  22. package/commands/dgs/add-repo.md +54 -0
  23. package/commands/dgs/add-tests.md +41 -0
  24. package/commands/dgs/add-todo.md +47 -0
  25. package/commands/dgs/approve-spec.md +38 -0
  26. package/commands/dgs/audit-milestone.md +36 -0
  27. package/commands/dgs/audit-phase.md +37 -0
  28. package/commands/dgs/cancel-job.md +23 -0
  29. package/commands/dgs/capture-principle.md +143 -0
  30. package/commands/dgs/check-todos.md +45 -0
  31. package/commands/dgs/cleanup.md +18 -0
  32. package/commands/dgs/complete-milestone.md +136 -0
  33. package/commands/dgs/complete-project.md +70 -0
  34. package/commands/dgs/consolidate-ideas.md +50 -0
  35. package/commands/dgs/create-milestone-job.md +37 -0
  36. package/commands/dgs/debug.md +164 -0
  37. package/commands/dgs/develop-idea.md +53 -0
  38. package/commands/dgs/discuss-idea.md +41 -0
  39. package/commands/dgs/discuss-phase.md +83 -0
  40. package/commands/dgs/execute-phase.md +41 -0
  41. package/commands/dgs/fast.md +38 -0
  42. package/commands/dgs/find-related-ideas.md +43 -0
  43. package/commands/dgs/health.md +28 -0
  44. package/commands/dgs/help.md +22 -0
  45. package/commands/dgs/import-spec.md +36 -0
  46. package/commands/dgs/init-product.md +28 -0
  47. package/commands/dgs/insert-phase.md +32 -0
  48. package/commands/dgs/join-discord.md +18 -0
  49. package/commands/dgs/list-docs.md +40 -0
  50. package/commands/dgs/list-ideas.md +42 -0
  51. package/commands/dgs/list-jobs.md +22 -0
  52. package/commands/dgs/list-phase-assumptions.md +46 -0
  53. package/commands/dgs/list-projects.md +57 -0
  54. package/commands/dgs/list-specs.md +40 -0
  55. package/commands/dgs/map-codebase.md +92 -0
  56. package/commands/dgs/new-milestone.md +44 -0
  57. package/commands/dgs/new-project.md +42 -0
  58. package/commands/dgs/node-repair.md +26 -0
  59. package/commands/dgs/overlap-check.md +20 -0
  60. package/commands/dgs/pause-work.md +38 -0
  61. package/commands/dgs/plan-milestone-gaps.md +34 -0
  62. package/commands/dgs/plan-phase.md +44 -0
  63. package/commands/dgs/progress.md +24 -0
  64. package/commands/dgs/quick.md +41 -0
  65. package/commands/dgs/reactivate-project.md +70 -0
  66. package/commands/dgs/reapply-patches.md +110 -0
  67. package/commands/dgs/refine-spec.md +38 -0
  68. package/commands/dgs/reject-idea.md +43 -0
  69. package/commands/dgs/remove-doc.md +44 -0
  70. package/commands/dgs/remove-phase.md +31 -0
  71. package/commands/dgs/remove-repo.md +69 -0
  72. package/commands/dgs/research-idea.md +43 -0
  73. package/commands/dgs/research-phase.md +189 -0
  74. package/commands/dgs/restore-idea.md +45 -0
  75. package/commands/dgs/resume-work.md +40 -0
  76. package/commands/dgs/rollback-job.md +24 -0
  77. package/commands/dgs/run-job.md +35 -0
  78. package/commands/dgs/search.md +40 -0
  79. package/commands/dgs/set-profile.md +34 -0
  80. package/commands/dgs/settings.md +38 -0
  81. package/commands/dgs/switch-project.md +58 -0
  82. package/commands/dgs/undo-consolidation.md +42 -0
  83. package/commands/dgs/update-idea.md +44 -0
  84. package/commands/dgs/update.md +37 -0
  85. package/commands/dgs/validate-phase.md +35 -0
  86. package/commands/dgs/verify-work.md +39 -0
  87. package/commands/dgs/write-spec.md +49 -0
  88. package/deliver-great-systems/.planning/phases/09-backend-wiring-and-error-handling/09-01-SUMMARY.md +84 -0
  89. package/deliver-great-systems/.planning/phases/09-backend-wiring-and-error-handling/09-02-SUMMARY.md +86 -0
  90. package/deliver-great-systems/.planning/phases/10-v1-to-v2-migration-flow/10-01-SUMMARY.md +85 -0
  91. package/deliver-great-systems/bin/dgs-tools.cjs +1444 -0
  92. package/deliver-great-systems/bin/lib/auto-test.cjs +1365 -0
  93. package/deliver-great-systems/bin/lib/commands.cjs +570 -0
  94. package/deliver-great-systems/bin/lib/config.cjs +417 -0
  95. package/deliver-great-systems/bin/lib/conflict-agent.cjs +1063 -0
  96. package/deliver-great-systems/bin/lib/conflict-agent.test.cjs +554 -0
  97. package/deliver-great-systems/bin/lib/context.cjs +929 -0
  98. package/deliver-great-systems/bin/lib/context.test.cjs +693 -0
  99. package/deliver-great-systems/bin/lib/core.cjs +744 -0
  100. package/deliver-great-systems/bin/lib/core.test.cjs +822 -0
  101. package/deliver-great-systems/bin/lib/docs.cjs +919 -0
  102. package/deliver-great-systems/bin/lib/docs.test.cjs +211 -0
  103. package/deliver-great-systems/bin/lib/execution.cjs +705 -0
  104. package/deliver-great-systems/bin/lib/execution.test.cjs +1472 -0
  105. package/deliver-great-systems/bin/lib/frontmatter.cjs +324 -0
  106. package/deliver-great-systems/bin/lib/ideas.cjs +1406 -0
  107. package/deliver-great-systems/bin/lib/ideas.test.cjs +1417 -0
  108. package/deliver-great-systems/bin/lib/identity.cjs +125 -0
  109. package/deliver-great-systems/bin/lib/init.cjs +1114 -0
  110. package/deliver-great-systems/bin/lib/init.test.cjs +1271 -0
  111. package/deliver-great-systems/bin/lib/jobs.cjs +2015 -0
  112. package/deliver-great-systems/bin/lib/jobs.test.cjs +2619 -0
  113. package/deliver-great-systems/bin/lib/merge-conflicts.cjs +654 -0
  114. package/deliver-great-systems/bin/lib/merge-conflicts.test.cjs +370 -0
  115. package/deliver-great-systems/bin/lib/migration.cjs +352 -0
  116. package/deliver-great-systems/bin/lib/migration.test.cjs +582 -0
  117. package/deliver-great-systems/bin/lib/milestone.cjs +243 -0
  118. package/deliver-great-systems/bin/lib/overlap.cjs +437 -0
  119. package/deliver-great-systems/bin/lib/overlap.test.cjs +747 -0
  120. package/deliver-great-systems/bin/lib/path-audit.test.cjs +384 -0
  121. package/deliver-great-systems/bin/lib/paths.cjs +144 -0
  122. package/deliver-great-systems/bin/lib/paths.test.cjs +486 -0
  123. package/deliver-great-systems/bin/lib/phase.cjs +910 -0
  124. package/deliver-great-systems/bin/lib/projects.cjs +691 -0
  125. package/deliver-great-systems/bin/lib/projects.test.cjs +871 -0
  126. package/deliver-great-systems/bin/lib/repos.cjs +1432 -0
  127. package/deliver-great-systems/bin/lib/repos.test.cjs +1882 -0
  128. package/deliver-great-systems/bin/lib/roadmap.cjs +305 -0
  129. package/deliver-great-systems/bin/lib/search.cjs +570 -0
  130. package/deliver-great-systems/bin/lib/specs.cjs +1303 -0
  131. package/deliver-great-systems/bin/lib/state.cjs +893 -0
  132. package/deliver-great-systems/bin/lib/template.cjs +228 -0
  133. package/deliver-great-systems/bin/lib/test-helpers.cjs +291 -0
  134. package/deliver-great-systems/bin/lib/verify.cjs +796 -0
  135. package/deliver-great-systems/references/checkpoints.md +776 -0
  136. package/deliver-great-systems/references/conflict-resolution.md +66 -0
  137. package/deliver-great-systems/references/context-tiers.md +166 -0
  138. package/deliver-great-systems/references/continuation-format.md +249 -0
  139. package/deliver-great-systems/references/decimal-phase-calculation.md +67 -0
  140. package/deliver-great-systems/references/git-integration.md +250 -0
  141. package/deliver-great-systems/references/git-planning-commit.md +40 -0
  142. package/deliver-great-systems/references/model-profile-resolution.md +36 -0
  143. package/deliver-great-systems/references/model-profiles.md +95 -0
  144. package/deliver-great-systems/references/phase-argument-parsing.md +61 -0
  145. package/deliver-great-systems/references/planning-config.md +224 -0
  146. package/deliver-great-systems/references/questioning.md +162 -0
  147. package/deliver-great-systems/references/spec-review-loop.md +177 -0
  148. package/deliver-great-systems/references/tdd.md +265 -0
  149. package/deliver-great-systems/references/ui-brand.md +160 -0
  150. package/deliver-great-systems/references/verification-patterns.md +612 -0
  151. package/deliver-great-systems/templates/DEBUG.md +166 -0
  152. package/deliver-great-systems/templates/UAT.md +251 -0
  153. package/deliver-great-systems/templates/VALIDATION.md +95 -0
  154. package/deliver-great-systems/templates/claude-md.md +74 -0
  155. package/deliver-great-systems/templates/codebase/architecture.md +257 -0
  156. package/deliver-great-systems/templates/codebase/concerns.md +312 -0
  157. package/deliver-great-systems/templates/codebase/conventions.md +309 -0
  158. package/deliver-great-systems/templates/codebase/integrations.md +282 -0
  159. package/deliver-great-systems/templates/codebase/stack.md +188 -0
  160. package/deliver-great-systems/templates/codebase/structure.md +287 -0
  161. package/deliver-great-systems/templates/codebase/testing.md +482 -0
  162. package/deliver-great-systems/templates/config.json +38 -0
  163. package/deliver-great-systems/templates/context.md +354 -0
  164. package/deliver-great-systems/templates/continue-here.md +80 -0
  165. package/deliver-great-systems/templates/debug-subagent-prompt.md +93 -0
  166. package/deliver-great-systems/templates/discovery.md +148 -0
  167. package/deliver-great-systems/templates/milestone-archive.md +125 -0
  168. package/deliver-great-systems/templates/milestone.md +117 -0
  169. package/deliver-great-systems/templates/phase-prompt.md +615 -0
  170. package/deliver-great-systems/templates/planner-subagent-prompt.md +119 -0
  171. package/deliver-great-systems/templates/project.md +186 -0
  172. package/deliver-great-systems/templates/requirements.md +233 -0
  173. package/deliver-great-systems/templates/research-project/ARCHITECTURE.md +206 -0
  174. package/deliver-great-systems/templates/research-project/FEATURES.md +149 -0
  175. package/deliver-great-systems/templates/research-project/PITFALLS.md +202 -0
  176. package/deliver-great-systems/templates/research-project/STACK.md +122 -0
  177. package/deliver-great-systems/templates/research-project/SUMMARY.md +172 -0
  178. package/deliver-great-systems/templates/research.md +554 -0
  179. package/deliver-great-systems/templates/retrospective.md +54 -0
  180. package/deliver-great-systems/templates/roadmap.md +204 -0
  181. package/deliver-great-systems/templates/state.md +178 -0
  182. package/deliver-great-systems/templates/summary-complex.md +59 -0
  183. package/deliver-great-systems/templates/summary-minimal.md +41 -0
  184. package/deliver-great-systems/templates/summary-standard.md +48 -0
  185. package/deliver-great-systems/templates/summary.md +253 -0
  186. package/deliver-great-systems/templates/user-setup.md +313 -0
  187. package/deliver-great-systems/templates/verification-report.md +324 -0
  188. package/deliver-great-systems/workflows/add-doc.md +151 -0
  189. package/deliver-great-systems/workflows/add-idea.md +96 -0
  190. package/deliver-great-systems/workflows/add-phase.md +120 -0
  191. package/deliver-great-systems/workflows/add-tests.md +359 -0
  192. package/deliver-great-systems/workflows/add-todo.md +162 -0
  193. package/deliver-great-systems/workflows/approve-spec.md +194 -0
  194. package/deliver-great-systems/workflows/audit-milestone.md +364 -0
  195. package/deliver-great-systems/workflows/audit-phase.md +462 -0
  196. package/deliver-great-systems/workflows/cancel-job.md +108 -0
  197. package/deliver-great-systems/workflows/check-todos.md +181 -0
  198. package/deliver-great-systems/workflows/cleanup.md +247 -0
  199. package/deliver-great-systems/workflows/codereview.md +526 -0
  200. package/deliver-great-systems/workflows/complete-milestone.md +1298 -0
  201. package/deliver-great-systems/workflows/consolidate-ideas.md +365 -0
  202. package/deliver-great-systems/workflows/create-milestone-job.md +177 -0
  203. package/deliver-great-systems/workflows/develop-idea.md +544 -0
  204. package/deliver-great-systems/workflows/diagnose-issues.md +231 -0
  205. package/deliver-great-systems/workflows/discovery-phase.md +301 -0
  206. package/deliver-great-systems/workflows/discuss-idea.md +263 -0
  207. package/deliver-great-systems/workflows/discuss-phase.md +733 -0
  208. package/deliver-great-systems/workflows/execute-phase.md +571 -0
  209. package/deliver-great-systems/workflows/execute-plan.md +592 -0
  210. package/deliver-great-systems/workflows/find-related-ideas.md +271 -0
  211. package/deliver-great-systems/workflows/health.md +173 -0
  212. package/deliver-great-systems/workflows/help.md +997 -0
  213. package/deliver-great-systems/workflows/import-spec.md +381 -0
  214. package/deliver-great-systems/workflows/init-product.md +767 -0
  215. package/deliver-great-systems/workflows/insert-phase.md +138 -0
  216. package/deliver-great-systems/workflows/list-docs.md +119 -0
  217. package/deliver-great-systems/workflows/list-ideas.md +154 -0
  218. package/deliver-great-systems/workflows/list-jobs.md +89 -0
  219. package/deliver-great-systems/workflows/list-phase-assumptions.md +192 -0
  220. package/deliver-great-systems/workflows/list-specs.md +101 -0
  221. package/deliver-great-systems/workflows/map-codebase.md +621 -0
  222. package/deliver-great-systems/workflows/new-milestone.md +591 -0
  223. package/deliver-great-systems/workflows/new-project.md +1113 -0
  224. package/deliver-great-systems/workflows/node-repair.md +94 -0
  225. package/deliver-great-systems/workflows/overlap-check.md +86 -0
  226. package/deliver-great-systems/workflows/pause-work.md +134 -0
  227. package/deliver-great-systems/workflows/plan-milestone-gaps.md +306 -0
  228. package/deliver-great-systems/workflows/plan-phase.md +698 -0
  229. package/deliver-great-systems/workflows/progress.md +386 -0
  230. package/deliver-great-systems/workflows/quick.md +845 -0
  231. package/deliver-great-systems/workflows/refine-spec.md +275 -0
  232. package/deliver-great-systems/workflows/reject-idea.md +109 -0
  233. package/deliver-great-systems/workflows/remove-doc.md +117 -0
  234. package/deliver-great-systems/workflows/remove-phase.md +163 -0
  235. package/deliver-great-systems/workflows/research-idea.md +325 -0
  236. package/deliver-great-systems/workflows/research-phase.md +81 -0
  237. package/deliver-great-systems/workflows/restore-idea.md +101 -0
  238. package/deliver-great-systems/workflows/resume-project.md +311 -0
  239. package/deliver-great-systems/workflows/rollback-job.md +130 -0
  240. package/deliver-great-systems/workflows/run-job.md +498 -0
  241. package/deliver-great-systems/workflows/search.md +130 -0
  242. package/deliver-great-systems/workflows/set-profile.md +83 -0
  243. package/deliver-great-systems/workflows/settings.md +470 -0
  244. package/deliver-great-systems/workflows/transition.md +563 -0
  245. package/deliver-great-systems/workflows/undo-consolidation.md +155 -0
  246. package/deliver-great-systems/workflows/update-idea.md +157 -0
  247. package/deliver-great-systems/workflows/update.md +242 -0
  248. package/deliver-great-systems/workflows/validate-phase.md +177 -0
  249. package/deliver-great-systems/workflows/verify-phase.md +253 -0
  250. package/deliver-great-systems/workflows/verify-work.md +671 -0
  251. package/deliver-great-systems/workflows/write-spec.md +450 -0
  252. package/hooks/dist/dgs-check-update.js +62 -0
  253. package/hooks/dist/dgs-context-monitor.js +141 -0
  254. package/hooks/dist/dgs-statusline.js +115 -0
  255. package/package.json +60 -0
  256. package/scripts/build-hooks.js +43 -0
@@ -0,0 +1,654 @@
1
+ /**
2
+ * Merge Conflicts — Conflict detection, context assembly, classification, and resolution recording
3
+ *
4
+ * Provides CLI commands for the merge-conflicts subcommand group:
5
+ * - detect: parse failed merge state, return conflicted files grouped by repo
6
+ * - context: assemble resolution context for a specific file
7
+ * - resolved: record resolution outcome to RESOLUTIONS.md
8
+ * - summary: aggregate all resolutions into a merge-level report
9
+ */
10
+
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+ const { execGit, safeReadFile, loadConfig, output, error } = require('./core.cjs');
14
+ const { parseReposMd } = require('./repos.cjs');
15
+ const { extractFrontmatter } = require('./frontmatter.cjs');
16
+
17
+ // ─── Conflict Hunk Parsing ──────────────────────────────────────────────────
18
+
19
+ /**
20
+ * Parse conflict hunks from file content containing merge conflict markers.
21
+ * Each hunk is bounded by <<<<<<< and >>>>>>>, with ======= separating ours/theirs.
22
+ *
23
+ * @param {string} content - File content with conflict markers
24
+ * @returns {Array<{start_line: number, end_line: number, ours: string, theirs: string}>}
25
+ */
26
+ function parseConflictHunks(content) {
27
+ const lines = content.split('\n');
28
+ const hunks = [];
29
+ let inConflict = false;
30
+ let inOurs = false;
31
+ let startLine = 0;
32
+ let oursLines = [];
33
+ let theirsLines = [];
34
+
35
+ for (let i = 0; i < lines.length; i++) {
36
+ const line = lines[i];
37
+ if (/^<{7}/.test(line)) {
38
+ inConflict = true;
39
+ inOurs = true;
40
+ startLine = i + 1; // 1-based
41
+ oursLines = [];
42
+ theirsLines = [];
43
+ } else if (inConflict && /^={7}/.test(line)) {
44
+ inOurs = false;
45
+ } else if (inConflict && /^>{7}/.test(line)) {
46
+ hunks.push({
47
+ start_line: startLine,
48
+ end_line: i + 1, // 1-based
49
+ ours: oursLines.join('\n'),
50
+ theirs: theirsLines.join('\n'),
51
+ });
52
+ inConflict = false;
53
+ inOurs = false;
54
+ } else if (inConflict) {
55
+ if (inOurs) {
56
+ oursLines.push(line);
57
+ } else {
58
+ theirsLines.push(line);
59
+ }
60
+ }
61
+ }
62
+
63
+ return hunks;
64
+ }
65
+
66
+ // ─── Classification Logic ───────────────────────────────────────────────────
67
+
68
+ /**
69
+ * Structural pattern detection for code lines.
70
+ * Matches function declarations, class declarations, imports, exports,
71
+ * require statements, and scope-defining punctuation.
72
+ *
73
+ * @param {string} line - Single line of code
74
+ * @returns {boolean}
75
+ */
76
+ function isStructuralLine(line) {
77
+ const trimmed = line.trim();
78
+ if (!trimmed) return false;
79
+
80
+ const structuralPatterns = [
81
+ /^\s*function\s+\w+/,
82
+ /^\s*class\s+\w+/,
83
+ /^\s*module\.exports/,
84
+ /^\s*require\s*\(/,
85
+ /^\s*import\s+/,
86
+ /^\s*export\s+/,
87
+ /^\s*const\s+\w+\s*=\s*require\s*\(/,
88
+ /^\s*[{}()[\]]\s*$/,
89
+ /^\s*[{}()[\]],?\s*$/,
90
+ ];
91
+
92
+ return structuralPatterns.some(p => p.test(line));
93
+ }
94
+
95
+ /**
96
+ * Check if every non-empty line of the shorter side appears in the longer side.
97
+ *
98
+ * @param {string[]} shorter - Lines from shorter side
99
+ * @param {string[]} longer - Lines from longer side
100
+ * @returns {boolean}
101
+ */
102
+ function isSupersetOf(shorter, longer) {
103
+ const longerSet = new Set(longer.map(l => l.trim()).filter(l => l));
104
+ return shorter.filter(l => l.trim()).every(l => longerSet.has(l.trim()));
105
+ }
106
+
107
+ /**
108
+ * Classify conflict hunks from a file's content.
109
+ *
110
+ * Pure function (no I/O) that takes a file's content (with conflict markers)
111
+ * and optional plan context (task descriptions that touched this file),
112
+ * and returns classified hunks.
113
+ *
114
+ * Classification types:
115
+ * - ADDITIVE: One side adds new code the other doesn't have
116
+ * - DELETION: One side removes lines the other keeps
117
+ * - DIVERGENT: Both sides modify the same lines differently
118
+ * - STRUCTURAL: Changes to function signatures, imports, exports, scope
119
+ *
120
+ * @param {string} fileContent - File content with conflict markers
121
+ * @param {Array<{task_name: string, action_excerpt: string}>|null} planContext - Optional plan context
122
+ * @returns {Array<{start_line: number, end_line: number, ours: string, theirs: string, type: string, confidence: string, ambiguous: boolean, reasoning: string}>}
123
+ */
124
+ function classifyConflictHunks(fileContent, planContext) {
125
+ const hunks = parseConflictHunks(fileContent);
126
+
127
+ return hunks.map(hunk => {
128
+ const oursLines = hunk.ours.split('\n').filter(l => l.trim());
129
+ const theirsLines = hunk.theirs.split('\n').filter(l => l.trim());
130
+ const oursEmpty = oursLines.length === 0;
131
+ const theirsEmpty = theirsLines.length === 0;
132
+
133
+ let type = 'DIVERGENT';
134
+ let confidence = 'LOW';
135
+ let ambiguous = false;
136
+ let reasoning = '';
137
+
138
+ // Check STRUCTURAL first — both sides have structural patterns
139
+ const oursStructural = oursLines.filter(isStructuralLine);
140
+ const theirsStructural = theirsLines.filter(isStructuralLine);
141
+
142
+ if (oursStructural.length > 0 && theirsStructural.length > 0) {
143
+ type = 'STRUCTURAL';
144
+ confidence = 'HIGH';
145
+ reasoning = 'Both sides modify structural elements (functions, imports, exports, or scope)';
146
+ } else if (oursStructural.length > 0 || theirsStructural.length > 0) {
147
+ // Only one side has structural changes
148
+ const structSide = oursStructural.length > 0 ? 'ours' : 'theirs';
149
+ type = 'STRUCTURAL';
150
+ confidence = 'MEDIUM';
151
+ reasoning = `Structural patterns detected on ${structSide} side`;
152
+ }
153
+ // Check ADDITIVE
154
+ else if (oursEmpty || theirsEmpty) {
155
+ if (oursEmpty && !theirsEmpty) {
156
+ type = 'ADDITIVE';
157
+ confidence = 'HIGH';
158
+ reasoning = 'Ours side is empty — theirs adds new content';
159
+ } else if (theirsEmpty && !oursEmpty) {
160
+ // Could be DELETION (theirs removes content) or ADDITIVE (ours adds content)
161
+ // Without merge-base context, classify as DELETION
162
+ type = 'DELETION';
163
+ confidence = 'HIGH';
164
+ reasoning = 'Theirs side is empty — content was removed on theirs branch';
165
+ }
166
+ }
167
+ // Check superset patterns
168
+ else if (isSupersetOf(oursLines, theirsLines)) {
169
+ type = 'ADDITIVE';
170
+ confidence = 'MEDIUM';
171
+ reasoning = 'Theirs is a superset of ours — theirs adds additional content';
172
+ } else if (isSupersetOf(theirsLines, oursLines)) {
173
+ type = 'ADDITIVE';
174
+ confidence = 'MEDIUM';
175
+ reasoning = 'Ours is a superset of theirs — ours adds additional content';
176
+ }
177
+ // Default: DIVERGENT
178
+ else {
179
+ type = 'DIVERGENT';
180
+ confidence = 'LOW';
181
+ reasoning = 'Both sides have content, neither is a superset of the other';
182
+ ambiguous = true;
183
+ }
184
+
185
+ // Enhance classification with plan context if available
186
+ if (planContext && Array.isArray(planContext) && planContext.length > 0) {
187
+ const contextText = planContext.map(c => (c.task_name || '') + ' ' + (c.action_excerpt || '')).join(' ').toLowerCase();
188
+
189
+ if (type === 'ADDITIVE' && (contextText.includes('add') || contextText.includes('new') || contextText.includes('create'))) {
190
+ confidence = 'HIGH';
191
+ reasoning += '. Plan context confirms additive intent';
192
+ }
193
+
194
+ if (type === 'DIVERGENT' && contextText.length > 0) {
195
+ confidence = 'MEDIUM';
196
+ reasoning += '. Plan context available for both changes';
197
+ ambiguous = true;
198
+ }
199
+ }
200
+
201
+ return {
202
+ start_line: hunk.start_line,
203
+ end_line: hunk.end_line,
204
+ ours: hunk.ours,
205
+ theirs: hunk.theirs,
206
+ type,
207
+ confidence,
208
+ ambiguous,
209
+ reasoning,
210
+ };
211
+ });
212
+ }
213
+
214
+ // ─── Conflict Detection ─────────────────────────────────────────────────────
215
+
216
+ /**
217
+ * Detect conflicted files after a failed merge.
218
+ * Parses git merge state and returns conflicted files grouped by repo
219
+ * with rich metadata.
220
+ *
221
+ * @param {string} cwd - Working directory
222
+ * @param {string|null} phaseDir - Optional phase directory for context
223
+ * @param {boolean} raw - Raw output mode
224
+ */
225
+ function cmdMergeConflictsDetect(cwd, phaseDir, raw) {
226
+ const timestamp = new Date().toISOString();
227
+
228
+ // Get conflicted files
229
+ const diffResult = execGit(cwd, ['diff', '--name-only', '--diff-filter=U']);
230
+ const conflictedFiles = diffResult.exitCode === 0 && diffResult.stdout
231
+ ? diffResult.stdout.split('\n').filter(f => f.trim())
232
+ : [];
233
+
234
+ if (conflictedFiles.length === 0) {
235
+ output({
236
+ conflicted: false,
237
+ files: [],
238
+ repos: {},
239
+ metadata: {
240
+ timestamp,
241
+ branch: null,
242
+ merge_head: null,
243
+ head_sha: null,
244
+ merge_base: null,
245
+ command_version: '1.0',
246
+ },
247
+ }, raw);
248
+ return;
249
+ }
250
+
251
+ // Get metadata
252
+ const branchResult = execGit(cwd, ['branch', '--show-current']);
253
+ const branch = branchResult.exitCode === 0 ? branchResult.stdout : null;
254
+
255
+ const mergeHeadResult = execGit(cwd, ['rev-parse', 'MERGE_HEAD']);
256
+ const mergeHead = mergeHeadResult.exitCode === 0 ? mergeHeadResult.stdout : null;
257
+
258
+ const headResult = execGit(cwd, ['rev-parse', 'HEAD']);
259
+ const headSha = headResult.exitCode === 0 ? headResult.stdout : null;
260
+
261
+ let mergeBase = null;
262
+ if (mergeHead) {
263
+ const baseResult = execGit(cwd, ['merge-base', 'HEAD', 'MERGE_HEAD']);
264
+ mergeBase = baseResult.exitCode === 0 ? baseResult.stdout : null;
265
+ }
266
+
267
+ // Parse REPOS.md for grouping
268
+ const reposData = parseReposMd(cwd);
269
+ const repos = reposData ? reposData.repos : [];
270
+
271
+ // Group files by repo
272
+ const grouped = {};
273
+
274
+ for (const filePath of conflictedFiles) {
275
+ let repoName = '_product';
276
+
277
+ // Check which repo owns this file by prefix matching
278
+ for (const repo of repos) {
279
+ const repoPath = repo.path.replace(/^\.\//, '').replace(/^\.\.\//, '');
280
+ if (filePath.startsWith(repoPath + '/')) {
281
+ repoName = repo.name;
282
+ break;
283
+ }
284
+ }
285
+
286
+ if (!grouped[repoName]) {
287
+ grouped[repoName] = { files: [], count: 0 };
288
+ }
289
+
290
+ // Count conflict markers in the file
291
+ let conflictMarkers = 0;
292
+ const fullPath = path.join(cwd, filePath);
293
+ const content = safeReadFile(fullPath);
294
+ if (content) {
295
+ const lines = content.split('\n');
296
+ conflictMarkers = lines.filter(l => /^<{7}/.test(l)).length;
297
+ }
298
+
299
+ // Determine path relative to repo
300
+ let relPath = filePath;
301
+ if (repoName !== '_product') {
302
+ const repo = repos.find(r => r.name === repoName);
303
+ if (repo) {
304
+ const repoPath = repo.path.replace(/^\.\//, '').replace(/^\.\.\//, '');
305
+ relPath = filePath.slice(repoPath.length + 1);
306
+ }
307
+ }
308
+
309
+ grouped[repoName].files.push({
310
+ path: relPath,
311
+ full_path: filePath,
312
+ conflict_markers: conflictMarkers,
313
+ });
314
+ grouped[repoName].count++;
315
+ }
316
+
317
+ output({
318
+ conflicted: true,
319
+ file_count: conflictedFiles.length,
320
+ repos: grouped,
321
+ metadata: {
322
+ timestamp,
323
+ branch,
324
+ merge_head: mergeHead,
325
+ head_sha: headSha,
326
+ merge_base: mergeBase,
327
+ command_version: '1.0',
328
+ },
329
+ }, raw);
330
+ }
331
+
332
+ // ─── Context Assembly ───────────────────────────────────────────────────────
333
+
334
+ /**
335
+ * Assemble resolution context for a specific conflicted file.
336
+ * Gathers conflict hunks, plan task matching, SUMMARY context, and branch identification.
337
+ *
338
+ * @param {string} cwd - Working directory
339
+ * @param {string} filePath - Path to conflicted file
340
+ * @param {string|null} phaseDir - Phase directory for plan/summary context
341
+ * @param {boolean} raw - Raw output mode
342
+ */
343
+ function cmdMergeConflictsContext(cwd, filePath, phaseDir, raw) {
344
+ const fullPath = path.isAbsolute(filePath) ? filePath : path.join(cwd, filePath);
345
+
346
+ // Validate file exists and has conflict markers
347
+ const fileContent = safeReadFile(fullPath);
348
+ if (!fileContent) {
349
+ error('File not found: ' + filePath);
350
+ }
351
+
352
+ if (!/^<{7}/m.test(fileContent)) {
353
+ error('File has no conflict markers: ' + filePath);
354
+ }
355
+
356
+ // Plan task matching
357
+ let planContext = [];
358
+ if (phaseDir) {
359
+ const absPhaseDir = path.isAbsolute(phaseDir) ? phaseDir : path.join(cwd, phaseDir);
360
+ try {
361
+ const phaseFiles = fs.readdirSync(absPhaseDir);
362
+ const planFiles = phaseFiles.filter(f => f.match(/\d+-PLAN\.md$/i) || f === 'PLAN.md');
363
+
364
+ for (const planFile of planFiles) {
365
+ const planContent = safeReadFile(path.join(absPhaseDir, planFile));
366
+ if (!planContent) continue;
367
+
368
+ const fm = extractFrontmatter(planContent);
369
+ const planNumber = fm.plan || planFile.replace(/-PLAN\.md$/i, '');
370
+
371
+ // Search for <files> tags in tasks and match against the conflicted file
372
+ const taskPattern = /<task\b[^>]*>([\s\S]*?)<\/task>/g;
373
+ let match;
374
+ while ((match = taskPattern.exec(planContent)) !== null) {
375
+ const taskBody = match[1];
376
+ const nameMatch = taskBody.match(/<name>([\s\S]*?)<\/name>/);
377
+ const filesMatch = taskBody.match(/<files>([\s\S]*?)<\/files>/);
378
+ const actionMatch = taskBody.match(/<action>([\s\S]*?)<\/action>/);
379
+
380
+ if (filesMatch) {
381
+ const filesList = filesMatch[1].split(/[\n,]/).map(f => f.trim()).filter(f => f);
382
+ const normalizedFilePath = filePath.replace(/^\.\//, '');
383
+
384
+ if (filesList.some(f => normalizedFilePath.includes(f.replace(/^\.\//, '')) || f.replace(/^\.\//, '').includes(normalizedFilePath))) {
385
+ const taskName = nameMatch ? nameMatch[1].trim() : 'unnamed';
386
+ const actionExcerpt = actionMatch ? actionMatch[1].trim().slice(0, 200) : '';
387
+ planContext.push({
388
+ plan: planNumber,
389
+ task_name: taskName,
390
+ action_excerpt: actionExcerpt,
391
+ });
392
+ }
393
+ }
394
+ }
395
+ }
396
+ } catch {
397
+ // Phase dir not readable — fall through to git fallback
398
+ }
399
+
400
+ // Fallback: git log commit matching
401
+ if (planContext.length === 0) {
402
+ const logResult = execGit(cwd, ['log', '--oneline', '--follow', '-10', '--', filePath]);
403
+ if (logResult.exitCode === 0 && logResult.stdout) {
404
+ const commits = logResult.stdout.split('\n').filter(l => l.trim());
405
+ for (const commit of commits) {
406
+ const planMatch = commit.match(/(?:feat|fix|test|refactor|chore)\((\d+-\d+)\)/);
407
+ if (planMatch) {
408
+ planContext.push({
409
+ plan: planMatch[1],
410
+ task_name: commit.replace(/^[a-f0-9]+\s+/, ''),
411
+ action_excerpt: '',
412
+ });
413
+ }
414
+ }
415
+ }
416
+ }
417
+ }
418
+
419
+ // Classify hunks
420
+ const hunks = classifyConflictHunks(fileContent, planContext.length > 0 ? planContext : null);
421
+
422
+ // SUMMARY.md context
423
+ let summaryContext = [];
424
+ if (phaseDir) {
425
+ const absPhaseDir = path.isAbsolute(phaseDir) ? phaseDir : path.join(cwd, phaseDir);
426
+ try {
427
+ const phaseFiles = fs.readdirSync(absPhaseDir);
428
+ const summaryFiles = phaseFiles.filter(f => f.match(/\d+-SUMMARY\.md$/i) || f === 'SUMMARY.md');
429
+
430
+ for (const summaryFile of summaryFiles) {
431
+ const summaryContent = safeReadFile(path.join(absPhaseDir, summaryFile));
432
+ if (!summaryContent) continue;
433
+
434
+ const fm = extractFrontmatter(summaryContent);
435
+
436
+ // Check if key-files mention this file
437
+ const keyFiles = fm['key-files'] || {};
438
+ const created = keyFiles.created || [];
439
+ const modified = keyFiles.modified || [];
440
+ const allKeyFiles = [...(Array.isArray(created) ? created : [created]), ...(Array.isArray(modified) ? modified : [modified])];
441
+ const normalizedFilePath = filePath.replace(/^\.\//, '');
442
+
443
+ if (allKeyFiles.some(f => f && (normalizedFilePath.includes(f.replace(/^\.\//, '')) || f.replace(/^\.\//, '').includes(normalizedFilePath)))) {
444
+ // Extract accomplishments section
445
+ const accomplishmentsMatch = summaryContent.match(/## Accomplishments\s*\n([\s\S]*?)(?=\n## |\n---)/);
446
+ const accomplishments = accomplishmentsMatch ? accomplishmentsMatch[1].trim() : '';
447
+
448
+ summaryContext.push({
449
+ plan: fm.plan || summaryFile.replace(/-SUMMARY\.md$/i, ''),
450
+ accomplishments,
451
+ });
452
+ }
453
+ }
454
+ } catch {
455
+ // Phase dir not readable
456
+ }
457
+ }
458
+
459
+ // Branch identification
460
+ const branchResult = execGit(cwd, ['branch', '--show-current']);
461
+ const oursBranch = branchResult.exitCode === 0 ? branchResult.stdout : null;
462
+
463
+ let theirsBranch = null;
464
+ const mergeHeadResult = execGit(cwd, ['log', '-1', '--format=%D', 'MERGE_HEAD']);
465
+ if (mergeHeadResult.exitCode === 0 && mergeHeadResult.stdout) {
466
+ theirsBranch = mergeHeadResult.stdout;
467
+ }
468
+
469
+ // Extract phase numbers from branch names
470
+ const phasePattern = /(?:dgs|gsd)\/.*phase-(\d+)/;
471
+ const oursPhase = oursBranch ? (oursBranch.match(phasePattern) || [])[1] || null : null;
472
+ const theirsPhase = theirsBranch ? (theirsBranch.match(phasePattern) || [])[1] || null : null;
473
+
474
+ // Merge base
475
+ let mergeBase = null;
476
+ const mergeHeadShaResult = execGit(cwd, ['rev-parse', 'MERGE_HEAD']);
477
+ if (mergeHeadShaResult.exitCode === 0) {
478
+ const baseResult = execGit(cwd, ['merge-base', 'HEAD', 'MERGE_HEAD']);
479
+ mergeBase = baseResult.exitCode === 0 ? baseResult.stdout : null;
480
+ }
481
+
482
+ output({
483
+ file: filePath,
484
+ hunks,
485
+ plan_context: planContext,
486
+ summary_context: summaryContext,
487
+ branches: {
488
+ ours: { name: oursBranch, phase: oursPhase },
489
+ theirs: { name: theirsBranch, phase: theirsPhase },
490
+ },
491
+ metadata: {
492
+ timestamp: new Date().toISOString(),
493
+ merge_base: mergeBase,
494
+ },
495
+ }, raw);
496
+ }
497
+
498
+ // ─── Resolution Recording ───────────────────────────────────────────────────
499
+
500
+ /**
501
+ * Record a resolution outcome to RESOLUTIONS.md in the phase directory.
502
+ *
503
+ * @param {string} cwd - Working directory
504
+ * @param {string} filePath - Path to the resolved file
505
+ * @param {string} phaseDir - Phase directory for RESOLUTIONS.md
506
+ * @param {string} resolution - JSON string with strategy, description, confidence
507
+ * @param {boolean} raw - Raw output mode
508
+ */
509
+ function cmdMergeConflictsResolved(cwd, filePath, phaseDir, resolution, raw) {
510
+ if (!filePath) error('File path required');
511
+ if (!phaseDir) error('Phase directory required (--phase-dir)');
512
+ if (!resolution) error('Resolution JSON required (--resolution)');
513
+
514
+ // Parse resolution JSON
515
+ let res;
516
+ try {
517
+ res = JSON.parse(resolution);
518
+ } catch {
519
+ error('Invalid resolution JSON. Expected: {"strategy":"ours|theirs|manual|combined","description":"...","confidence":"HIGH|MEDIUM|LOW"}');
520
+ }
521
+
522
+ const validStrategies = ['ours', 'theirs', 'manual', 'combined'];
523
+ if (!res.strategy || !validStrategies.includes(res.strategy)) {
524
+ error('Invalid strategy. Must be one of: ' + validStrategies.join(', '));
525
+ }
526
+
527
+ const validConfidences = ['HIGH', 'MEDIUM', 'LOW'];
528
+ if (!res.confidence || !validConfidences.includes(res.confidence)) {
529
+ error('Invalid confidence. Must be one of: ' + validConfidences.join(', '));
530
+ }
531
+
532
+ // Verify the file no longer has conflict markers
533
+ const fullPath = path.isAbsolute(filePath) ? filePath : path.join(cwd, filePath);
534
+ const fileContent = safeReadFile(fullPath);
535
+ if (fileContent && /^<{7}/m.test(fileContent)) {
536
+ error('File still has conflict markers. Resolve before recording.');
537
+ }
538
+
539
+ const timestamp = new Date().toISOString();
540
+ const absPhaseDir = path.isAbsolute(phaseDir) ? phaseDir : path.join(cwd, phaseDir);
541
+ const resolutionsPath = path.join(absPhaseDir, 'RESOLUTIONS.md');
542
+
543
+ // Create or append to RESOLUTIONS.md
544
+ let existing = safeReadFile(resolutionsPath);
545
+ if (!existing) {
546
+ // Extract phase name from directory
547
+ const phaseName = path.basename(absPhaseDir);
548
+ existing = `# Merge Conflict Resolutions\n\n**Phase:** ${phaseName}\n**Created:** ${timestamp}\n\n---\n\n`;
549
+ }
550
+
551
+ // Append resolution entry
552
+ const entry = [
553
+ `### ${filePath}\n`,
554
+ `- **Strategy:** ${res.strategy}`,
555
+ `- **Confidence:** ${res.confidence}`,
556
+ `- **Description:** ${res.description || 'No description provided'}`,
557
+ `- **Resolved:** ${timestamp}`,
558
+ '',
559
+ ].join('\n');
560
+
561
+ const newContent = existing + entry + '\n';
562
+ fs.writeFileSync(resolutionsPath, newContent, 'utf-8');
563
+
564
+ output({
565
+ recorded: true,
566
+ file: filePath,
567
+ resolutions_path: resolutionsPath,
568
+ }, raw);
569
+ }
570
+
571
+ // ─── Resolution Summary ─────────────────────────────────────────────────────
572
+
573
+ /**
574
+ * Aggregate all per-file resolutions from RESOLUTIONS.md into a merge-level report.
575
+ *
576
+ * @param {string} cwd - Working directory
577
+ * @param {string} phaseDir - Phase directory containing RESOLUTIONS.md
578
+ * @param {boolean} raw - Raw output mode
579
+ */
580
+ function cmdMergeConflictsSummary(cwd, phaseDir, raw) {
581
+ if (!phaseDir) error('Phase directory required (--phase-dir)');
582
+
583
+ const absPhaseDir = path.isAbsolute(phaseDir) ? phaseDir : path.join(cwd, phaseDir);
584
+ const resolutionsPath = path.join(absPhaseDir, 'RESOLUTIONS.md');
585
+ const content = safeReadFile(resolutionsPath);
586
+
587
+ if (!content) {
588
+ output({ found: false, resolutions: [], summary: null }, raw, '');
589
+ return;
590
+ }
591
+
592
+ // Parse resolution entries — each is a ### <filepath> section with bullet fields
593
+ const entries = [];
594
+ const entryPattern = /### (.+)\n([\s\S]*?)(?=\n### |\n*$)/g;
595
+ let match;
596
+
597
+ while ((match = entryPattern.exec(content)) !== null) {
598
+ const file = match[1].trim();
599
+ const body = match[2];
600
+
601
+ const strategyMatch = body.match(/\*\*Strategy:\*\*\s*(\w+)/);
602
+ const confidenceMatch = body.match(/\*\*Confidence:\*\*\s*(\w+)/);
603
+ const descriptionMatch = body.match(/\*\*Description:\*\*\s*(.+)/);
604
+ const timestampMatch = body.match(/\*\*Resolved:\*\*\s*(.+)/);
605
+
606
+ entries.push({
607
+ file,
608
+ strategy: strategyMatch ? strategyMatch[1] : 'unknown',
609
+ confidence: confidenceMatch ? confidenceMatch[1] : 'unknown',
610
+ description: descriptionMatch ? descriptionMatch[1].trim() : '',
611
+ timestamp: timestampMatch ? timestampMatch[1].trim() : '',
612
+ });
613
+ }
614
+
615
+ // Aggregate
616
+ const byStrategy = { ours: 0, theirs: 0, manual: 0, combined: 0 };
617
+ const byConfidence = { HIGH: 0, MEDIUM: 0, LOW: 0 };
618
+
619
+ for (const entry of entries) {
620
+ if (byStrategy[entry.strategy] !== undefined) {
621
+ byStrategy[entry.strategy]++;
622
+ }
623
+ if (byConfidence[entry.confidence] !== undefined) {
624
+ byConfidence[entry.confidence]++;
625
+ }
626
+ }
627
+
628
+ output({
629
+ found: true,
630
+ total_resolved: entries.length,
631
+ by_strategy: byStrategy,
632
+ by_confidence: byConfidence,
633
+ resolutions: entries.map(e => ({
634
+ file: e.file,
635
+ strategy: e.strategy,
636
+ confidence: e.confidence,
637
+ timestamp: e.timestamp,
638
+ })),
639
+ metadata: {
640
+ phase_dir: phaseDir,
641
+ timestamp: new Date().toISOString(),
642
+ },
643
+ }, raw);
644
+ }
645
+
646
+ // ─── Exports ────────────────────────────────────────────────────────────────
647
+
648
+ module.exports = {
649
+ cmdMergeConflictsDetect,
650
+ cmdMergeConflictsContext,
651
+ cmdMergeConflictsResolved,
652
+ cmdMergeConflictsSummary,
653
+ classifyConflictHunks,
654
+ };