maxsimcli 3.10.3 → 3.12.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 (142) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/assets/CHANGELOG.md +26 -0
  3. package/dist/assets/dashboard/client/assets/index-CxFKStBk.css +32 -0
  4. package/dist/assets/dashboard/client/assets/{index-CZ8WC97G.js → index-wtQDvXzr.js} +64 -64
  5. package/dist/assets/dashboard/client/index.html +2 -2
  6. package/dist/assets/dashboard/server.js +5 -1
  7. package/dist/assets/templates/agents/AGENTS.md +82 -0
  8. package/dist/assets/templates/commands/maxsim/settings.md +1 -1
  9. package/dist/assets/templates/skills/code-review/SKILL.md +151 -0
  10. package/dist/assets/templates/skills/memory-management/SKILL.md +174 -0
  11. package/dist/assets/templates/skills/simplify/SKILL.md +137 -0
  12. package/dist/assets/templates/skills/using-maxsim/SKILL.md +115 -0
  13. package/dist/assets/templates/templates/config.json +1 -1
  14. package/dist/assets/templates/workflows/add-tests.md +3 -3
  15. package/dist/assets/templates/workflows/complete-milestone.md +1 -1
  16. package/dist/assets/templates/workflows/execute-phase.md +4 -14
  17. package/dist/assets/templates/workflows/execute-plan.md +10 -0
  18. package/dist/assets/templates/workflows/init-existing.md +7 -3
  19. package/dist/assets/templates/workflows/new-milestone.md +4 -0
  20. package/dist/assets/templates/workflows/new-project.md +6 -2
  21. package/dist/assets/templates/workflows/plan-phase.md +2 -2
  22. package/dist/assets/templates/workflows/settings.md +8 -4
  23. package/dist/assets/templates/workflows/verify-work.md +1 -1
  24. package/dist/cli.cjs +818 -599
  25. package/dist/cli.cjs.map +1 -1
  26. package/dist/cli.js +78 -204
  27. package/dist/cli.js.map +1 -1
  28. package/dist/core/commands.d.ts +7 -0
  29. package/dist/core/commands.d.ts.map +1 -1
  30. package/dist/core/commands.js +40 -35
  31. package/dist/core/commands.js.map +1 -1
  32. package/dist/core/core.d.ts +39 -1
  33. package/dist/core/core.d.ts.map +1 -1
  34. package/dist/core/core.js +122 -47
  35. package/dist/core/core.js.map +1 -1
  36. package/dist/core/dashboard-launcher.d.ts +56 -0
  37. package/dist/core/dashboard-launcher.d.ts.map +1 -0
  38. package/dist/core/dashboard-launcher.js +243 -0
  39. package/dist/core/dashboard-launcher.js.map +1 -0
  40. package/dist/core/index.d.ts +4 -2
  41. package/dist/core/index.d.ts.map +1 -1
  42. package/dist/core/index.js +20 -2
  43. package/dist/core/index.js.map +1 -1
  44. package/dist/core/init.d.ts +2 -3
  45. package/dist/core/init.d.ts.map +1 -1
  46. package/dist/core/init.js +33 -52
  47. package/dist/core/init.js.map +1 -1
  48. package/dist/core/milestone.d.ts.map +1 -1
  49. package/dist/core/milestone.js +15 -20
  50. package/dist/core/milestone.js.map +1 -1
  51. package/dist/core/phase.d.ts +33 -0
  52. package/dist/core/phase.d.ts.map +1 -1
  53. package/dist/core/phase.js +282 -225
  54. package/dist/core/phase.js.map +1 -1
  55. package/dist/core/roadmap.d.ts.map +1 -1
  56. package/dist/core/roadmap.js +17 -18
  57. package/dist/core/roadmap.js.map +1 -1
  58. package/dist/core/state.d.ts +5 -0
  59. package/dist/core/state.d.ts.map +1 -1
  60. package/dist/core/state.js +51 -42
  61. package/dist/core/state.js.map +1 -1
  62. package/dist/core/template.d.ts.map +1 -1
  63. package/dist/core/template.js +1 -1
  64. package/dist/core/template.js.map +1 -1
  65. package/dist/core/types.d.ts +4 -4
  66. package/dist/core/types.d.ts.map +1 -1
  67. package/dist/core/types.js +1 -2
  68. package/dist/core/types.js.map +1 -1
  69. package/dist/core/verify.d.ts.map +1 -1
  70. package/dist/core/verify.js +61 -80
  71. package/dist/core/verify.js.map +1 -1
  72. package/dist/install/adapters.d.ts +15 -0
  73. package/dist/install/adapters.d.ts.map +1 -0
  74. package/dist/install/adapters.js +203 -0
  75. package/dist/install/adapters.js.map +1 -0
  76. package/dist/install/copy.d.ts +15 -0
  77. package/dist/install/copy.d.ts.map +1 -0
  78. package/dist/install/copy.js +191 -0
  79. package/dist/install/copy.js.map +1 -0
  80. package/dist/install/dashboard.d.ts +16 -0
  81. package/dist/install/dashboard.d.ts.map +1 -0
  82. package/dist/install/dashboard.js +273 -0
  83. package/dist/install/dashboard.js.map +1 -0
  84. package/dist/install/hooks.d.ts +32 -0
  85. package/dist/install/hooks.d.ts.map +1 -0
  86. package/dist/install/hooks.js +285 -0
  87. package/dist/install/hooks.js.map +1 -0
  88. package/dist/install/index.d.ts +2 -0
  89. package/dist/install/index.d.ts.map +1 -0
  90. package/dist/install/index.js +598 -0
  91. package/dist/install/index.js.map +1 -0
  92. package/dist/install/manifest.d.ts +20 -0
  93. package/dist/install/manifest.d.ts.map +1 -0
  94. package/dist/install/manifest.js +135 -0
  95. package/dist/install/manifest.js.map +1 -0
  96. package/dist/install/patches.d.ts +11 -0
  97. package/dist/install/patches.d.ts.map +1 -0
  98. package/dist/install/patches.js +136 -0
  99. package/dist/install/patches.js.map +1 -0
  100. package/dist/install/shared.d.ts +50 -0
  101. package/dist/install/shared.d.ts.map +1 -0
  102. package/dist/install/shared.js +142 -0
  103. package/dist/install/shared.js.map +1 -0
  104. package/dist/install/uninstall.d.ts +6 -0
  105. package/dist/install/uninstall.d.ts.map +1 -0
  106. package/dist/install/uninstall.js +280 -0
  107. package/dist/install/uninstall.js.map +1 -0
  108. package/dist/install.cjs +782 -705
  109. package/dist/install.cjs.map +1 -1
  110. package/dist/mcp/index.d.ts +12 -0
  111. package/dist/mcp/index.d.ts.map +1 -0
  112. package/dist/mcp/index.js +21 -0
  113. package/dist/mcp/index.js.map +1 -0
  114. package/dist/mcp/phase-tools.d.ts +13 -0
  115. package/dist/mcp/phase-tools.d.ts.map +1 -0
  116. package/dist/mcp/phase-tools.js +164 -0
  117. package/dist/mcp/phase-tools.js.map +1 -0
  118. package/dist/mcp/state-tools.d.ts +13 -0
  119. package/dist/mcp/state-tools.d.ts.map +1 -0
  120. package/dist/mcp/state-tools.js +185 -0
  121. package/dist/mcp/state-tools.js.map +1 -0
  122. package/dist/mcp/todo-tools.d.ts +13 -0
  123. package/dist/mcp/todo-tools.d.ts.map +1 -0
  124. package/dist/mcp/todo-tools.js +143 -0
  125. package/dist/mcp/todo-tools.js.map +1 -0
  126. package/dist/mcp/utils.d.ts +27 -0
  127. package/dist/mcp/utils.d.ts.map +1 -0
  128. package/dist/mcp/utils.js +82 -0
  129. package/dist/mcp/utils.js.map +1 -0
  130. package/dist/mcp-server.cjs +11806 -0
  131. package/dist/mcp-server.cjs.map +1 -0
  132. package/dist/mcp-server.d.cts +2 -0
  133. package/dist/mcp-server.d.ts +12 -0
  134. package/dist/mcp-server.d.ts.map +1 -0
  135. package/dist/mcp-server.js +31 -0
  136. package/dist/mcp-server.js.map +1 -0
  137. package/package.json +5 -3
  138. package/dist/assets/dashboard/client/assets/index-DzJChB-D.css +0 -32
  139. package/dist/install.d.ts +0 -2
  140. package/dist/install.d.ts.map +0 -1
  141. package/dist/install.js +0 -1804
  142. package/dist/install.js.map +0 -1
@@ -8,6 +8,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
8
8
  return (mod && mod.__esModule) ? mod : { "default": mod };
9
9
  };
10
10
  Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.scaffoldPhaseStubs = scaffoldPhaseStubs;
12
+ exports.phaseAddCore = phaseAddCore;
13
+ exports.phaseInsertCore = phaseInsertCore;
14
+ exports.phaseCompleteCore = phaseCompleteCore;
11
15
  exports.cmdPhasesList = cmdPhasesList;
12
16
  exports.cmdPhaseNextDecimal = cmdPhaseNextDecimal;
13
17
  exports.cmdFindPhase = cmdFindPhase;
@@ -20,11 +24,209 @@ const node_fs_1 = __importDefault(require("node:fs"));
20
24
  const node_path_1 = __importDefault(require("node:path"));
21
25
  const core_js_1 = require("./core.js");
22
26
  const frontmatter_js_1 = require("./frontmatter.js");
27
+ // ─── Stub scaffolding ───────────────────────────────────────────────────────
28
+ function scaffoldPhaseStubs(dirPath, phaseId, name) {
29
+ const today = (0, core_js_1.todayISO)();
30
+ node_fs_1.default.writeFileSync(node_path_1.default.join(dirPath, `${phaseId}-CONTEXT.md`), `# Phase ${phaseId} Context: ${name}\n\n**Created:** ${today}\n**Phase goal:** [To be defined during /maxsim:discuss-phase]\n\n---\n\n_Context will be populated by /maxsim:discuss-phase_\n`);
31
+ node_fs_1.default.writeFileSync(node_path_1.default.join(dirPath, `${phaseId}-RESEARCH.md`), `# Phase ${phaseId}: ${name} - Research\n\n**Researched:** Not yet\n**Domain:** TBD\n**Confidence:** TBD\n\n---\n\n_Research will be populated by /maxsim:research-phase_\n`);
32
+ }
33
+ // ─── Core functions ─────────────────────────────────────────────────────────
34
+ function phaseAddCore(cwd, description, options) {
35
+ const rmPath = (0, core_js_1.roadmapPath)(cwd);
36
+ if (!node_fs_1.default.existsSync(rmPath)) {
37
+ throw new Error('ROADMAP.md not found');
38
+ }
39
+ const content = node_fs_1.default.readFileSync(rmPath, 'utf-8');
40
+ const slug = (0, core_js_1.generateSlugInternal)(description);
41
+ const phasePattern = (0, core_js_1.getPhasePattern)();
42
+ let maxPhase = 0;
43
+ let m;
44
+ while ((m = phasePattern.exec(content)) !== null) {
45
+ const num = parseInt(m[1], 10);
46
+ if (num > maxPhase)
47
+ maxPhase = num;
48
+ }
49
+ const newPhaseNum = maxPhase + 1;
50
+ const paddedNum = String(newPhaseNum).padStart(2, '0');
51
+ const dirName = `${paddedNum}-${slug}`;
52
+ const dirPath = (0, core_js_1.planningPath)(cwd, 'phases', dirName);
53
+ node_fs_1.default.mkdirSync(dirPath, { recursive: true });
54
+ node_fs_1.default.writeFileSync(node_path_1.default.join(dirPath, '.gitkeep'), '');
55
+ if (options?.includeStubs) {
56
+ scaffoldPhaseStubs(dirPath, paddedNum, description);
57
+ }
58
+ const phaseEntry = `\n### Phase ${newPhaseNum}: ${description}\n\n**Goal:** [To be planned]\n**Requirements**: TBD\n**Depends on:** Phase ${maxPhase}\n**Plans:** 0 plans\n\nPlans:\n- [ ] TBD (run /maxsim:plan-phase ${newPhaseNum} to break down)\n`;
59
+ let updatedContent;
60
+ const lastSeparator = content.lastIndexOf('\n---');
61
+ if (lastSeparator > 0) {
62
+ updatedContent = content.slice(0, lastSeparator) + phaseEntry + content.slice(lastSeparator);
63
+ }
64
+ else {
65
+ updatedContent = content + phaseEntry;
66
+ }
67
+ node_fs_1.default.writeFileSync(rmPath, updatedContent, 'utf-8');
68
+ return {
69
+ phase_number: newPhaseNum,
70
+ padded: paddedNum,
71
+ slug,
72
+ directory: `.planning/phases/${dirName}`,
73
+ description,
74
+ };
75
+ }
76
+ function phaseInsertCore(cwd, afterPhase, description, options) {
77
+ const rmPath = (0, core_js_1.roadmapPath)(cwd);
78
+ if (!node_fs_1.default.existsSync(rmPath)) {
79
+ throw new Error('ROADMAP.md not found');
80
+ }
81
+ const content = node_fs_1.default.readFileSync(rmPath, 'utf-8');
82
+ const slug = (0, core_js_1.generateSlugInternal)(description);
83
+ const normalizedAfter = (0, core_js_1.normalizePhaseName)(afterPhase);
84
+ const unpadded = normalizedAfter.replace(/^0+/, '');
85
+ const afterPhaseEscaped = '0*' + unpadded.replace(/\./g, '\\.');
86
+ const targetPattern = (0, core_js_1.getPhasePattern)(afterPhaseEscaped, 'i');
87
+ if (!targetPattern.test(content)) {
88
+ throw new Error(`Phase ${afterPhase} not found in ROADMAP.md`);
89
+ }
90
+ const phasesDirPath = (0, core_js_1.phasesPath)(cwd);
91
+ const normalizedBase = (0, core_js_1.normalizePhaseName)(afterPhase);
92
+ const existingDecimals = [];
93
+ try {
94
+ const dirs = (0, core_js_1.listSubDirs)(phasesDirPath);
95
+ const decimalPattern = new RegExp(`^${normalizedBase}\\.(\\d+)`);
96
+ for (const dir of dirs) {
97
+ const dm = dir.match(decimalPattern);
98
+ if (dm)
99
+ existingDecimals.push(parseInt(dm[1], 10));
100
+ }
101
+ }
102
+ catch (e) {
103
+ (0, core_js_1.debugLog)(e);
104
+ }
105
+ const nextDecimal = existingDecimals.length === 0 ? 1 : Math.max(...existingDecimals) + 1;
106
+ const decimalPhase = `${normalizedBase}.${nextDecimal}`;
107
+ const dirName = `${decimalPhase}-${slug}`;
108
+ const dirPath = (0, core_js_1.planningPath)(cwd, 'phases', dirName);
109
+ node_fs_1.default.mkdirSync(dirPath, { recursive: true });
110
+ node_fs_1.default.writeFileSync(node_path_1.default.join(dirPath, '.gitkeep'), '');
111
+ if (options?.includeStubs) {
112
+ scaffoldPhaseStubs(dirPath, decimalPhase, description);
113
+ }
114
+ const phaseEntry = `\n### Phase ${decimalPhase}: ${description} (INSERTED)\n\n**Goal:** [Urgent work - to be planned]\n**Requirements**: TBD\n**Depends on:** Phase ${afterPhase}\n**Plans:** 0 plans\n\nPlans:\n- [ ] TBD (run /maxsim:plan-phase ${decimalPhase} to break down)\n`;
115
+ const headerPattern = new RegExp(`(#{2,4}\\s*Phase\\s+0*${afterPhaseEscaped}:[^\\n]*\\n)`, 'i');
116
+ const headerMatch = content.match(headerPattern);
117
+ if (!headerMatch) {
118
+ throw new Error(`Could not find Phase ${afterPhase} header`);
119
+ }
120
+ const headerIdx = content.indexOf(headerMatch[0]);
121
+ const afterHeader = content.slice(headerIdx + headerMatch[0].length);
122
+ const nextPhaseMatch = afterHeader.match(/\n#{2,4}\s+Phase\s+\d/i);
123
+ let insertIdx;
124
+ if (nextPhaseMatch) {
125
+ insertIdx = headerIdx + headerMatch[0].length + nextPhaseMatch.index;
126
+ }
127
+ else {
128
+ insertIdx = content.length;
129
+ }
130
+ const updatedContent = content.slice(0, insertIdx) + phaseEntry + content.slice(insertIdx);
131
+ node_fs_1.default.writeFileSync(rmPath, updatedContent, 'utf-8');
132
+ return {
133
+ phase_number: decimalPhase,
134
+ after_phase: afterPhase,
135
+ slug,
136
+ directory: `.planning/phases/${dirName}`,
137
+ description,
138
+ };
139
+ }
140
+ function phaseCompleteCore(cwd, phaseNum) {
141
+ const rmPath = (0, core_js_1.roadmapPath)(cwd);
142
+ const stPath = (0, core_js_1.statePath)(cwd);
143
+ const phasesDirPath = (0, core_js_1.phasesPath)(cwd);
144
+ const today = (0, core_js_1.todayISO)();
145
+ const phaseInfo = (0, core_js_1.findPhaseInternal)(cwd, phaseNum);
146
+ if (!phaseInfo) {
147
+ throw new Error(`Phase ${phaseNum} not found`);
148
+ }
149
+ const planCount = phaseInfo.plans.length;
150
+ const summaryCount = phaseInfo.summaries.length;
151
+ let requirementsUpdated = false;
152
+ if (node_fs_1.default.existsSync(rmPath)) {
153
+ let roadmapContent = node_fs_1.default.readFileSync(rmPath, 'utf-8');
154
+ const checkboxPattern = new RegExp(`(-\\s*\\[)[ ](\\]\\s*.*Phase\\s+${(0, core_js_1.escapePhaseNum)(phaseNum)}[:\\s][^\\n]*)`, 'i');
155
+ roadmapContent = roadmapContent.replace(checkboxPattern, `$1x$2 (completed ${today})`);
156
+ const phaseEscaped = (0, core_js_1.escapePhaseNum)(phaseNum);
157
+ const tablePattern = new RegExp(`(\\|\\s*${phaseEscaped}\\.?\\s[^|]*\\|[^|]*\\|)\\s*[^|]*(\\|)\\s*[^|]*(\\|)`, 'i');
158
+ roadmapContent = roadmapContent.replace(tablePattern, `$1 Complete $2 ${today} $3`);
159
+ const planCountPattern = new RegExp(`(#{2,4}\\s*Phase\\s+${phaseEscaped}[\\s\\S]*?\\*\\*Plans:\\*\\*\\s*)[^\\n]+`, 'i');
160
+ roadmapContent = roadmapContent.replace(planCountPattern, `$1${summaryCount}/${planCount} plans complete`);
161
+ node_fs_1.default.writeFileSync(rmPath, roadmapContent, 'utf-8');
162
+ // Update REQUIREMENTS.md
163
+ const reqPath = (0, core_js_1.planningPath)(cwd, 'REQUIREMENTS.md');
164
+ if (node_fs_1.default.existsSync(reqPath)) {
165
+ const reqMatch = roadmapContent.match(new RegExp(`Phase\\s+${(0, core_js_1.escapePhaseNum)(phaseNum)}[\\s\\S]*?\\*\\*Requirements:\\*\\*\\s*([^\\n]+)`, 'i'));
166
+ if (reqMatch) {
167
+ const reqIds = reqMatch[1].replace(/[\[\]]/g, '').split(/[,\s]+/).map(r => r.trim()).filter(Boolean);
168
+ let reqContent = node_fs_1.default.readFileSync(reqPath, 'utf-8');
169
+ for (const reqId of reqIds) {
170
+ reqContent = reqContent.replace(new RegExp(`(-\\s*\\[)[ ](\\]\\s*\\*\\*${reqId}\\*\\*)`, 'gi'), '$1x$2');
171
+ reqContent = reqContent.replace(new RegExp(`(\\|\\s*${reqId}\\s*\\|[^|]+\\|)\\s*Pending\\s*(\\|)`, 'gi'), '$1 Complete $2');
172
+ }
173
+ node_fs_1.default.writeFileSync(reqPath, reqContent, 'utf-8');
174
+ requirementsUpdated = true;
175
+ }
176
+ }
177
+ }
178
+ // Find next phase
179
+ let nextPhaseNum = null;
180
+ let nextPhaseName = null;
181
+ let isLastPhase = true;
182
+ try {
183
+ const dirs = (0, core_js_1.listSubDirs)(phasesDirPath, true);
184
+ for (const dir of dirs) {
185
+ const dm = dir.match(/^(\d+[A-Z]?(?:\.\d+)?)-?(.*)/i);
186
+ if (dm) {
187
+ if ((0, core_js_1.comparePhaseNum)(dm[1], phaseNum) > 0) {
188
+ nextPhaseNum = dm[1];
189
+ nextPhaseName = dm[2] || null;
190
+ isLastPhase = false;
191
+ break;
192
+ }
193
+ }
194
+ }
195
+ }
196
+ catch (e) {
197
+ (0, core_js_1.debugLog)(e);
198
+ }
199
+ // Update STATE.md
200
+ if (node_fs_1.default.existsSync(stPath)) {
201
+ let stateContent = node_fs_1.default.readFileSync(stPath, 'utf-8');
202
+ stateContent = stateContent.replace(/(\*\*Current Phase:\*\*\s*).*/, `$1${nextPhaseNum || phaseNum}`);
203
+ if (nextPhaseName) {
204
+ stateContent = stateContent.replace(/(\*\*Current Phase Name:\*\*\s*).*/, `$1${nextPhaseName.replace(/-/g, ' ')}`);
205
+ }
206
+ stateContent = stateContent.replace(/(\*\*Status:\*\*\s*).*/, `$1${isLastPhase ? 'Milestone complete' : 'Ready to plan'}`);
207
+ stateContent = stateContent.replace(/(\*\*Current Plan:\*\*\s*).*/, `$1Not started`);
208
+ stateContent = stateContent.replace(/(\*\*Last Activity:\*\*\s*).*/, `$1${today}`);
209
+ stateContent = stateContent.replace(/(\*\*Last Activity Description:\*\*\s*).*/, `$1Phase ${phaseNum} complete${nextPhaseNum ? `, transitioned to Phase ${nextPhaseNum}` : ''}`);
210
+ node_fs_1.default.writeFileSync(stPath, stateContent, 'utf-8');
211
+ }
212
+ return {
213
+ completed_phase: phaseNum,
214
+ phase_name: phaseInfo.phase_name,
215
+ plans_executed: `${summaryCount}/${planCount}`,
216
+ next_phase: nextPhaseNum,
217
+ next_phase_name: nextPhaseName,
218
+ is_last_phase: isLastPhase,
219
+ date: today,
220
+ roadmap_updated: node_fs_1.default.existsSync(rmPath),
221
+ state_updated: node_fs_1.default.existsSync(stPath),
222
+ requirements_updated: requirementsUpdated,
223
+ };
224
+ }
23
225
  // ─── Phase list ─────────────────────────────────────────────────────────────
24
226
  function cmdPhasesList(cwd, options, raw) {
25
- const phasesDir = node_path_1.default.join(cwd, '.planning', 'phases');
227
+ const phasesDirPath = (0, core_js_1.phasesPath)(cwd);
26
228
  const { type, phase, includeArchived } = options;
27
- if (!node_fs_1.default.existsSync(phasesDir)) {
229
+ if (!node_fs_1.default.existsSync(phasesDirPath)) {
28
230
  if (type) {
29
231
  (0, core_js_1.output)({ files: [], count: 0 }, raw, '');
30
232
  }
@@ -34,8 +236,7 @@ function cmdPhasesList(cwd, options, raw) {
34
236
  return;
35
237
  }
36
238
  try {
37
- const entries = node_fs_1.default.readdirSync(phasesDir, { withFileTypes: true });
38
- let dirs = entries.filter(e => e.isDirectory()).map(e => e.name);
239
+ let dirs = (0, core_js_1.listSubDirs)(phasesDirPath);
39
240
  if (includeArchived) {
40
241
  const archived = (0, core_js_1.getArchivedPhaseDirs)(cwd);
41
242
  for (const a of archived) {
@@ -55,14 +256,14 @@ function cmdPhasesList(cwd, options, raw) {
55
256
  if (type) {
56
257
  const files = [];
57
258
  for (const dir of dirs) {
58
- const dirPath = node_path_1.default.join(phasesDir, dir);
259
+ const dirPath = node_path_1.default.join(phasesDirPath, dir);
59
260
  const dirFiles = node_fs_1.default.readdirSync(dirPath);
60
261
  let filtered;
61
262
  if (type === 'plans') {
62
- filtered = dirFiles.filter(f => f.endsWith('-PLAN.md') || f === 'PLAN.md');
263
+ filtered = dirFiles.filter(core_js_1.isPlanFile);
63
264
  }
64
265
  else if (type === 'summaries') {
65
- filtered = dirFiles.filter(f => f.endsWith('-SUMMARY.md') || f === 'SUMMARY.md');
266
+ filtered = dirFiles.filter(core_js_1.isSummaryFile);
66
267
  }
67
268
  else {
68
269
  filtered = dirFiles;
@@ -80,20 +281,20 @@ function cmdPhasesList(cwd, options, raw) {
80
281
  (0, core_js_1.output)({ directories: dirs, count: dirs.length }, raw, dirs.join('\n'));
81
282
  }
82
283
  catch (e) {
284
+ (0, core_js_1.rethrowCliSignals)(e);
83
285
  (0, core_js_1.error)('Failed to list phases: ' + e.message);
84
286
  }
85
287
  }
86
288
  // ─── Next decimal ───────────────────────────────────────────────────────────
87
289
  function cmdPhaseNextDecimal(cwd, basePhase, raw) {
88
- const phasesDir = node_path_1.default.join(cwd, '.planning', 'phases');
290
+ const phasesDirPath = (0, core_js_1.phasesPath)(cwd);
89
291
  const normalized = (0, core_js_1.normalizePhaseName)(basePhase);
90
- if (!node_fs_1.default.existsSync(phasesDir)) {
292
+ if (!node_fs_1.default.existsSync(phasesDirPath)) {
91
293
  (0, core_js_1.output)({ found: false, base_phase: normalized, next: `${normalized}.1`, existing: [] }, raw, `${normalized}.1`);
92
294
  return;
93
295
  }
94
296
  try {
95
- const entries = node_fs_1.default.readdirSync(phasesDir, { withFileTypes: true });
96
- const dirs = entries.filter(e => e.isDirectory()).map(e => e.name);
297
+ const dirs = (0, core_js_1.listSubDirs)(phasesDirPath);
97
298
  const baseExists = dirs.some(d => d.startsWith(normalized + '-') || d === normalized);
98
299
  const decimalPattern = new RegExp(`^${normalized}\\.(\\d+)`);
99
300
  const existingDecimals = [];
@@ -120,6 +321,7 @@ function cmdPhaseNextDecimal(cwd, basePhase, raw) {
120
321
  (0, core_js_1.output)({ found: baseExists, base_phase: normalized, next: nextDecimal, existing: existingDecimals }, raw, nextDecimal);
121
322
  }
122
323
  catch (e) {
324
+ (0, core_js_1.rethrowCliSignals)(e);
123
325
  (0, core_js_1.error)('Failed to calculate next decimal phase: ' + e.message);
124
326
  }
125
327
  }
@@ -128,12 +330,11 @@ function cmdFindPhase(cwd, phase, raw) {
128
330
  if (!phase) {
129
331
  (0, core_js_1.error)('phase identifier required');
130
332
  }
131
- const phasesDir = node_path_1.default.join(cwd, '.planning', 'phases');
333
+ const phasesDirPath = (0, core_js_1.phasesPath)(cwd);
132
334
  const normalized = (0, core_js_1.normalizePhaseName)(phase);
133
335
  const notFound = { found: false, directory: null, phase_number: null, phase_name: null, plans: [], summaries: [] };
134
336
  try {
135
- const entries = node_fs_1.default.readdirSync(phasesDir, { withFileTypes: true });
136
- const dirs = entries.filter(e => e.isDirectory()).map(e => e.name).sort((a, b) => (0, core_js_1.comparePhaseNum)(a, b));
337
+ const dirs = (0, core_js_1.listSubDirs)(phasesDirPath, true);
137
338
  const match = dirs.find(d => d.startsWith(normalized));
138
339
  if (!match) {
139
340
  (0, core_js_1.output)(notFound, raw, '');
@@ -142,10 +343,10 @@ function cmdFindPhase(cwd, phase, raw) {
142
343
  const dirMatch = match.match(/^(\d+[A-Z]?(?:\.\d+)?)-?(.*)/i);
143
344
  const phaseNumber = dirMatch ? dirMatch[1] : normalized;
144
345
  const phaseName = dirMatch && dirMatch[2] ? dirMatch[2] : null;
145
- const phaseDir = node_path_1.default.join(phasesDir, match);
346
+ const phaseDir = node_path_1.default.join(phasesDirPath, match);
146
347
  const phaseFiles = node_fs_1.default.readdirSync(phaseDir);
147
- const plans = phaseFiles.filter(f => f.endsWith('-PLAN.md') || f === 'PLAN.md').sort();
148
- const summaries = phaseFiles.filter(f => f.endsWith('-SUMMARY.md') || f === 'SUMMARY.md').sort();
348
+ const plans = phaseFiles.filter(core_js_1.isPlanFile).sort();
349
+ const summaries = phaseFiles.filter(core_js_1.isSummaryFile).sort();
149
350
  const result = {
150
351
  found: true,
151
352
  directory: node_path_1.default.join('.planning', 'phases', match),
@@ -156,7 +357,8 @@ function cmdFindPhase(cwd, phase, raw) {
156
357
  };
157
358
  (0, core_js_1.output)(result, raw, result.directory);
158
359
  }
159
- catch {
360
+ catch (e) {
361
+ (0, core_js_1.rethrowCliSignals)(e);
160
362
  (0, core_js_1.output)(notFound, raw, '');
161
363
  }
162
364
  }
@@ -165,38 +367,36 @@ function cmdPhasePlanIndex(cwd, phase, raw) {
165
367
  if (!phase) {
166
368
  (0, core_js_1.error)('phase required for phase-plan-index');
167
369
  }
168
- const phasesDir = node_path_1.default.join(cwd, '.planning', 'phases');
370
+ const phasesDirPath = (0, core_js_1.phasesPath)(cwd);
169
371
  const normalized = (0, core_js_1.normalizePhaseName)(phase);
170
372
  let phaseDir = null;
171
373
  let phaseDirName = null;
172
374
  try {
173
- const entries = node_fs_1.default.readdirSync(phasesDir, { withFileTypes: true });
174
- const dirs = entries.filter(e => e.isDirectory()).map(e => e.name).sort((a, b) => (0, core_js_1.comparePhaseNum)(a, b));
375
+ const dirs = (0, core_js_1.listSubDirs)(phasesDirPath, true);
175
376
  const match = dirs.find(d => d.startsWith(normalized));
176
377
  if (match) {
177
- phaseDir = node_path_1.default.join(phasesDir, match);
378
+ phaseDir = node_path_1.default.join(phasesDirPath, match);
178
379
  phaseDirName = match;
179
380
  }
180
381
  }
181
382
  catch (e) {
182
383
  /* optional op, ignore */
183
- if (process.env.MAXSIM_DEBUG)
184
- console.error(e);
384
+ (0, core_js_1.debugLog)(e);
185
385
  }
186
386
  if (!phaseDir) {
187
387
  (0, core_js_1.output)({ phase: normalized, error: 'Phase not found', plans: [], waves: {}, incomplete: [], has_checkpoints: false }, raw);
188
388
  return;
189
389
  }
190
390
  const phaseFiles = node_fs_1.default.readdirSync(phaseDir);
191
- const planFiles = phaseFiles.filter(f => f.endsWith('-PLAN.md') || f === 'PLAN.md').sort();
192
- const summaryFiles = phaseFiles.filter(f => f.endsWith('-SUMMARY.md') || f === 'SUMMARY.md');
193
- const completedPlanIds = new Set(summaryFiles.map(s => s.replace('-SUMMARY.md', '').replace('SUMMARY.md', '')));
391
+ const planFiles = phaseFiles.filter(core_js_1.isPlanFile).sort();
392
+ const summaryFiles = phaseFiles.filter(core_js_1.isSummaryFile);
393
+ const completedPlanIds = new Set(summaryFiles.map(core_js_1.summaryId));
194
394
  const plans = [];
195
395
  const waves = {};
196
396
  const incomplete = [];
197
397
  let hasCheckpoints = false;
198
398
  for (const planFile of planFiles) {
199
- const planId = planFile.replace('-PLAN.md', '').replace('PLAN.md', '');
399
+ const id = (0, core_js_1.planId)(planFile);
200
400
  const planPath = node_path_1.default.join(phaseDir, planFile);
201
401
  const content = node_fs_1.default.readFileSync(planPath, 'utf-8');
202
402
  const fm = (0, frontmatter_js_1.extractFrontmatter)(content);
@@ -214,12 +414,12 @@ function cmdPhasePlanIndex(cwd, phase, raw) {
214
414
  if (fm['files-modified']) {
215
415
  filesModified = Array.isArray(fm['files-modified']) ? fm['files-modified'] : [fm['files-modified']];
216
416
  }
217
- const hasSummary = completedPlanIds.has(planId);
417
+ const hasSummary = completedPlanIds.has(id);
218
418
  if (!hasSummary) {
219
- incomplete.push(planId);
419
+ incomplete.push(id);
220
420
  }
221
421
  const plan = {
222
- id: planId,
422
+ id,
223
423
  wave,
224
424
  autonomous,
225
425
  objective: fm.objective || null,
@@ -232,7 +432,7 @@ function cmdPhasePlanIndex(cwd, phase, raw) {
232
432
  if (!waves[waveKey]) {
233
433
  waves[waveKey] = [];
234
434
  }
235
- waves[waveKey].push(planId);
435
+ waves[waveKey].push(id);
236
436
  }
237
437
  (0, core_js_1.output)({ phase: normalized, plans, waves, incomplete, has_checkpoints: hasCheckpoints }, raw);
238
438
  }
@@ -241,134 +441,61 @@ function cmdPhaseAdd(cwd, description, raw) {
241
441
  if (!description) {
242
442
  (0, core_js_1.error)('description required for phase add');
243
443
  }
244
- const roadmapPath = node_path_1.default.join(cwd, '.planning', 'ROADMAP.md');
245
- if (!node_fs_1.default.existsSync(roadmapPath)) {
246
- (0, core_js_1.error)('ROADMAP.md not found');
247
- }
248
- const content = node_fs_1.default.readFileSync(roadmapPath, 'utf-8');
249
- const slug = (0, core_js_1.generateSlugInternal)(description);
250
- const phasePattern = (0, core_js_1.getPhasePattern)();
251
- let maxPhase = 0;
252
- let m;
253
- while ((m = phasePattern.exec(content)) !== null) {
254
- const num = parseInt(m[1], 10);
255
- if (num > maxPhase)
256
- maxPhase = num;
257
- }
258
- const newPhaseNum = maxPhase + 1;
259
- const paddedNum = String(newPhaseNum).padStart(2, '0');
260
- const dirName = `${paddedNum}-${slug}`;
261
- const dirPath = node_path_1.default.join(cwd, '.planning', 'phases', dirName);
262
- node_fs_1.default.mkdirSync(dirPath, { recursive: true });
263
- node_fs_1.default.writeFileSync(node_path_1.default.join(dirPath, '.gitkeep'), '');
264
- const phaseEntry = `\n### Phase ${newPhaseNum}: ${description}\n\n**Goal:** [To be planned]\n**Requirements**: TBD\n**Depends on:** Phase ${maxPhase}\n**Plans:** 0 plans\n\nPlans:\n- [ ] TBD (run /maxsim:plan-phase ${newPhaseNum} to break down)\n`;
265
- let updatedContent;
266
- const lastSeparator = content.lastIndexOf('\n---');
267
- if (lastSeparator > 0) {
268
- updatedContent = content.slice(0, lastSeparator) + phaseEntry + content.slice(lastSeparator);
444
+ try {
445
+ const result = phaseAddCore(cwd, description, { includeStubs: false });
446
+ (0, core_js_1.output)({ phase_number: result.phase_number, padded: result.padded, name: result.description, slug: result.slug, directory: result.directory }, raw, result.padded);
269
447
  }
270
- else {
271
- updatedContent = content + phaseEntry;
448
+ catch (e) {
449
+ (0, core_js_1.rethrowCliSignals)(e);
450
+ (0, core_js_1.error)(e.message);
272
451
  }
273
- node_fs_1.default.writeFileSync(roadmapPath, updatedContent, 'utf-8');
274
- (0, core_js_1.output)({ phase_number: newPhaseNum, padded: paddedNum, name: description, slug, directory: `.planning/phases/${dirName}` }, raw, paddedNum);
275
452
  }
276
453
  // ─── Phase insert ───────────────────────────────────────────────────────────
277
454
  function cmdPhaseInsert(cwd, afterPhase, description, raw) {
278
455
  if (!afterPhase || !description) {
279
456
  (0, core_js_1.error)('after-phase and description required for phase insert');
280
457
  }
281
- const roadmapPath = node_path_1.default.join(cwd, '.planning', 'ROADMAP.md');
282
- if (!node_fs_1.default.existsSync(roadmapPath)) {
283
- (0, core_js_1.error)('ROADMAP.md not found');
284
- }
285
- const content = node_fs_1.default.readFileSync(roadmapPath, 'utf-8');
286
- const slug = (0, core_js_1.generateSlugInternal)(description);
287
- const normalizedAfter = (0, core_js_1.normalizePhaseName)(afterPhase);
288
- const unpadded = normalizedAfter.replace(/^0+/, '');
289
- const afterPhaseEscaped = '0*' + unpadded.replace(/\./g, '\\.');
290
- const targetPattern = (0, core_js_1.getPhasePattern)(afterPhaseEscaped, 'i');
291
- if (!targetPattern.test(content)) {
292
- (0, core_js_1.error)(`Phase ${afterPhase} not found in ROADMAP.md`);
293
- }
294
- const phasesDir = node_path_1.default.join(cwd, '.planning', 'phases');
295
- const normalizedBase = (0, core_js_1.normalizePhaseName)(afterPhase);
296
- const existingDecimals = [];
297
458
  try {
298
- const entries = node_fs_1.default.readdirSync(phasesDir, { withFileTypes: true });
299
- const dirs = entries.filter(e => e.isDirectory()).map(e => e.name);
300
- const decimalPattern = new RegExp(`^${normalizedBase}\\.(\\d+)`);
301
- for (const dir of dirs) {
302
- const dm = dir.match(decimalPattern);
303
- if (dm)
304
- existingDecimals.push(parseInt(dm[1], 10));
305
- }
459
+ const result = phaseInsertCore(cwd, afterPhase, description, { includeStubs: false });
460
+ (0, core_js_1.output)({ phase_number: result.phase_number, after_phase: result.after_phase, name: result.description, slug: result.slug, directory: result.directory }, raw, result.phase_number);
306
461
  }
307
462
  catch (e) {
308
- /* optional op, ignore */
309
- if (process.env.MAXSIM_DEBUG)
310
- console.error(e);
311
- }
312
- const nextDecimal = existingDecimals.length === 0 ? 1 : Math.max(...existingDecimals) + 1;
313
- const decimalPhase = `${normalizedBase}.${nextDecimal}`;
314
- const dirName = `${decimalPhase}-${slug}`;
315
- const dirPath = node_path_1.default.join(cwd, '.planning', 'phases', dirName);
316
- node_fs_1.default.mkdirSync(dirPath, { recursive: true });
317
- node_fs_1.default.writeFileSync(node_path_1.default.join(dirPath, '.gitkeep'), '');
318
- const phaseEntry = `\n### Phase ${decimalPhase}: ${description} (INSERTED)\n\n**Goal:** [Urgent work - to be planned]\n**Requirements**: TBD\n**Depends on:** Phase ${afterPhase}\n**Plans:** 0 plans\n\nPlans:\n- [ ] TBD (run /maxsim:plan-phase ${decimalPhase} to break down)\n`;
319
- const headerPattern = new RegExp(`(#{2,4}\\s*Phase\\s+0*${afterPhaseEscaped}:[^\\n]*\\n)`, 'i');
320
- const headerMatch = content.match(headerPattern);
321
- if (!headerMatch) {
322
- (0, core_js_1.error)(`Could not find Phase ${afterPhase} header`);
323
- }
324
- const headerIdx = content.indexOf(headerMatch[0]);
325
- const afterHeader = content.slice(headerIdx + headerMatch[0].length);
326
- const nextPhaseMatch = afterHeader.match(/\n#{2,4}\s+Phase\s+\d/i);
327
- let insertIdx;
328
- if (nextPhaseMatch) {
329
- insertIdx = headerIdx + headerMatch[0].length + nextPhaseMatch.index;
330
- }
331
- else {
332
- insertIdx = content.length;
463
+ (0, core_js_1.rethrowCliSignals)(e);
464
+ (0, core_js_1.error)(e.message);
333
465
  }
334
- const updatedContent = content.slice(0, insertIdx) + phaseEntry + content.slice(insertIdx);
335
- node_fs_1.default.writeFileSync(roadmapPath, updatedContent, 'utf-8');
336
- (0, core_js_1.output)({ phase_number: decimalPhase, after_phase: afterPhase, name: description, slug, directory: `.planning/phases/${dirName}` }, raw, decimalPhase);
337
466
  }
338
467
  // ─── Phase remove ───────────────────────────────────────────────────────────
339
468
  function cmdPhaseRemove(cwd, targetPhase, options, raw) {
340
469
  if (!targetPhase) {
341
470
  (0, core_js_1.error)('phase number required for phase remove');
342
471
  }
343
- const roadmapPath = node_path_1.default.join(cwd, '.planning', 'ROADMAP.md');
344
- const phasesDir = node_path_1.default.join(cwd, '.planning', 'phases');
472
+ const rmPath = (0, core_js_1.roadmapPath)(cwd);
473
+ const phasesDirPath = (0, core_js_1.phasesPath)(cwd);
345
474
  const force = options.force || false;
346
- if (!node_fs_1.default.existsSync(roadmapPath)) {
475
+ if (!node_fs_1.default.existsSync(rmPath)) {
347
476
  (0, core_js_1.error)('ROADMAP.md not found');
348
477
  }
349
478
  const normalized = (0, core_js_1.normalizePhaseName)(targetPhase);
350
479
  const isDecimal = targetPhase.includes('.');
351
480
  let targetDir = null;
352
481
  try {
353
- const entries = node_fs_1.default.readdirSync(phasesDir, { withFileTypes: true });
354
- const dirs = entries.filter(e => e.isDirectory()).map(e => e.name).sort((a, b) => (0, core_js_1.comparePhaseNum)(a, b));
482
+ const dirs = (0, core_js_1.listSubDirs)(phasesDirPath, true);
355
483
  targetDir = dirs.find(d => d.startsWith(normalized + '-') || d === normalized) || null;
356
484
  }
357
485
  catch (e) {
358
486
  /* optional op, ignore */
359
- if (process.env.MAXSIM_DEBUG)
360
- console.error(e);
487
+ (0, core_js_1.debugLog)(e);
361
488
  }
362
489
  if (targetDir && !force) {
363
- const targetPath = node_path_1.default.join(phasesDir, targetDir);
490
+ const targetPath = node_path_1.default.join(phasesDirPath, targetDir);
364
491
  const files = node_fs_1.default.readdirSync(targetPath);
365
- const summaries = files.filter(f => f.endsWith('-SUMMARY.md') || f === 'SUMMARY.md');
492
+ const summaries = files.filter(core_js_1.isSummaryFile);
366
493
  if (summaries.length > 0) {
367
494
  (0, core_js_1.error)(`Phase ${targetPhase} has ${summaries.length} executed plan(s). Use --force to remove anyway.`);
368
495
  }
369
496
  }
370
497
  if (targetDir) {
371
- node_fs_1.default.rmSync(node_path_1.default.join(phasesDir, targetDir), { recursive: true, force: true });
498
+ node_fs_1.default.rmSync(node_path_1.default.join(phasesDirPath, targetDir), { recursive: true, force: true });
372
499
  }
373
500
  const renamedDirs = [];
374
501
  const renamedFiles = [];
@@ -377,8 +504,7 @@ function cmdPhaseRemove(cwd, targetPhase, options, raw) {
377
504
  const baseInt = baseParts[0];
378
505
  const removedDecimal = parseInt(baseParts[1], 10);
379
506
  try {
380
- const entries = node_fs_1.default.readdirSync(phasesDir, { withFileTypes: true });
381
- const dirs = entries.filter(e => e.isDirectory()).map(e => e.name).sort((a, b) => (0, core_js_1.comparePhaseNum)(a, b));
507
+ const dirs = (0, core_js_1.listSubDirs)(phasesDirPath, true);
382
508
  const decPattern = new RegExp(`^${baseInt}\\.(\\d+)-(.+)$`);
383
509
  const toRename = [];
384
510
  for (const dir of dirs) {
@@ -393,13 +519,13 @@ function cmdPhaseRemove(cwd, targetPhase, options, raw) {
393
519
  const oldPhaseId = `${baseInt}.${item.oldDecimal}`;
394
520
  const newPhaseId = `${baseInt}.${newDecimal}`;
395
521
  const newDirName = `${baseInt}.${newDecimal}-${item.slug}`;
396
- node_fs_1.default.renameSync(node_path_1.default.join(phasesDir, item.dir), node_path_1.default.join(phasesDir, newDirName));
522
+ node_fs_1.default.renameSync(node_path_1.default.join(phasesDirPath, item.dir), node_path_1.default.join(phasesDirPath, newDirName));
397
523
  renamedDirs.push({ from: item.dir, to: newDirName });
398
- const dirFiles = node_fs_1.default.readdirSync(node_path_1.default.join(phasesDir, newDirName));
524
+ const dirFiles = node_fs_1.default.readdirSync(node_path_1.default.join(phasesDirPath, newDirName));
399
525
  for (const f of dirFiles) {
400
526
  if (f.includes(oldPhaseId)) {
401
527
  const newFileName = f.replace(oldPhaseId, newPhaseId);
402
- node_fs_1.default.renameSync(node_path_1.default.join(phasesDir, newDirName, f), node_path_1.default.join(phasesDir, newDirName, newFileName));
528
+ node_fs_1.default.renameSync(node_path_1.default.join(phasesDirPath, newDirName, f), node_path_1.default.join(phasesDirPath, newDirName, newFileName));
403
529
  renamedFiles.push({ from: f, to: newFileName });
404
530
  }
405
531
  }
@@ -407,15 +533,13 @@ function cmdPhaseRemove(cwd, targetPhase, options, raw) {
407
533
  }
408
534
  catch (e) {
409
535
  /* optional op, ignore */
410
- if (process.env.MAXSIM_DEBUG)
411
- console.error(e);
536
+ (0, core_js_1.debugLog)(e);
412
537
  }
413
538
  }
414
539
  else {
415
540
  const removedInt = parseInt(normalized, 10);
416
541
  try {
417
- const entries = node_fs_1.default.readdirSync(phasesDir, { withFileTypes: true });
418
- const dirs = entries.filter(e => e.isDirectory()).map(e => e.name).sort((a, b) => (0, core_js_1.comparePhaseNum)(a, b));
542
+ const dirs = (0, core_js_1.listSubDirs)(phasesDirPath, true);
419
543
  const toRename = [];
420
544
  for (const dir of dirs) {
421
545
  const dm = dir.match(/^(\d+)([A-Z])?(?:\.(\d+))?-(.+)$/i);
@@ -446,13 +570,13 @@ function cmdPhaseRemove(cwd, targetPhase, options, raw) {
446
570
  const oldPrefix = `${oldPadded}${letterSuffix}${decimalSuffix}`;
447
571
  const newPrefix = `${newPadded}${letterSuffix}${decimalSuffix}`;
448
572
  const newDirName = `${newPrefix}-${item.slug}`;
449
- node_fs_1.default.renameSync(node_path_1.default.join(phasesDir, item.dir), node_path_1.default.join(phasesDir, newDirName));
573
+ node_fs_1.default.renameSync(node_path_1.default.join(phasesDirPath, item.dir), node_path_1.default.join(phasesDirPath, newDirName));
450
574
  renamedDirs.push({ from: item.dir, to: newDirName });
451
- const dirFiles = node_fs_1.default.readdirSync(node_path_1.default.join(phasesDir, newDirName));
575
+ const dirFiles = node_fs_1.default.readdirSync(node_path_1.default.join(phasesDirPath, newDirName));
452
576
  for (const f of dirFiles) {
453
577
  if (f.startsWith(oldPrefix)) {
454
578
  const newFileName = newPrefix + f.slice(oldPrefix.length);
455
- node_fs_1.default.renameSync(node_path_1.default.join(phasesDir, newDirName, f), node_path_1.default.join(phasesDir, newDirName, newFileName));
579
+ node_fs_1.default.renameSync(node_path_1.default.join(phasesDirPath, newDirName, f), node_path_1.default.join(phasesDirPath, newDirName, newFileName));
456
580
  renamedFiles.push({ from: f, to: newFileName });
457
581
  }
458
582
  }
@@ -460,13 +584,12 @@ function cmdPhaseRemove(cwd, targetPhase, options, raw) {
460
584
  }
461
585
  catch (e) {
462
586
  /* optional op, ignore */
463
- if (process.env.MAXSIM_DEBUG)
464
- console.error(e);
587
+ (0, core_js_1.debugLog)(e);
465
588
  }
466
589
  }
467
590
  // Update ROADMAP.md
468
- let roadmapContent = node_fs_1.default.readFileSync(roadmapPath, 'utf-8');
469
- const targetEscaped = targetPhase.replace(/\./g, '\\.');
591
+ let roadmapContent = node_fs_1.default.readFileSync(rmPath, 'utf-8');
592
+ const targetEscaped = (0, core_js_1.escapePhaseNum)(targetPhase);
470
593
  const sectionPattern = new RegExp(`\\n?#{2,4}\\s*Phase\\s+${targetEscaped}\\s*:[\\s\\S]*?(?=\\n#{2,4}\\s+Phase\\s+\\d|$)`, 'i');
471
594
  roadmapContent = roadmapContent.replace(sectionPattern, '');
472
595
  const checkboxPattern = new RegExp(`\\n?-\\s*\\[[ x]\\]\\s*.*Phase\\s+${targetEscaped}[:\\s][^\\n]*`, 'gi');
@@ -489,11 +612,11 @@ function cmdPhaseRemove(cwd, targetPhase, options, raw) {
489
612
  roadmapContent = roadmapContent.replace(new RegExp(`(Depends on:\\*\\*\\s*Phase\\s+)${oldStr}\\b`, 'gi'), `$1${newStr}`);
490
613
  }
491
614
  }
492
- node_fs_1.default.writeFileSync(roadmapPath, roadmapContent, 'utf-8');
615
+ node_fs_1.default.writeFileSync(rmPath, roadmapContent, 'utf-8');
493
616
  // Update STATE.md phase count
494
- const statePath = node_path_1.default.join(cwd, '.planning', 'STATE.md');
495
- if (node_fs_1.default.existsSync(statePath)) {
496
- let stateContent = node_fs_1.default.readFileSync(statePath, 'utf-8');
617
+ const stPath = (0, core_js_1.statePath)(cwd);
618
+ if (node_fs_1.default.existsSync(stPath)) {
619
+ let stateContent = node_fs_1.default.readFileSync(stPath, 'utf-8');
497
620
  const totalPattern = /(\*\*Total Phases:\*\*\s*)(\d+)/;
498
621
  const totalMatch = stateContent.match(totalPattern);
499
622
  if (totalMatch) {
@@ -506,7 +629,7 @@ function cmdPhaseRemove(cwd, targetPhase, options, raw) {
506
629
  const oldTotal = parseInt(ofMatch[2], 10);
507
630
  stateContent = stateContent.replace(ofPattern, `$1${oldTotal - 1}$3`);
508
631
  }
509
- node_fs_1.default.writeFileSync(statePath, stateContent, 'utf-8');
632
+ node_fs_1.default.writeFileSync(stPath, stateContent, 'utf-8');
510
633
  }
511
634
  (0, core_js_1.output)({
512
635
  removed: targetPhase,
@@ -514,7 +637,7 @@ function cmdPhaseRemove(cwd, targetPhase, options, raw) {
514
637
  renamed_directories: renamedDirs,
515
638
  renamed_files: renamedFiles,
516
639
  roadmap_updated: true,
517
- state_updated: node_fs_1.default.existsSync(statePath),
640
+ state_updated: node_fs_1.default.existsSync(stPath),
518
641
  }, raw);
519
642
  }
520
643
  // ─── Phase complete ─────────────────────────────────────────────────────────
@@ -522,89 +645,23 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
522
645
  if (!phaseNum) {
523
646
  (0, core_js_1.error)('phase number required for phase complete');
524
647
  }
525
- const roadmapPath = node_path_1.default.join(cwd, '.planning', 'ROADMAP.md');
526
- const statePath = node_path_1.default.join(cwd, '.planning', 'STATE.md');
527
- const phasesDir = node_path_1.default.join(cwd, '.planning', 'phases');
528
- const normalized = (0, core_js_1.normalizePhaseName)(phaseNum);
529
- const today = new Date().toISOString().split('T')[0];
530
- const phaseInfo = (0, core_js_1.findPhaseInternal)(cwd, phaseNum);
531
- if (!phaseInfo) {
532
- (0, core_js_1.error)(`Phase ${phaseNum} not found`);
533
- }
534
- const planCount = phaseInfo.plans.length;
535
- const summaryCount = phaseInfo.summaries.length;
536
- if (node_fs_1.default.existsSync(roadmapPath)) {
537
- let roadmapContent = node_fs_1.default.readFileSync(roadmapPath, 'utf-8');
538
- const checkboxPattern = new RegExp(`(-\\s*\\[)[ ](\\]\\s*.*Phase\\s+${phaseNum.replace('.', '\\.')}[:\\s][^\\n]*)`, 'i');
539
- roadmapContent = roadmapContent.replace(checkboxPattern, `$1x$2 (completed ${today})`);
540
- const phaseEscaped = phaseNum.replace('.', '\\.');
541
- const tablePattern = new RegExp(`(\\|\\s*${phaseEscaped}\\.?\\s[^|]*\\|[^|]*\\|)\\s*[^|]*(\\|)\\s*[^|]*(\\|)`, 'i');
542
- roadmapContent = roadmapContent.replace(tablePattern, `$1 Complete $2 ${today} $3`);
543
- const planCountPattern = new RegExp(`(#{2,4}\\s*Phase\\s+${phaseEscaped}[\\s\\S]*?\\*\\*Plans:\\*\\*\\s*)[^\\n]+`, 'i');
544
- roadmapContent = roadmapContent.replace(planCountPattern, `$1${summaryCount}/${planCount} plans complete`);
545
- node_fs_1.default.writeFileSync(roadmapPath, roadmapContent, 'utf-8');
546
- // Update REQUIREMENTS.md
547
- const reqPath = node_path_1.default.join(cwd, '.planning', 'REQUIREMENTS.md');
548
- if (node_fs_1.default.existsSync(reqPath)) {
549
- const reqMatch = roadmapContent.match(new RegExp(`Phase\\s+${phaseNum.replace('.', '\\.')}[\\s\\S]*?\\*\\*Requirements:\\*\\*\\s*([^\\n]+)`, 'i'));
550
- if (reqMatch) {
551
- const reqIds = reqMatch[1].replace(/[\[\]]/g, '').split(/[,\s]+/).map(r => r.trim()).filter(Boolean);
552
- let reqContent = node_fs_1.default.readFileSync(reqPath, 'utf-8');
553
- for (const reqId of reqIds) {
554
- reqContent = reqContent.replace(new RegExp(`(-\\s*\\[)[ ](\\]\\s*\\*\\*${reqId}\\*\\*)`, 'gi'), '$1x$2');
555
- reqContent = reqContent.replace(new RegExp(`(\\|\\s*${reqId}\\s*\\|[^|]+\\|)\\s*Pending\\s*(\\|)`, 'gi'), '$1 Complete $2');
556
- }
557
- node_fs_1.default.writeFileSync(reqPath, reqContent, 'utf-8');
558
- }
559
- }
560
- }
561
- // Find next phase
562
- let nextPhaseNum = null;
563
- let nextPhaseName = null;
564
- let isLastPhase = true;
565
648
  try {
566
- const entries = node_fs_1.default.readdirSync(phasesDir, { withFileTypes: true });
567
- const dirs = entries.filter(e => e.isDirectory()).map(e => e.name).sort((a, b) => (0, core_js_1.comparePhaseNum)(a, b));
568
- for (const dir of dirs) {
569
- const dm = dir.match(/^(\d+[A-Z]?(?:\.\d+)?)-?(.*)/i);
570
- if (dm) {
571
- if ((0, core_js_1.comparePhaseNum)(dm[1], phaseNum) > 0) {
572
- nextPhaseNum = dm[1];
573
- nextPhaseName = dm[2] || null;
574
- isLastPhase = false;
575
- break;
576
- }
577
- }
578
- }
649
+ const result = phaseCompleteCore(cwd, phaseNum);
650
+ (0, core_js_1.output)({
651
+ completed_phase: result.completed_phase,
652
+ phase_name: result.phase_name,
653
+ plans_executed: result.plans_executed,
654
+ next_phase: result.next_phase,
655
+ next_phase_name: result.next_phase_name,
656
+ is_last_phase: result.is_last_phase,
657
+ date: result.date,
658
+ roadmap_updated: result.roadmap_updated,
659
+ state_updated: result.state_updated,
660
+ }, raw);
579
661
  }
580
662
  catch (e) {
581
- /* optional op, ignore */
582
- if (process.env.MAXSIM_DEBUG)
583
- console.error(e);
663
+ (0, core_js_1.rethrowCliSignals)(e);
664
+ (0, core_js_1.error)(e.message);
584
665
  }
585
- // Update STATE.md
586
- if (node_fs_1.default.existsSync(statePath)) {
587
- let stateContent = node_fs_1.default.readFileSync(statePath, 'utf-8');
588
- stateContent = stateContent.replace(/(\*\*Current Phase:\*\*\s*).*/, `$1${nextPhaseNum || phaseNum}`);
589
- if (nextPhaseName) {
590
- stateContent = stateContent.replace(/(\*\*Current Phase Name:\*\*\s*).*/, `$1${nextPhaseName.replace(/-/g, ' ')}`);
591
- }
592
- stateContent = stateContent.replace(/(\*\*Status:\*\*\s*).*/, `$1${isLastPhase ? 'Milestone complete' : 'Ready to plan'}`);
593
- stateContent = stateContent.replace(/(\*\*Current Plan:\*\*\s*).*/, `$1Not started`);
594
- stateContent = stateContent.replace(/(\*\*Last Activity:\*\*\s*).*/, `$1${today}`);
595
- stateContent = stateContent.replace(/(\*\*Last Activity Description:\*\*\s*).*/, `$1Phase ${phaseNum} complete${nextPhaseNum ? `, transitioned to Phase ${nextPhaseNum}` : ''}`);
596
- node_fs_1.default.writeFileSync(statePath, stateContent, 'utf-8');
597
- }
598
- (0, core_js_1.output)({
599
- completed_phase: phaseNum,
600
- phase_name: phaseInfo.phase_name,
601
- plans_executed: `${summaryCount}/${planCount}`,
602
- next_phase: nextPhaseNum,
603
- next_phase_name: nextPhaseName,
604
- is_last_phase: isLastPhase,
605
- date: today,
606
- roadmap_updated: node_fs_1.default.existsSync(roadmapPath),
607
- state_updated: node_fs_1.default.existsSync(statePath),
608
- }, raw);
609
666
  }
610
667
  //# sourceMappingURL=phase.js.map