@luanpdd/kit-mcp 1.18.0 → 1.20.0

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 (232) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +648 -648
  3. package/kit/COMANDOS.md +138 -138
  4. package/kit/README.md +52 -52
  5. package/kit/agents/advisor-researcher.md +106 -106
  6. package/kit/agents/assumptions-analyzer.md +107 -107
  7. package/kit/agents/codebase-mapper.md +768 -768
  8. package/kit/agents/debugger.md +772 -772
  9. package/kit/agents/example-reviewer.md +21 -21
  10. package/kit/agents/executor.md +523 -523
  11. package/kit/agents/integration-checker.md +200 -200
  12. package/kit/agents/nyquist-auditor.md +178 -178
  13. package/kit/agents/phase-researcher.md +696 -696
  14. package/kit/agents/plan-checker.md +272 -272
  15. package/kit/agents/planner.md +891 -891
  16. package/kit/agents/project-researcher.md +652 -652
  17. package/kit/agents/research-synthesizer.md +245 -245
  18. package/kit/agents/roadmapper.md +677 -677
  19. package/kit/agents/ui-auditor.md +437 -437
  20. package/kit/agents/ui-checker.md +302 -302
  21. package/kit/agents/ui-researcher.md +355 -355
  22. package/kit/agents/user-profiler.md +175 -175
  23. package/kit/agents/verifier.md +728 -728
  24. package/kit/commands/adicionar-backlog.md +75 -75
  25. package/kit/commands/adicionar-fase.md +42 -42
  26. package/kit/commands/adicionar-tarefa.md +45 -45
  27. package/kit/commands/adicionar-testes.md +41 -41
  28. package/kit/commands/ajuda.md +21 -21
  29. package/kit/commands/atualizar.md +37 -37
  30. package/kit/commands/auditar-marco.md +179 -179
  31. package/kit/commands/auditar-uat.md +23 -23
  32. package/kit/commands/autonomo.md +40 -40
  33. package/kit/commands/branch-pr.md +24 -24
  34. package/kit/commands/burn-rate-status.md +338 -70
  35. package/kit/commands/concluir-marco.md +247 -247
  36. package/kit/commands/configuracoes.md +36 -36
  37. package/kit/commands/definir-perfil.md +10 -10
  38. package/kit/commands/depurar.md +190 -190
  39. package/kit/commands/discutir-fase.md +131 -131
  40. package/kit/commands/entrar-discord.md +17 -17
  41. package/kit/commands/estatisticas.md +18 -18
  42. package/kit/commands/example-greeting.md +33 -33
  43. package/kit/commands/executar-fase.md +58 -58
  44. package/kit/commands/expresso.md +56 -56
  45. package/kit/commands/fase-ui.md +34 -34
  46. package/kit/commands/fazer.md +57 -57
  47. package/kit/commands/fio.md +125 -125
  48. package/kit/commands/fluxos-trabalho.md +64 -64
  49. package/kit/commands/forense.md +176 -176
  50. package/kit/commands/gerenciador.md +38 -38
  51. package/kit/commands/inserir-fase.md +31 -31
  52. package/kit/commands/limpeza.md +17 -17
  53. package/kit/commands/listar-hipoteses-fase.md +45 -45
  54. package/kit/commands/listar-workspaces.md +18 -18
  55. package/kit/commands/mapear-codebase.md +70 -70
  56. package/kit/commands/nota.md +33 -33
  57. package/kit/commands/novo-marco.md +43 -43
  58. package/kit/commands/novo-projeto.md +41 -41
  59. package/kit/commands/novo-workspace.md +43 -43
  60. package/kit/commands/pausar-trabalho.md +37 -37
  61. package/kit/commands/perfil-usuario.md +45 -45
  62. package/kit/commands/pesquisar-fase.md +195 -195
  63. package/kit/commands/planejar-fase.md +67 -67
  64. package/kit/commands/planejar-lacunas.md +33 -33
  65. package/kit/commands/plantar-ideia.md +25 -25
  66. package/kit/commands/progresso.md +24 -24
  67. package/kit/commands/proximo.md +30 -30
  68. package/kit/commands/publicar.md +490 -490
  69. package/kit/commands/rapido.md +35 -35
  70. package/kit/commands/reaplicar-patches.md +124 -124
  71. package/kit/commands/relatorio-sessao.md +19 -19
  72. package/kit/commands/remover-fase.md +31 -31
  73. package/kit/commands/remover-workspace.md +26 -26
  74. package/kit/commands/resumo-marco.md +50 -50
  75. package/kit/commands/retomar-trabalho.md +40 -40
  76. package/kit/commands/revisar-backlog.md +60 -60
  77. package/kit/commands/revisar-ui.md +32 -32
  78. package/kit/commands/revisar.md +37 -37
  79. package/kit/commands/saude.md +21 -21
  80. package/kit/commands/setup-notion.md +93 -93
  81. package/kit/commands/sync-main.md +68 -68
  82. package/kit/commands/validar-fase.md +35 -35
  83. package/kit/commands/verificar-tarefas.md +44 -44
  84. package/kit/commands/verificar-trabalho.md +64 -64
  85. package/kit/file-manifest.json +3 -3
  86. package/kit/framework/bin/lib/commands.cjs +959 -959
  87. package/kit/framework/bin/lib/config.cjs +442 -442
  88. package/kit/framework/bin/lib/core.cjs +1230 -1230
  89. package/kit/framework/bin/lib/frontmatter.cjs +336 -336
  90. package/kit/framework/bin/lib/init.cjs +1442 -1442
  91. package/kit/framework/bin/lib/milestone.cjs +252 -252
  92. package/kit/framework/bin/lib/model-profiles.cjs +68 -68
  93. package/kit/framework/bin/lib/phase.cjs +888 -888
  94. package/kit/framework/bin/lib/profile-output.cjs +952 -952
  95. package/kit/framework/bin/lib/profile-pipeline.cjs +539 -539
  96. package/kit/framework/bin/lib/roadmap.cjs +329 -329
  97. package/kit/framework/bin/lib/security.cjs +382 -382
  98. package/kit/framework/bin/lib/state.cjs +1031 -1031
  99. package/kit/framework/bin/lib/template.cjs +222 -222
  100. package/kit/framework/bin/lib/uat.cjs +282 -282
  101. package/kit/framework/bin/lib/verify.cjs +888 -888
  102. package/kit/framework/bin/lib/workstream.cjs +491 -491
  103. package/kit/framework/bin/tools.cjs +918 -918
  104. package/kit/framework/commands/workstreams.md +63 -63
  105. package/kit/framework/references/checkpoints.md +778 -778
  106. package/kit/framework/references/continuation-format.md +249 -249
  107. package/kit/framework/references/decimal-phase-calculation.md +64 -64
  108. package/kit/framework/references/git-integration.md +295 -295
  109. package/kit/framework/references/git-planning-commit.md +38 -38
  110. package/kit/framework/references/model-profile-resolution.md +36 -36
  111. package/kit/framework/references/model-profiles.md +139 -139
  112. package/kit/framework/references/phase-argument-parsing.md +61 -61
  113. package/kit/framework/references/planning-config.md +202 -202
  114. package/kit/framework/references/questioning.md +162 -162
  115. package/kit/framework/references/tdd.md +263 -263
  116. package/kit/framework/references/ui-brand.md +160 -160
  117. package/kit/framework/references/user-profiling.md +657 -657
  118. package/kit/framework/references/verification-patterns.md +612 -612
  119. package/kit/framework/references/workstream-flag.md +58 -58
  120. package/kit/framework/templates/DEBUG.md +164 -164
  121. package/kit/framework/templates/UAT.md +265 -265
  122. package/kit/framework/templates/UI-SPEC.md +100 -100
  123. package/kit/framework/templates/VALIDATION.md +76 -76
  124. package/kit/framework/templates/claude-md.md +122 -122
  125. package/kit/framework/templates/codebase/architecture.md +185 -185
  126. package/kit/framework/templates/codebase/concerns.md +205 -205
  127. package/kit/framework/templates/codebase/conventions.md +204 -204
  128. package/kit/framework/templates/codebase/integrations.md +192 -192
  129. package/kit/framework/templates/codebase/stack.md +158 -158
  130. package/kit/framework/templates/codebase/structure.md +199 -199
  131. package/kit/framework/templates/codebase/testing.md +301 -301
  132. package/kit/framework/templates/config.json +44 -44
  133. package/kit/framework/templates/context.md +352 -352
  134. package/kit/framework/templates/continue-here.md +78 -78
  135. package/kit/framework/templates/copilot-instructions.md +7 -7
  136. package/kit/framework/templates/debug-subagent-prompt.md +91 -91
  137. package/kit/framework/templates/dev-preferences.md +20 -20
  138. package/kit/framework/templates/discovery.md +146 -146
  139. package/kit/framework/templates/discussion-log.md +63 -63
  140. package/kit/framework/templates/milestone-archive.md +123 -123
  141. package/kit/framework/templates/milestone.md +115 -115
  142. package/kit/framework/templates/phase-prompt.md +610 -610
  143. package/kit/framework/templates/planner-subagent-prompt.md +117 -117
  144. package/kit/framework/templates/project.md +186 -186
  145. package/kit/framework/templates/requirements.md +231 -231
  146. package/kit/framework/templates/research-project/ARCHITECTURE.md +204 -204
  147. package/kit/framework/templates/research-project/FEATURES.md +147 -147
  148. package/kit/framework/templates/research-project/PITFALLS.md +200 -200
  149. package/kit/framework/templates/research-project/STACK.md +120 -120
  150. package/kit/framework/templates/research-project/SUMMARY.md +170 -170
  151. package/kit/framework/templates/research.md +419 -419
  152. package/kit/framework/templates/retrospective.md +54 -54
  153. package/kit/framework/templates/roadmap.md +202 -202
  154. package/kit/framework/templates/state.md +176 -176
  155. package/kit/framework/templates/summary-complex.md +59 -59
  156. package/kit/framework/templates/summary-minimal.md +41 -41
  157. package/kit/framework/templates/summary-standard.md +48 -48
  158. package/kit/framework/templates/summary.md +209 -209
  159. package/kit/framework/templates/user-profile.md +146 -146
  160. package/kit/framework/templates/user-setup.md +256 -256
  161. package/kit/framework/templates/verification-report.md +258 -258
  162. package/kit/framework/workflows/add-phase.md +112 -112
  163. package/kit/framework/workflows/add-tests.md +351 -351
  164. package/kit/framework/workflows/add-todo.md +158 -158
  165. package/kit/framework/workflows/audit-milestone.md +340 -340
  166. package/kit/framework/workflows/audit-uat.md +109 -109
  167. package/kit/framework/workflows/autonomous.md +891 -891
  168. package/kit/framework/workflows/check-todos.md +177 -177
  169. package/kit/framework/workflows/cleanup.md +152 -152
  170. package/kit/framework/workflows/complete-milestone.md +696 -696
  171. package/kit/framework/workflows/diagnose-issues.md +231 -231
  172. package/kit/framework/workflows/discovery-phase.md +289 -289
  173. package/kit/framework/workflows/discuss-phase-assumptions.md +653 -653
  174. package/kit/framework/workflows/discuss-phase.md +784 -784
  175. package/kit/framework/workflows/do.md +104 -104
  176. package/kit/framework/workflows/execute-phase.md +838 -838
  177. package/kit/framework/workflows/execute-plan.md +510 -510
  178. package/kit/framework/workflows/fast.md +102 -102
  179. package/kit/framework/workflows/forensics.md +265 -265
  180. package/kit/framework/workflows/health.md +181 -181
  181. package/kit/framework/workflows/help.md +619 -619
  182. package/kit/framework/workflows/insert-phase.md +130 -130
  183. package/kit/framework/workflows/list-phase-assumptions.md +178 -178
  184. package/kit/framework/workflows/list-workspaces.md +56 -56
  185. package/kit/framework/workflows/manager.md +362 -362
  186. package/kit/framework/workflows/map-codebase.md +377 -377
  187. package/kit/framework/workflows/milestone-summary.md +223 -223
  188. package/kit/framework/workflows/new-milestone.md +486 -486
  189. package/kit/framework/workflows/new-project.md +1159 -1159
  190. package/kit/framework/workflows/new-workspace.md +237 -237
  191. package/kit/framework/workflows/next.md +97 -97
  192. package/kit/framework/workflows/node-repair.md +92 -92
  193. package/kit/framework/workflows/note.md +156 -156
  194. package/kit/framework/workflows/pause-work.md +176 -176
  195. package/kit/framework/workflows/plan-milestone-gaps.md +273 -273
  196. package/kit/framework/workflows/plan-phase.md +765 -765
  197. package/kit/framework/workflows/plant-seed.md +169 -169
  198. package/kit/framework/workflows/pr-branch.md +129 -129
  199. package/kit/framework/workflows/profile-user.md +450 -450
  200. package/kit/framework/workflows/progress.md +507 -507
  201. package/kit/framework/workflows/quick.md +757 -757
  202. package/kit/framework/workflows/remove-phase.md +155 -155
  203. package/kit/framework/workflows/remove-workspace.md +90 -90
  204. package/kit/framework/workflows/research-phase.md +82 -82
  205. package/kit/framework/workflows/resume-project.md +326 -326
  206. package/kit/framework/workflows/review.md +228 -228
  207. package/kit/framework/workflows/session-report.md +146 -146
  208. package/kit/framework/workflows/settings.md +283 -283
  209. package/kit/framework/workflows/ship.md +228 -228
  210. package/kit/framework/workflows/stats.md +60 -60
  211. package/kit/framework/workflows/transition.md +671 -671
  212. package/kit/framework/workflows/ui-phase.md +302 -302
  213. package/kit/framework/workflows/ui-review.md +165 -165
  214. package/kit/framework/workflows/update.md +323 -323
  215. package/kit/framework/workflows/validate-phase.md +174 -174
  216. package/kit/framework/workflows/verify-phase.md +252 -252
  217. package/kit/framework/workflows/verify-work.md +637 -637
  218. package/kit/hooks/check-update.js +118 -118
  219. package/kit/hooks/context-monitor.js +163 -163
  220. package/kit/hooks/prompt-guard.js +103 -103
  221. package/kit/hooks/statusline.js +125 -125
  222. package/kit/hooks/workflow-guard.js +101 -101
  223. package/kit/settings.json +45 -45
  224. package/kit/skills/example-skill/SKILL.md +42 -42
  225. package/package.json +63 -59
  226. package/src/core/kit.js +216 -216
  227. package/src/core/metrics.js +135 -10
  228. package/src/core/reflect.js +247 -247
  229. package/src/core/reverse-sync.js +372 -372
  230. package/src/core/sync.js +418 -418
  231. package/src/core/watch.js +121 -121
  232. package/src/mcp-server/index.js +34 -3
@@ -1,329 +1,329 @@
1
- /**
2
- * Roadmap — Roadmap parsing and update operations
3
- */
4
-
5
- const fs = require('fs');
6
- const path = require('path');
7
- const { escapeRegex, normalizePhaseName, planningPaths, output, error, findPhaseInternal, stripShippedMilestones, extractCurrentMilestone, replaceInCurrentMilestone } = require('./core.cjs');
8
-
9
- function cmdRoadmapGetPhase(cwd, phaseNum, raw) {
10
- const roadmapPath = planningPaths(cwd).roadmap;
11
-
12
- if (!fs.existsSync(roadmapPath)) {
13
- output({ found: false, error: 'ROADMAP.md not found' }, raw, '');
14
- return;
15
- }
16
-
17
- try {
18
- const content = extractCurrentMilestone(fs.readFileSync(roadmapPath, 'utf-8'), cwd);
19
-
20
- // Escape special regex chars in phase number, handle decimal
21
- const escapedPhase = escapeRegex(phaseNum);
22
-
23
- // Match "## Phase X:", "### Phase X:", or "#### Phase X:" with optional name
24
- const phasePattern = new RegExp(
25
- `#{2,4}\\s*Phase\\s+${escapedPhase}:\\s*([^\\n]+)`,
26
- 'i'
27
- );
28
- const headerMatch = content.match(phasePattern);
29
-
30
- if (!headerMatch) {
31
- // Fallback: check if phase exists in summary list but missing detail section
32
- const checklistPattern = new RegExp(
33
- `-\\s*\\[[ x]\\]\\s*\\*\\*Phase\\s+${escapedPhase}:\\s*([^*]+)\\*\\*`,
34
- 'i'
35
- );
36
- const checklistMatch = content.match(checklistPattern);
37
-
38
- if (checklistMatch) {
39
- // Phase exists in summary but missing detail section - malformed ROADMAP
40
- output({
41
- found: false,
42
- phase_number: phaseNum,
43
- phase_name: checklistMatch[1].trim(),
44
- error: 'malformed_roadmap',
45
- message: `Phase ${phaseNum} exists in summary list but missing "### Phase ${phaseNum}:" detail section. ROADMAP.md needs both formats.`
46
- }, raw, '');
47
- return;
48
- }
49
-
50
- output({ found: false, phase_number: phaseNum }, raw, '');
51
- return;
52
- }
53
-
54
- const phaseName = headerMatch[1].trim();
55
- const headerIndex = headerMatch.index;
56
-
57
- // Find the end of this section (next ## or ### phase header, or end of file)
58
- const restOfContent = content.slice(headerIndex);
59
- const nextHeaderMatch = restOfContent.match(/\n#{2,4}\s+Phase\s+\d/i);
60
- const sectionEnd = nextHeaderMatch
61
- ? headerIndex + nextHeaderMatch.index
62
- : content.length;
63
-
64
- const section = content.slice(headerIndex, sectionEnd).trim();
65
-
66
- // Extract goal if present (supports both **Goal:** and **Goal**: formats)
67
- const goalMatch = section.match(/\*\*Goal(?::\*\*|\*\*:)\s*([^\n]+)/i);
68
- const goal = goalMatch ? goalMatch[1].trim() : null;
69
-
70
- // Extract success criteria as structured array
71
- const criteriaMatch = section.match(/\*\*Success Criteria\*\*[^\n]*:\s*\n((?:\s*\d+\.\s*[^\n]+\n?)+)/i);
72
- const success_criteria = criteriaMatch
73
- ? criteriaMatch[1].trim().split('\n').map(line => line.replace(/^\s*\d+\.\s*/, '').trim()).filter(Boolean)
74
- : [];
75
-
76
- output(
77
- {
78
- found: true,
79
- phase_number: phaseNum,
80
- phase_name: phaseName,
81
- goal,
82
- success_criteria,
83
- section,
84
- },
85
- raw,
86
- section
87
- );
88
- } catch (e) {
89
- error('Failed to read ROADMAP.md: ' + e.message);
90
- }
91
- }
92
-
93
- function cmdRoadmapAnalyze(cwd, raw) {
94
- const roadmapPath = planningPaths(cwd).roadmap;
95
-
96
- if (!fs.existsSync(roadmapPath)) {
97
- output({ error: 'ROADMAP.md not found', milestones: [], phases: [], current_phase: null }, raw);
98
- return;
99
- }
100
-
101
- const rawContent = fs.readFileSync(roadmapPath, 'utf-8');
102
- const content = extractCurrentMilestone(rawContent, cwd);
103
- const phasesDir = planningPaths(cwd).phases;
104
-
105
- // Extract all phase headings: ## Phase N: Name or ### Phase N: Name
106
- const phasePattern = /#{2,4}\s*Phase\s+(\d+[A-Z]?(?:\.\d+)*)\s*:\s*([^\n]+)/gi;
107
- const phases = [];
108
- let match;
109
-
110
- while ((match = phasePattern.exec(content)) !== null) {
111
- const phaseNum = match[1];
112
- const phaseName = match[2].replace(/\(INSERTED\)/i, '').trim();
113
-
114
- // Extract goal from the section
115
- const sectionStart = match.index;
116
- const restOfContent = content.slice(sectionStart);
117
- const nextHeader = restOfContent.match(/\n#{2,4}\s+Phase\s+\d/i);
118
- const sectionEnd = nextHeader ? sectionStart + nextHeader.index : content.length;
119
- const section = content.slice(sectionStart, sectionEnd);
120
-
121
- const goalMatch = section.match(/\*\*Goal(?::\*\*|\*\*:)\s*([^\n]+)/i);
122
- const goal = goalMatch ? goalMatch[1].trim() : null;
123
-
124
- const dependsMatch = section.match(/\*\*Depends on(?::\*\*|\*\*:)\s*([^\n]+)/i);
125
- const depends_on = dependsMatch ? dependsMatch[1].trim() : null;
126
-
127
- // Check completion on disk
128
- const normalized = normalizePhaseName(phaseNum);
129
- let diskStatus = 'no_directory';
130
- let planCount = 0;
131
- let summaryCount = 0;
132
- let hasContext = false;
133
- let hasResearch = false;
134
-
135
- try {
136
- const entries = fs.readdirSync(phasesDir, { withFileTypes: true });
137
- const dirs = entries.filter(e => e.isDirectory()).map(e => e.name);
138
- const dirMatch = dirs.find(d => d.startsWith(normalized + '-') || d === normalized);
139
-
140
- if (dirMatch) {
141
- const phaseFiles = fs.readdirSync(path.join(phasesDir, dirMatch));
142
- planCount = phaseFiles.filter(f => f.endsWith('-PLAN.md') || f === 'PLAN.md').length;
143
- summaryCount = phaseFiles.filter(f => f.endsWith('-SUMMARY.md') || f === 'SUMMARY.md').length;
144
- hasContext = phaseFiles.some(f => f.endsWith('-CONTEXT.md') || f === 'CONTEXT.md');
145
- hasResearch = phaseFiles.some(f => f.endsWith('-RESEARCH.md') || f === 'RESEARCH.md');
146
-
147
- if (summaryCount >= planCount && planCount > 0) diskStatus = 'complete';
148
- else if (summaryCount > 0) diskStatus = 'partial';
149
- else if (planCount > 0) diskStatus = 'planned';
150
- else if (hasResearch) diskStatus = 'researched';
151
- else if (hasContext) diskStatus = 'discussed';
152
- else diskStatus = 'empty';
153
- }
154
- } catch { /* intentionally empty */ }
155
-
156
- // Check ROADMAP checkbox status
157
- const checkboxPattern = new RegExp(`-\\s*\\[(x| )\\]\\s*.*Phase\\s+${escapeRegex(phaseNum)}[:\\s]`, 'i');
158
- const checkboxMatch = content.match(checkboxPattern);
159
- const roadmapComplete = checkboxMatch ? checkboxMatch[1] === 'x' : false;
160
-
161
- // If roadmap marks phase complete, trust that over disk file structure.
162
- // Phases completed before framework tracking (or via external tools) may lack
163
- // the standard PLAN/SUMMARY pairs but are still done.
164
- if (roadmapComplete && diskStatus !== 'complete') {
165
- diskStatus = 'complete';
166
- }
167
-
168
- phases.push({
169
- number: phaseNum,
170
- name: phaseName,
171
- goal,
172
- depends_on,
173
- plan_count: planCount,
174
- summary_count: summaryCount,
175
- has_context: hasContext,
176
- has_research: hasResearch,
177
- disk_status: diskStatus,
178
- roadmap_complete: roadmapComplete,
179
- });
180
- }
181
-
182
- // Extract milestone info
183
- const milestones = [];
184
- const milestonePattern = /##\s*(.*v(\d+(?:\.\d+)+)[^(\n]*)/gi;
185
- let mMatch;
186
- while ((mMatch = milestonePattern.exec(content)) !== null) {
187
- milestones.push({
188
- heading: mMatch[1].trim(),
189
- version: 'v' + mMatch[2],
190
- });
191
- }
192
-
193
- // Find current and next phase
194
- const currentPhase = phases.find(p => p.disk_status === 'planned' || p.disk_status === 'partial') || null;
195
- const nextPhase = phases.find(p => p.disk_status === 'empty' || p.disk_status === 'no_directory' || p.disk_status === 'discussed' || p.disk_status === 'researched') || null;
196
-
197
- // Aggregated stats
198
- const totalPlans = phases.reduce((sum, p) => sum + p.plan_count, 0);
199
- const totalSummaries = phases.reduce((sum, p) => sum + p.summary_count, 0);
200
- const completedPhases = phases.filter(p => p.disk_status === 'complete').length;
201
-
202
- // Detect phases in summary list without detail sections (malformed ROADMAP)
203
- const checklistPattern = /-\s*\[[ x]\]\s*\*\*Phase\s+(\d+[A-Z]?(?:\.\d+)*)/gi;
204
- const checklistPhases = new Set();
205
- let checklistMatch;
206
- while ((checklistMatch = checklistPattern.exec(content)) !== null) {
207
- checklistPhases.add(checklistMatch[1]);
208
- }
209
- const detailPhases = new Set(phases.map(p => p.number));
210
- const missingDetails = [...checklistPhases].filter(p => !detailPhases.has(p));
211
-
212
- const result = {
213
- milestones,
214
- phases,
215
- phase_count: phases.length,
216
- completed_phases: completedPhases,
217
- total_plans: totalPlans,
218
- total_summaries: totalSummaries,
219
- progress_percent: totalPlans > 0 ? Math.min(100, Math.round((totalSummaries / totalPlans) * 100)) : 0,
220
- current_phase: currentPhase ? currentPhase.number : null,
221
- next_phase: nextPhase ? nextPhase.number : null,
222
- missing_phase_details: missingDetails.length > 0 ? missingDetails : null,
223
- };
224
-
225
- output(result, raw);
226
- }
227
-
228
- function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
229
- if (!phaseNum) {
230
- error('phase number required for roadmap update-plan-progress');
231
- }
232
-
233
- const roadmapPath = planningPaths(cwd).roadmap;
234
-
235
- const phaseInfo = findPhaseInternal(cwd, phaseNum);
236
- if (!phaseInfo) {
237
- error(`Phase ${phaseNum} not found`);
238
- }
239
-
240
- const planCount = phaseInfo.plans.length;
241
- const summaryCount = phaseInfo.summaries.length;
242
-
243
- if (planCount === 0) {
244
- output({ updated: false, reason: 'No plans found', plan_count: 0, summary_count: 0 }, raw, 'no plans');
245
- return;
246
- }
247
-
248
- const isComplete = summaryCount >= planCount;
249
- const status = isComplete ? 'Complete' : summaryCount > 0 ? 'In Progress' : 'Planned';
250
- const today = new Date().toISOString().split('T')[0];
251
-
252
- if (!fs.existsSync(roadmapPath)) {
253
- output({ updated: false, reason: 'ROADMAP.md not found', plan_count: planCount, summary_count: summaryCount }, raw, 'no roadmap');
254
- return;
255
- }
256
-
257
- let roadmapContent = fs.readFileSync(roadmapPath, 'utf-8');
258
- const phaseEscaped = escapeRegex(phaseNum);
259
-
260
- // Progress table row: update Plans/Status/Date columns (handles 4 or 5 column tables)
261
- const tableRowPattern = new RegExp(
262
- `^(\\|\\s*${phaseEscaped}\\.?\\s[^|]*(?:\\|[^\\n]*))$`,
263
- 'im'
264
- );
265
- const dateField = isComplete ? ` ${today} ` : ' ';
266
- roadmapContent = roadmapContent.replace(tableRowPattern, (fullRow) => {
267
- const cells = fullRow.split('|').slice(1, -1); // drop leading/trailing empty from split
268
- if (cells.length === 5) {
269
- // 5-col: Phase | Milestone | Plans | Status | Completed
270
- cells[2] = ` ${summaryCount}/${planCount} `;
271
- cells[3] = ` ${status.padEnd(11)}`;
272
- cells[4] = dateField;
273
- } else if (cells.length === 4) {
274
- // 4-col: Phase | Plans | Status | Completed
275
- cells[1] = ` ${summaryCount}/${planCount} `;
276
- cells[2] = ` ${status.padEnd(11)}`;
277
- cells[3] = dateField;
278
- }
279
- return '|' + cells.join('|') + '|';
280
- });
281
-
282
- // Update plan count in phase detail section
283
- const planCountPattern = new RegExp(
284
- `(#{2,4}\\s*Phase\\s+${phaseEscaped}[\\s\\S]*?\\*\\*Plans:\\*\\*\\s*)[^\\n]+`,
285
- 'i'
286
- );
287
- const planCountText = isComplete
288
- ? `${summaryCount}/${planCount} plans complete`
289
- : `${summaryCount}/${planCount} plans executed`;
290
- roadmapContent = replaceInCurrentMilestone(roadmapContent, planCountPattern, `$1${planCountText}`);
291
-
292
- // If complete: check checkbox
293
- if (isComplete) {
294
- const checkboxPattern = new RegExp(
295
- `(-\\s*\\[)[ ](\\]\\s*.*Phase\\s+${phaseEscaped}[:\\s][^\\n]*)`,
296
- 'i'
297
- );
298
- roadmapContent = replaceInCurrentMilestone(roadmapContent, checkboxPattern, `$1x$2 (completed ${today})`);
299
- }
300
-
301
- // Mark completed plan checkboxes (e.g. "- [ ] 50-01-PLAN.md" or "- [ ] 50-01:")
302
- for (const summaryFile of phaseInfo.summaries) {
303
- const planId = summaryFile.replace('-SUMMARY.md', '').replace('SUMMARY.md', '');
304
- if (!planId) continue;
305
- const planEscaped = escapeRegex(planId);
306
- const planCheckboxPattern = new RegExp(
307
- `(-\\s*\\[) (\\]\\s*${planEscaped})`,
308
- 'i'
309
- );
310
- roadmapContent = roadmapContent.replace(planCheckboxPattern, '$1x$2');
311
- }
312
-
313
- fs.writeFileSync(roadmapPath, roadmapContent, 'utf-8');
314
-
315
- output({
316
- updated: true,
317
- phase: phaseNum,
318
- plan_count: planCount,
319
- summary_count: summaryCount,
320
- status,
321
- complete: isComplete,
322
- }, raw, `${summaryCount}/${planCount} ${status}`);
323
- }
324
-
325
- module.exports = {
326
- cmdRoadmapGetPhase,
327
- cmdRoadmapAnalyze,
328
- cmdRoadmapUpdatePlanProgress,
329
- };
1
+ /**
2
+ * Roadmap — Roadmap parsing and update operations
3
+ */
4
+
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+ const { escapeRegex, normalizePhaseName, planningPaths, output, error, findPhaseInternal, stripShippedMilestones, extractCurrentMilestone, replaceInCurrentMilestone } = require('./core.cjs');
8
+
9
+ function cmdRoadmapGetPhase(cwd, phaseNum, raw) {
10
+ const roadmapPath = planningPaths(cwd).roadmap;
11
+
12
+ if (!fs.existsSync(roadmapPath)) {
13
+ output({ found: false, error: 'ROADMAP.md not found' }, raw, '');
14
+ return;
15
+ }
16
+
17
+ try {
18
+ const content = extractCurrentMilestone(fs.readFileSync(roadmapPath, 'utf-8'), cwd);
19
+
20
+ // Escape special regex chars in phase number, handle decimal
21
+ const escapedPhase = escapeRegex(phaseNum);
22
+
23
+ // Match "## Phase X:", "### Phase X:", or "#### Phase X:" with optional name
24
+ const phasePattern = new RegExp(
25
+ `#{2,4}\\s*Phase\\s+${escapedPhase}:\\s*([^\\n]+)`,
26
+ 'i'
27
+ );
28
+ const headerMatch = content.match(phasePattern);
29
+
30
+ if (!headerMatch) {
31
+ // Fallback: check if phase exists in summary list but missing detail section
32
+ const checklistPattern = new RegExp(
33
+ `-\\s*\\[[ x]\\]\\s*\\*\\*Phase\\s+${escapedPhase}:\\s*([^*]+)\\*\\*`,
34
+ 'i'
35
+ );
36
+ const checklistMatch = content.match(checklistPattern);
37
+
38
+ if (checklistMatch) {
39
+ // Phase exists in summary but missing detail section - malformed ROADMAP
40
+ output({
41
+ found: false,
42
+ phase_number: phaseNum,
43
+ phase_name: checklistMatch[1].trim(),
44
+ error: 'malformed_roadmap',
45
+ message: `Phase ${phaseNum} exists in summary list but missing "### Phase ${phaseNum}:" detail section. ROADMAP.md needs both formats.`
46
+ }, raw, '');
47
+ return;
48
+ }
49
+
50
+ output({ found: false, phase_number: phaseNum }, raw, '');
51
+ return;
52
+ }
53
+
54
+ const phaseName = headerMatch[1].trim();
55
+ const headerIndex = headerMatch.index;
56
+
57
+ // Find the end of this section (next ## or ### phase header, or end of file)
58
+ const restOfContent = content.slice(headerIndex);
59
+ const nextHeaderMatch = restOfContent.match(/\n#{2,4}\s+Phase\s+\d/i);
60
+ const sectionEnd = nextHeaderMatch
61
+ ? headerIndex + nextHeaderMatch.index
62
+ : content.length;
63
+
64
+ const section = content.slice(headerIndex, sectionEnd).trim();
65
+
66
+ // Extract goal if present (supports both **Goal:** and **Goal**: formats)
67
+ const goalMatch = section.match(/\*\*Goal(?::\*\*|\*\*:)\s*([^\n]+)/i);
68
+ const goal = goalMatch ? goalMatch[1].trim() : null;
69
+
70
+ // Extract success criteria as structured array
71
+ const criteriaMatch = section.match(/\*\*Success Criteria\*\*[^\n]*:\s*\n((?:\s*\d+\.\s*[^\n]+\n?)+)/i);
72
+ const success_criteria = criteriaMatch
73
+ ? criteriaMatch[1].trim().split('\n').map(line => line.replace(/^\s*\d+\.\s*/, '').trim()).filter(Boolean)
74
+ : [];
75
+
76
+ output(
77
+ {
78
+ found: true,
79
+ phase_number: phaseNum,
80
+ phase_name: phaseName,
81
+ goal,
82
+ success_criteria,
83
+ section,
84
+ },
85
+ raw,
86
+ section
87
+ );
88
+ } catch (e) {
89
+ error('Failed to read ROADMAP.md: ' + e.message);
90
+ }
91
+ }
92
+
93
+ function cmdRoadmapAnalyze(cwd, raw) {
94
+ const roadmapPath = planningPaths(cwd).roadmap;
95
+
96
+ if (!fs.existsSync(roadmapPath)) {
97
+ output({ error: 'ROADMAP.md not found', milestones: [], phases: [], current_phase: null }, raw);
98
+ return;
99
+ }
100
+
101
+ const rawContent = fs.readFileSync(roadmapPath, 'utf-8');
102
+ const content = extractCurrentMilestone(rawContent, cwd);
103
+ const phasesDir = planningPaths(cwd).phases;
104
+
105
+ // Extract all phase headings: ## Phase N: Name or ### Phase N: Name
106
+ const phasePattern = /#{2,4}\s*Phase\s+(\d+[A-Z]?(?:\.\d+)*)\s*:\s*([^\n]+)/gi;
107
+ const phases = [];
108
+ let match;
109
+
110
+ while ((match = phasePattern.exec(content)) !== null) {
111
+ const phaseNum = match[1];
112
+ const phaseName = match[2].replace(/\(INSERTED\)/i, '').trim();
113
+
114
+ // Extract goal from the section
115
+ const sectionStart = match.index;
116
+ const restOfContent = content.slice(sectionStart);
117
+ const nextHeader = restOfContent.match(/\n#{2,4}\s+Phase\s+\d/i);
118
+ const sectionEnd = nextHeader ? sectionStart + nextHeader.index : content.length;
119
+ const section = content.slice(sectionStart, sectionEnd);
120
+
121
+ const goalMatch = section.match(/\*\*Goal(?::\*\*|\*\*:)\s*([^\n]+)/i);
122
+ const goal = goalMatch ? goalMatch[1].trim() : null;
123
+
124
+ const dependsMatch = section.match(/\*\*Depends on(?::\*\*|\*\*:)\s*([^\n]+)/i);
125
+ const depends_on = dependsMatch ? dependsMatch[1].trim() : null;
126
+
127
+ // Check completion on disk
128
+ const normalized = normalizePhaseName(phaseNum);
129
+ let diskStatus = 'no_directory';
130
+ let planCount = 0;
131
+ let summaryCount = 0;
132
+ let hasContext = false;
133
+ let hasResearch = false;
134
+
135
+ try {
136
+ const entries = fs.readdirSync(phasesDir, { withFileTypes: true });
137
+ const dirs = entries.filter(e => e.isDirectory()).map(e => e.name);
138
+ const dirMatch = dirs.find(d => d.startsWith(normalized + '-') || d === normalized);
139
+
140
+ if (dirMatch) {
141
+ const phaseFiles = fs.readdirSync(path.join(phasesDir, dirMatch));
142
+ planCount = phaseFiles.filter(f => f.endsWith('-PLAN.md') || f === 'PLAN.md').length;
143
+ summaryCount = phaseFiles.filter(f => f.endsWith('-SUMMARY.md') || f === 'SUMMARY.md').length;
144
+ hasContext = phaseFiles.some(f => f.endsWith('-CONTEXT.md') || f === 'CONTEXT.md');
145
+ hasResearch = phaseFiles.some(f => f.endsWith('-RESEARCH.md') || f === 'RESEARCH.md');
146
+
147
+ if (summaryCount >= planCount && planCount > 0) diskStatus = 'complete';
148
+ else if (summaryCount > 0) diskStatus = 'partial';
149
+ else if (planCount > 0) diskStatus = 'planned';
150
+ else if (hasResearch) diskStatus = 'researched';
151
+ else if (hasContext) diskStatus = 'discussed';
152
+ else diskStatus = 'empty';
153
+ }
154
+ } catch { /* intentionally empty */ }
155
+
156
+ // Check ROADMAP checkbox status
157
+ const checkboxPattern = new RegExp(`-\\s*\\[(x| )\\]\\s*.*Phase\\s+${escapeRegex(phaseNum)}[:\\s]`, 'i');
158
+ const checkboxMatch = content.match(checkboxPattern);
159
+ const roadmapComplete = checkboxMatch ? checkboxMatch[1] === 'x' : false;
160
+
161
+ // If roadmap marks phase complete, trust that over disk file structure.
162
+ // Phases completed before framework tracking (or via external tools) may lack
163
+ // the standard PLAN/SUMMARY pairs but are still done.
164
+ if (roadmapComplete && diskStatus !== 'complete') {
165
+ diskStatus = 'complete';
166
+ }
167
+
168
+ phases.push({
169
+ number: phaseNum,
170
+ name: phaseName,
171
+ goal,
172
+ depends_on,
173
+ plan_count: planCount,
174
+ summary_count: summaryCount,
175
+ has_context: hasContext,
176
+ has_research: hasResearch,
177
+ disk_status: diskStatus,
178
+ roadmap_complete: roadmapComplete,
179
+ });
180
+ }
181
+
182
+ // Extract milestone info
183
+ const milestones = [];
184
+ const milestonePattern = /##\s*(.*v(\d+(?:\.\d+)+)[^(\n]*)/gi;
185
+ let mMatch;
186
+ while ((mMatch = milestonePattern.exec(content)) !== null) {
187
+ milestones.push({
188
+ heading: mMatch[1].trim(),
189
+ version: 'v' + mMatch[2],
190
+ });
191
+ }
192
+
193
+ // Find current and next phase
194
+ const currentPhase = phases.find(p => p.disk_status === 'planned' || p.disk_status === 'partial') || null;
195
+ const nextPhase = phases.find(p => p.disk_status === 'empty' || p.disk_status === 'no_directory' || p.disk_status === 'discussed' || p.disk_status === 'researched') || null;
196
+
197
+ // Aggregated stats
198
+ const totalPlans = phases.reduce((sum, p) => sum + p.plan_count, 0);
199
+ const totalSummaries = phases.reduce((sum, p) => sum + p.summary_count, 0);
200
+ const completedPhases = phases.filter(p => p.disk_status === 'complete').length;
201
+
202
+ // Detect phases in summary list without detail sections (malformed ROADMAP)
203
+ const checklistPattern = /-\s*\[[ x]\]\s*\*\*Phase\s+(\d+[A-Z]?(?:\.\d+)*)/gi;
204
+ const checklistPhases = new Set();
205
+ let checklistMatch;
206
+ while ((checklistMatch = checklistPattern.exec(content)) !== null) {
207
+ checklistPhases.add(checklistMatch[1]);
208
+ }
209
+ const detailPhases = new Set(phases.map(p => p.number));
210
+ const missingDetails = [...checklistPhases].filter(p => !detailPhases.has(p));
211
+
212
+ const result = {
213
+ milestones,
214
+ phases,
215
+ phase_count: phases.length,
216
+ completed_phases: completedPhases,
217
+ total_plans: totalPlans,
218
+ total_summaries: totalSummaries,
219
+ progress_percent: totalPlans > 0 ? Math.min(100, Math.round((totalSummaries / totalPlans) * 100)) : 0,
220
+ current_phase: currentPhase ? currentPhase.number : null,
221
+ next_phase: nextPhase ? nextPhase.number : null,
222
+ missing_phase_details: missingDetails.length > 0 ? missingDetails : null,
223
+ };
224
+
225
+ output(result, raw);
226
+ }
227
+
228
+ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
229
+ if (!phaseNum) {
230
+ error('phase number required for roadmap update-plan-progress');
231
+ }
232
+
233
+ const roadmapPath = planningPaths(cwd).roadmap;
234
+
235
+ const phaseInfo = findPhaseInternal(cwd, phaseNum);
236
+ if (!phaseInfo) {
237
+ error(`Phase ${phaseNum} not found`);
238
+ }
239
+
240
+ const planCount = phaseInfo.plans.length;
241
+ const summaryCount = phaseInfo.summaries.length;
242
+
243
+ if (planCount === 0) {
244
+ output({ updated: false, reason: 'No plans found', plan_count: 0, summary_count: 0 }, raw, 'no plans');
245
+ return;
246
+ }
247
+
248
+ const isComplete = summaryCount >= planCount;
249
+ const status = isComplete ? 'Complete' : summaryCount > 0 ? 'In Progress' : 'Planned';
250
+ const today = new Date().toISOString().split('T')[0];
251
+
252
+ if (!fs.existsSync(roadmapPath)) {
253
+ output({ updated: false, reason: 'ROADMAP.md not found', plan_count: planCount, summary_count: summaryCount }, raw, 'no roadmap');
254
+ return;
255
+ }
256
+
257
+ let roadmapContent = fs.readFileSync(roadmapPath, 'utf-8');
258
+ const phaseEscaped = escapeRegex(phaseNum);
259
+
260
+ // Progress table row: update Plans/Status/Date columns (handles 4 or 5 column tables)
261
+ const tableRowPattern = new RegExp(
262
+ `^(\\|\\s*${phaseEscaped}\\.?\\s[^|]*(?:\\|[^\\n]*))$`,
263
+ 'im'
264
+ );
265
+ const dateField = isComplete ? ` ${today} ` : ' ';
266
+ roadmapContent = roadmapContent.replace(tableRowPattern, (fullRow) => {
267
+ const cells = fullRow.split('|').slice(1, -1); // drop leading/trailing empty from split
268
+ if (cells.length === 5) {
269
+ // 5-col: Phase | Milestone | Plans | Status | Completed
270
+ cells[2] = ` ${summaryCount}/${planCount} `;
271
+ cells[3] = ` ${status.padEnd(11)}`;
272
+ cells[4] = dateField;
273
+ } else if (cells.length === 4) {
274
+ // 4-col: Phase | Plans | Status | Completed
275
+ cells[1] = ` ${summaryCount}/${planCount} `;
276
+ cells[2] = ` ${status.padEnd(11)}`;
277
+ cells[3] = dateField;
278
+ }
279
+ return '|' + cells.join('|') + '|';
280
+ });
281
+
282
+ // Update plan count in phase detail section
283
+ const planCountPattern = new RegExp(
284
+ `(#{2,4}\\s*Phase\\s+${phaseEscaped}[\\s\\S]*?\\*\\*Plans:\\*\\*\\s*)[^\\n]+`,
285
+ 'i'
286
+ );
287
+ const planCountText = isComplete
288
+ ? `${summaryCount}/${planCount} plans complete`
289
+ : `${summaryCount}/${planCount} plans executed`;
290
+ roadmapContent = replaceInCurrentMilestone(roadmapContent, planCountPattern, `$1${planCountText}`);
291
+
292
+ // If complete: check checkbox
293
+ if (isComplete) {
294
+ const checkboxPattern = new RegExp(
295
+ `(-\\s*\\[)[ ](\\]\\s*.*Phase\\s+${phaseEscaped}[:\\s][^\\n]*)`,
296
+ 'i'
297
+ );
298
+ roadmapContent = replaceInCurrentMilestone(roadmapContent, checkboxPattern, `$1x$2 (completed ${today})`);
299
+ }
300
+
301
+ // Mark completed plan checkboxes (e.g. "- [ ] 50-01-PLAN.md" or "- [ ] 50-01:")
302
+ for (const summaryFile of phaseInfo.summaries) {
303
+ const planId = summaryFile.replace('-SUMMARY.md', '').replace('SUMMARY.md', '');
304
+ if (!planId) continue;
305
+ const planEscaped = escapeRegex(planId);
306
+ const planCheckboxPattern = new RegExp(
307
+ `(-\\s*\\[) (\\]\\s*${planEscaped})`,
308
+ 'i'
309
+ );
310
+ roadmapContent = roadmapContent.replace(planCheckboxPattern, '$1x$2');
311
+ }
312
+
313
+ fs.writeFileSync(roadmapPath, roadmapContent, 'utf-8');
314
+
315
+ output({
316
+ updated: true,
317
+ phase: phaseNum,
318
+ plan_count: planCount,
319
+ summary_count: summaryCount,
320
+ status,
321
+ complete: isComplete,
322
+ }, raw, `${summaryCount}/${planCount} ${status}`);
323
+ }
324
+
325
+ module.exports = {
326
+ cmdRoadmapGetPhase,
327
+ cmdRoadmapAnalyze,
328
+ cmdRoadmapUpdatePlanProgress,
329
+ };