maxsimcli 3.10.3 → 3.11.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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/assets/CHANGELOG.md +7 -0
- package/dist/assets/dashboard/server.js +5 -1
- package/dist/assets/templates/workflows/execute-plan.md +10 -0
- package/dist/cli.cjs +554 -439
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +5 -0
- package/dist/cli.js.map +1 -1
- package/dist/core/commands.d.ts +7 -0
- package/dist/core/commands.d.ts.map +1 -1
- package/dist/core/commands.js +36 -34
- package/dist/core/commands.js.map +1 -1
- package/dist/core/core.d.ts +21 -1
- package/dist/core/core.d.ts.map +1 -1
- package/dist/core/core.js +81 -36
- package/dist/core/core.js.map +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/init.d.ts +2 -2
- package/dist/core/init.d.ts.map +1 -1
- package/dist/core/init.js +33 -51
- package/dist/core/init.js.map +1 -1
- package/dist/core/milestone.d.ts.map +1 -1
- package/dist/core/milestone.js +15 -20
- package/dist/core/milestone.js.map +1 -1
- package/dist/core/phase.d.ts +33 -0
- package/dist/core/phase.d.ts.map +1 -1
- package/dist/core/phase.js +275 -224
- package/dist/core/phase.js.map +1 -1
- package/dist/core/roadmap.d.ts.map +1 -1
- package/dist/core/roadmap.js +16 -18
- package/dist/core/roadmap.js.map +1 -1
- package/dist/core/state.d.ts +5 -0
- package/dist/core/state.d.ts.map +1 -1
- package/dist/core/state.js +44 -37
- package/dist/core/state.js.map +1 -1
- package/dist/core/template.d.ts.map +1 -1
- package/dist/core/template.js +1 -1
- package/dist/core/template.js.map +1 -1
- package/dist/core/types.d.ts +3 -2
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/core/verify.d.ts.map +1 -1
- package/dist/core/verify.js +61 -80
- package/dist/core/verify.js.map +1 -1
- package/dist/install.cjs +23 -0
- package/dist/install.cjs.map +1 -1
- package/dist/install.js +37 -0
- package/dist/install.js.map +1 -1
- package/dist/mcp/index.d.ts +12 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +21 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/phase-tools.d.ts +13 -0
- package/dist/mcp/phase-tools.d.ts.map +1 -0
- package/dist/mcp/phase-tools.js +164 -0
- package/dist/mcp/phase-tools.js.map +1 -0
- package/dist/mcp/state-tools.d.ts +13 -0
- package/dist/mcp/state-tools.d.ts.map +1 -0
- package/dist/mcp/state-tools.js +185 -0
- package/dist/mcp/state-tools.js.map +1 -0
- package/dist/mcp/todo-tools.d.ts +13 -0
- package/dist/mcp/todo-tools.d.ts.map +1 -0
- package/dist/mcp/todo-tools.js +143 -0
- package/dist/mcp/todo-tools.js.map +1 -0
- package/dist/mcp/utils.d.ts +27 -0
- package/dist/mcp/utils.d.ts.map +1 -0
- package/dist/mcp/utils.js +82 -0
- package/dist/mcp/utils.js.map +1 -0
- package/dist/mcp-server.cjs +11806 -0
- package/dist/mcp-server.cjs.map +1 -0
- package/dist/mcp-server.d.cts +2 -0
- package/dist/mcp-server.d.ts +12 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +31 -0
- package/dist/mcp-server.js.map +1 -0
- package/package.json +5 -3
package/dist/core/phase.js
CHANGED
|
@@ -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
|
|
227
|
+
const phasesDirPath = (0, core_js_1.phasesPath)(cwd);
|
|
26
228
|
const { type, phase, includeArchived } = options;
|
|
27
|
-
if (!node_fs_1.default.existsSync(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
263
|
+
filtered = dirFiles.filter(core_js_1.isPlanFile);
|
|
63
264
|
}
|
|
64
265
|
else if (type === 'summaries') {
|
|
65
|
-
filtered = dirFiles.filter(
|
|
266
|
+
filtered = dirFiles.filter(core_js_1.isSummaryFile);
|
|
66
267
|
}
|
|
67
268
|
else {
|
|
68
269
|
filtered = dirFiles;
|
|
@@ -85,15 +286,14 @@ function cmdPhasesList(cwd, options, raw) {
|
|
|
85
286
|
}
|
|
86
287
|
// ─── Next decimal ───────────────────────────────────────────────────────────
|
|
87
288
|
function cmdPhaseNextDecimal(cwd, basePhase, raw) {
|
|
88
|
-
const
|
|
289
|
+
const phasesDirPath = (0, core_js_1.phasesPath)(cwd);
|
|
89
290
|
const normalized = (0, core_js_1.normalizePhaseName)(basePhase);
|
|
90
|
-
if (!node_fs_1.default.existsSync(
|
|
291
|
+
if (!node_fs_1.default.existsSync(phasesDirPath)) {
|
|
91
292
|
(0, core_js_1.output)({ found: false, base_phase: normalized, next: `${normalized}.1`, existing: [] }, raw, `${normalized}.1`);
|
|
92
293
|
return;
|
|
93
294
|
}
|
|
94
295
|
try {
|
|
95
|
-
const
|
|
96
|
-
const dirs = entries.filter(e => e.isDirectory()).map(e => e.name);
|
|
296
|
+
const dirs = (0, core_js_1.listSubDirs)(phasesDirPath);
|
|
97
297
|
const baseExists = dirs.some(d => d.startsWith(normalized + '-') || d === normalized);
|
|
98
298
|
const decimalPattern = new RegExp(`^${normalized}\\.(\\d+)`);
|
|
99
299
|
const existingDecimals = [];
|
|
@@ -128,12 +328,11 @@ function cmdFindPhase(cwd, phase, raw) {
|
|
|
128
328
|
if (!phase) {
|
|
129
329
|
(0, core_js_1.error)('phase identifier required');
|
|
130
330
|
}
|
|
131
|
-
const
|
|
331
|
+
const phasesDirPath = (0, core_js_1.phasesPath)(cwd);
|
|
132
332
|
const normalized = (0, core_js_1.normalizePhaseName)(phase);
|
|
133
333
|
const notFound = { found: false, directory: null, phase_number: null, phase_name: null, plans: [], summaries: [] };
|
|
134
334
|
try {
|
|
135
|
-
const
|
|
136
|
-
const dirs = entries.filter(e => e.isDirectory()).map(e => e.name).sort((a, b) => (0, core_js_1.comparePhaseNum)(a, b));
|
|
335
|
+
const dirs = (0, core_js_1.listSubDirs)(phasesDirPath, true);
|
|
137
336
|
const match = dirs.find(d => d.startsWith(normalized));
|
|
138
337
|
if (!match) {
|
|
139
338
|
(0, core_js_1.output)(notFound, raw, '');
|
|
@@ -142,10 +341,10 @@ function cmdFindPhase(cwd, phase, raw) {
|
|
|
142
341
|
const dirMatch = match.match(/^(\d+[A-Z]?(?:\.\d+)?)-?(.*)/i);
|
|
143
342
|
const phaseNumber = dirMatch ? dirMatch[1] : normalized;
|
|
144
343
|
const phaseName = dirMatch && dirMatch[2] ? dirMatch[2] : null;
|
|
145
|
-
const phaseDir = node_path_1.default.join(
|
|
344
|
+
const phaseDir = node_path_1.default.join(phasesDirPath, match);
|
|
146
345
|
const phaseFiles = node_fs_1.default.readdirSync(phaseDir);
|
|
147
|
-
const plans = phaseFiles.filter(
|
|
148
|
-
const summaries = phaseFiles.filter(
|
|
346
|
+
const plans = phaseFiles.filter(core_js_1.isPlanFile).sort();
|
|
347
|
+
const summaries = phaseFiles.filter(core_js_1.isSummaryFile).sort();
|
|
149
348
|
const result = {
|
|
150
349
|
found: true,
|
|
151
350
|
directory: node_path_1.default.join('.planning', 'phases', match),
|
|
@@ -165,38 +364,36 @@ function cmdPhasePlanIndex(cwd, phase, raw) {
|
|
|
165
364
|
if (!phase) {
|
|
166
365
|
(0, core_js_1.error)('phase required for phase-plan-index');
|
|
167
366
|
}
|
|
168
|
-
const
|
|
367
|
+
const phasesDirPath = (0, core_js_1.phasesPath)(cwd);
|
|
169
368
|
const normalized = (0, core_js_1.normalizePhaseName)(phase);
|
|
170
369
|
let phaseDir = null;
|
|
171
370
|
let phaseDirName = null;
|
|
172
371
|
try {
|
|
173
|
-
const
|
|
174
|
-
const dirs = entries.filter(e => e.isDirectory()).map(e => e.name).sort((a, b) => (0, core_js_1.comparePhaseNum)(a, b));
|
|
372
|
+
const dirs = (0, core_js_1.listSubDirs)(phasesDirPath, true);
|
|
175
373
|
const match = dirs.find(d => d.startsWith(normalized));
|
|
176
374
|
if (match) {
|
|
177
|
-
phaseDir = node_path_1.default.join(
|
|
375
|
+
phaseDir = node_path_1.default.join(phasesDirPath, match);
|
|
178
376
|
phaseDirName = match;
|
|
179
377
|
}
|
|
180
378
|
}
|
|
181
379
|
catch (e) {
|
|
182
380
|
/* optional op, ignore */
|
|
183
|
-
|
|
184
|
-
console.error(e);
|
|
381
|
+
(0, core_js_1.debugLog)(e);
|
|
185
382
|
}
|
|
186
383
|
if (!phaseDir) {
|
|
187
384
|
(0, core_js_1.output)({ phase: normalized, error: 'Phase not found', plans: [], waves: {}, incomplete: [], has_checkpoints: false }, raw);
|
|
188
385
|
return;
|
|
189
386
|
}
|
|
190
387
|
const phaseFiles = node_fs_1.default.readdirSync(phaseDir);
|
|
191
|
-
const planFiles = phaseFiles.filter(
|
|
192
|
-
const summaryFiles = phaseFiles.filter(
|
|
193
|
-
const completedPlanIds = new Set(summaryFiles.map(
|
|
388
|
+
const planFiles = phaseFiles.filter(core_js_1.isPlanFile).sort();
|
|
389
|
+
const summaryFiles = phaseFiles.filter(core_js_1.isSummaryFile);
|
|
390
|
+
const completedPlanIds = new Set(summaryFiles.map(core_js_1.summaryId));
|
|
194
391
|
const plans = [];
|
|
195
392
|
const waves = {};
|
|
196
393
|
const incomplete = [];
|
|
197
394
|
let hasCheckpoints = false;
|
|
198
395
|
for (const planFile of planFiles) {
|
|
199
|
-
const
|
|
396
|
+
const id = (0, core_js_1.planId)(planFile);
|
|
200
397
|
const planPath = node_path_1.default.join(phaseDir, planFile);
|
|
201
398
|
const content = node_fs_1.default.readFileSync(planPath, 'utf-8');
|
|
202
399
|
const fm = (0, frontmatter_js_1.extractFrontmatter)(content);
|
|
@@ -214,12 +411,12 @@ function cmdPhasePlanIndex(cwd, phase, raw) {
|
|
|
214
411
|
if (fm['files-modified']) {
|
|
215
412
|
filesModified = Array.isArray(fm['files-modified']) ? fm['files-modified'] : [fm['files-modified']];
|
|
216
413
|
}
|
|
217
|
-
const hasSummary = completedPlanIds.has(
|
|
414
|
+
const hasSummary = completedPlanIds.has(id);
|
|
218
415
|
if (!hasSummary) {
|
|
219
|
-
incomplete.push(
|
|
416
|
+
incomplete.push(id);
|
|
220
417
|
}
|
|
221
418
|
const plan = {
|
|
222
|
-
id
|
|
419
|
+
id,
|
|
223
420
|
wave,
|
|
224
421
|
autonomous,
|
|
225
422
|
objective: fm.objective || null,
|
|
@@ -232,7 +429,7 @@ function cmdPhasePlanIndex(cwd, phase, raw) {
|
|
|
232
429
|
if (!waves[waveKey]) {
|
|
233
430
|
waves[waveKey] = [];
|
|
234
431
|
}
|
|
235
|
-
waves[waveKey].push(
|
|
432
|
+
waves[waveKey].push(id);
|
|
236
433
|
}
|
|
237
434
|
(0, core_js_1.output)({ phase: normalized, plans, waves, incomplete, has_checkpoints: hasCheckpoints }, raw);
|
|
238
435
|
}
|
|
@@ -241,134 +438,59 @@ function cmdPhaseAdd(cwd, description, raw) {
|
|
|
241
438
|
if (!description) {
|
|
242
439
|
(0, core_js_1.error)('description required for phase add');
|
|
243
440
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
(0, core_js_1.
|
|
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);
|
|
441
|
+
try {
|
|
442
|
+
const result = phaseAddCore(cwd, description, { includeStubs: false });
|
|
443
|
+
(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
444
|
}
|
|
270
|
-
|
|
271
|
-
|
|
445
|
+
catch (e) {
|
|
446
|
+
(0, core_js_1.error)(e.message);
|
|
272
447
|
}
|
|
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
448
|
}
|
|
276
449
|
// ─── Phase insert ───────────────────────────────────────────────────────────
|
|
277
450
|
function cmdPhaseInsert(cwd, afterPhase, description, raw) {
|
|
278
451
|
if (!afterPhase || !description) {
|
|
279
452
|
(0, core_js_1.error)('after-phase and description required for phase insert');
|
|
280
453
|
}
|
|
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
454
|
try {
|
|
298
|
-
const
|
|
299
|
-
|
|
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
|
-
}
|
|
455
|
+
const result = phaseInsertCore(cwd, afterPhase, description, { includeStubs: false });
|
|
456
|
+
(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
457
|
}
|
|
307
458
|
catch (e) {
|
|
308
|
-
|
|
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;
|
|
459
|
+
(0, core_js_1.error)(e.message);
|
|
333
460
|
}
|
|
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
461
|
}
|
|
338
462
|
// ─── Phase remove ───────────────────────────────────────────────────────────
|
|
339
463
|
function cmdPhaseRemove(cwd, targetPhase, options, raw) {
|
|
340
464
|
if (!targetPhase) {
|
|
341
465
|
(0, core_js_1.error)('phase number required for phase remove');
|
|
342
466
|
}
|
|
343
|
-
const
|
|
344
|
-
const
|
|
467
|
+
const rmPath = (0, core_js_1.roadmapPath)(cwd);
|
|
468
|
+
const phasesDirPath = (0, core_js_1.phasesPath)(cwd);
|
|
345
469
|
const force = options.force || false;
|
|
346
|
-
if (!node_fs_1.default.existsSync(
|
|
470
|
+
if (!node_fs_1.default.existsSync(rmPath)) {
|
|
347
471
|
(0, core_js_1.error)('ROADMAP.md not found');
|
|
348
472
|
}
|
|
349
473
|
const normalized = (0, core_js_1.normalizePhaseName)(targetPhase);
|
|
350
474
|
const isDecimal = targetPhase.includes('.');
|
|
351
475
|
let targetDir = null;
|
|
352
476
|
try {
|
|
353
|
-
const
|
|
354
|
-
const dirs = entries.filter(e => e.isDirectory()).map(e => e.name).sort((a, b) => (0, core_js_1.comparePhaseNum)(a, b));
|
|
477
|
+
const dirs = (0, core_js_1.listSubDirs)(phasesDirPath, true);
|
|
355
478
|
targetDir = dirs.find(d => d.startsWith(normalized + '-') || d === normalized) || null;
|
|
356
479
|
}
|
|
357
480
|
catch (e) {
|
|
358
481
|
/* optional op, ignore */
|
|
359
|
-
|
|
360
|
-
console.error(e);
|
|
482
|
+
(0, core_js_1.debugLog)(e);
|
|
361
483
|
}
|
|
362
484
|
if (targetDir && !force) {
|
|
363
|
-
const targetPath = node_path_1.default.join(
|
|
485
|
+
const targetPath = node_path_1.default.join(phasesDirPath, targetDir);
|
|
364
486
|
const files = node_fs_1.default.readdirSync(targetPath);
|
|
365
|
-
const summaries = files.filter(
|
|
487
|
+
const summaries = files.filter(core_js_1.isSummaryFile);
|
|
366
488
|
if (summaries.length > 0) {
|
|
367
489
|
(0, core_js_1.error)(`Phase ${targetPhase} has ${summaries.length} executed plan(s). Use --force to remove anyway.`);
|
|
368
490
|
}
|
|
369
491
|
}
|
|
370
492
|
if (targetDir) {
|
|
371
|
-
node_fs_1.default.rmSync(node_path_1.default.join(
|
|
493
|
+
node_fs_1.default.rmSync(node_path_1.default.join(phasesDirPath, targetDir), { recursive: true, force: true });
|
|
372
494
|
}
|
|
373
495
|
const renamedDirs = [];
|
|
374
496
|
const renamedFiles = [];
|
|
@@ -377,8 +499,7 @@ function cmdPhaseRemove(cwd, targetPhase, options, raw) {
|
|
|
377
499
|
const baseInt = baseParts[0];
|
|
378
500
|
const removedDecimal = parseInt(baseParts[1], 10);
|
|
379
501
|
try {
|
|
380
|
-
const
|
|
381
|
-
const dirs = entries.filter(e => e.isDirectory()).map(e => e.name).sort((a, b) => (0, core_js_1.comparePhaseNum)(a, b));
|
|
502
|
+
const dirs = (0, core_js_1.listSubDirs)(phasesDirPath, true);
|
|
382
503
|
const decPattern = new RegExp(`^${baseInt}\\.(\\d+)-(.+)$`);
|
|
383
504
|
const toRename = [];
|
|
384
505
|
for (const dir of dirs) {
|
|
@@ -393,13 +514,13 @@ function cmdPhaseRemove(cwd, targetPhase, options, raw) {
|
|
|
393
514
|
const oldPhaseId = `${baseInt}.${item.oldDecimal}`;
|
|
394
515
|
const newPhaseId = `${baseInt}.${newDecimal}`;
|
|
395
516
|
const newDirName = `${baseInt}.${newDecimal}-${item.slug}`;
|
|
396
|
-
node_fs_1.default.renameSync(node_path_1.default.join(
|
|
517
|
+
node_fs_1.default.renameSync(node_path_1.default.join(phasesDirPath, item.dir), node_path_1.default.join(phasesDirPath, newDirName));
|
|
397
518
|
renamedDirs.push({ from: item.dir, to: newDirName });
|
|
398
|
-
const dirFiles = node_fs_1.default.readdirSync(node_path_1.default.join(
|
|
519
|
+
const dirFiles = node_fs_1.default.readdirSync(node_path_1.default.join(phasesDirPath, newDirName));
|
|
399
520
|
for (const f of dirFiles) {
|
|
400
521
|
if (f.includes(oldPhaseId)) {
|
|
401
522
|
const newFileName = f.replace(oldPhaseId, newPhaseId);
|
|
402
|
-
node_fs_1.default.renameSync(node_path_1.default.join(
|
|
523
|
+
node_fs_1.default.renameSync(node_path_1.default.join(phasesDirPath, newDirName, f), node_path_1.default.join(phasesDirPath, newDirName, newFileName));
|
|
403
524
|
renamedFiles.push({ from: f, to: newFileName });
|
|
404
525
|
}
|
|
405
526
|
}
|
|
@@ -407,15 +528,13 @@ function cmdPhaseRemove(cwd, targetPhase, options, raw) {
|
|
|
407
528
|
}
|
|
408
529
|
catch (e) {
|
|
409
530
|
/* optional op, ignore */
|
|
410
|
-
|
|
411
|
-
console.error(e);
|
|
531
|
+
(0, core_js_1.debugLog)(e);
|
|
412
532
|
}
|
|
413
533
|
}
|
|
414
534
|
else {
|
|
415
535
|
const removedInt = parseInt(normalized, 10);
|
|
416
536
|
try {
|
|
417
|
-
const
|
|
418
|
-
const dirs = entries.filter(e => e.isDirectory()).map(e => e.name).sort((a, b) => (0, core_js_1.comparePhaseNum)(a, b));
|
|
537
|
+
const dirs = (0, core_js_1.listSubDirs)(phasesDirPath, true);
|
|
419
538
|
const toRename = [];
|
|
420
539
|
for (const dir of dirs) {
|
|
421
540
|
const dm = dir.match(/^(\d+)([A-Z])?(?:\.(\d+))?-(.+)$/i);
|
|
@@ -446,13 +565,13 @@ function cmdPhaseRemove(cwd, targetPhase, options, raw) {
|
|
|
446
565
|
const oldPrefix = `${oldPadded}${letterSuffix}${decimalSuffix}`;
|
|
447
566
|
const newPrefix = `${newPadded}${letterSuffix}${decimalSuffix}`;
|
|
448
567
|
const newDirName = `${newPrefix}-${item.slug}`;
|
|
449
|
-
node_fs_1.default.renameSync(node_path_1.default.join(
|
|
568
|
+
node_fs_1.default.renameSync(node_path_1.default.join(phasesDirPath, item.dir), node_path_1.default.join(phasesDirPath, newDirName));
|
|
450
569
|
renamedDirs.push({ from: item.dir, to: newDirName });
|
|
451
|
-
const dirFiles = node_fs_1.default.readdirSync(node_path_1.default.join(
|
|
570
|
+
const dirFiles = node_fs_1.default.readdirSync(node_path_1.default.join(phasesDirPath, newDirName));
|
|
452
571
|
for (const f of dirFiles) {
|
|
453
572
|
if (f.startsWith(oldPrefix)) {
|
|
454
573
|
const newFileName = newPrefix + f.slice(oldPrefix.length);
|
|
455
|
-
node_fs_1.default.renameSync(node_path_1.default.join(
|
|
574
|
+
node_fs_1.default.renameSync(node_path_1.default.join(phasesDirPath, newDirName, f), node_path_1.default.join(phasesDirPath, newDirName, newFileName));
|
|
456
575
|
renamedFiles.push({ from: f, to: newFileName });
|
|
457
576
|
}
|
|
458
577
|
}
|
|
@@ -460,13 +579,12 @@ function cmdPhaseRemove(cwd, targetPhase, options, raw) {
|
|
|
460
579
|
}
|
|
461
580
|
catch (e) {
|
|
462
581
|
/* optional op, ignore */
|
|
463
|
-
|
|
464
|
-
console.error(e);
|
|
582
|
+
(0, core_js_1.debugLog)(e);
|
|
465
583
|
}
|
|
466
584
|
}
|
|
467
585
|
// Update ROADMAP.md
|
|
468
|
-
let roadmapContent = node_fs_1.default.readFileSync(
|
|
469
|
-
const targetEscaped =
|
|
586
|
+
let roadmapContent = node_fs_1.default.readFileSync(rmPath, 'utf-8');
|
|
587
|
+
const targetEscaped = (0, core_js_1.escapePhaseNum)(targetPhase);
|
|
470
588
|
const sectionPattern = new RegExp(`\\n?#{2,4}\\s*Phase\\s+${targetEscaped}\\s*:[\\s\\S]*?(?=\\n#{2,4}\\s+Phase\\s+\\d|$)`, 'i');
|
|
471
589
|
roadmapContent = roadmapContent.replace(sectionPattern, '');
|
|
472
590
|
const checkboxPattern = new RegExp(`\\n?-\\s*\\[[ x]\\]\\s*.*Phase\\s+${targetEscaped}[:\\s][^\\n]*`, 'gi');
|
|
@@ -489,11 +607,11 @@ function cmdPhaseRemove(cwd, targetPhase, options, raw) {
|
|
|
489
607
|
roadmapContent = roadmapContent.replace(new RegExp(`(Depends on:\\*\\*\\s*Phase\\s+)${oldStr}\\b`, 'gi'), `$1${newStr}`);
|
|
490
608
|
}
|
|
491
609
|
}
|
|
492
|
-
node_fs_1.default.writeFileSync(
|
|
610
|
+
node_fs_1.default.writeFileSync(rmPath, roadmapContent, 'utf-8');
|
|
493
611
|
// Update STATE.md phase count
|
|
494
|
-
const
|
|
495
|
-
if (node_fs_1.default.existsSync(
|
|
496
|
-
let stateContent = node_fs_1.default.readFileSync(
|
|
612
|
+
const stPath = (0, core_js_1.statePath)(cwd);
|
|
613
|
+
if (node_fs_1.default.existsSync(stPath)) {
|
|
614
|
+
let stateContent = node_fs_1.default.readFileSync(stPath, 'utf-8');
|
|
497
615
|
const totalPattern = /(\*\*Total Phases:\*\*\s*)(\d+)/;
|
|
498
616
|
const totalMatch = stateContent.match(totalPattern);
|
|
499
617
|
if (totalMatch) {
|
|
@@ -506,7 +624,7 @@ function cmdPhaseRemove(cwd, targetPhase, options, raw) {
|
|
|
506
624
|
const oldTotal = parseInt(ofMatch[2], 10);
|
|
507
625
|
stateContent = stateContent.replace(ofPattern, `$1${oldTotal - 1}$3`);
|
|
508
626
|
}
|
|
509
|
-
node_fs_1.default.writeFileSync(
|
|
627
|
+
node_fs_1.default.writeFileSync(stPath, stateContent, 'utf-8');
|
|
510
628
|
}
|
|
511
629
|
(0, core_js_1.output)({
|
|
512
630
|
removed: targetPhase,
|
|
@@ -514,7 +632,7 @@ function cmdPhaseRemove(cwd, targetPhase, options, raw) {
|
|
|
514
632
|
renamed_directories: renamedDirs,
|
|
515
633
|
renamed_files: renamedFiles,
|
|
516
634
|
roadmap_updated: true,
|
|
517
|
-
state_updated: node_fs_1.default.existsSync(
|
|
635
|
+
state_updated: node_fs_1.default.existsSync(stPath),
|
|
518
636
|
}, raw);
|
|
519
637
|
}
|
|
520
638
|
// ─── Phase complete ─────────────────────────────────────────────────────────
|
|
@@ -522,89 +640,22 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|
|
522
640
|
if (!phaseNum) {
|
|
523
641
|
(0, core_js_1.error)('phase number required for phase complete');
|
|
524
642
|
}
|
|
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
643
|
try {
|
|
566
|
-
const
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
}
|
|
644
|
+
const result = phaseCompleteCore(cwd, phaseNum);
|
|
645
|
+
(0, core_js_1.output)({
|
|
646
|
+
completed_phase: result.completed_phase,
|
|
647
|
+
phase_name: result.phase_name,
|
|
648
|
+
plans_executed: result.plans_executed,
|
|
649
|
+
next_phase: result.next_phase,
|
|
650
|
+
next_phase_name: result.next_phase_name,
|
|
651
|
+
is_last_phase: result.is_last_phase,
|
|
652
|
+
date: result.date,
|
|
653
|
+
roadmap_updated: result.roadmap_updated,
|
|
654
|
+
state_updated: result.state_updated,
|
|
655
|
+
}, raw);
|
|
579
656
|
}
|
|
580
657
|
catch (e) {
|
|
581
|
-
|
|
582
|
-
if (process.env.MAXSIM_DEBUG)
|
|
583
|
-
console.error(e);
|
|
658
|
+
(0, core_js_1.error)(e.message);
|
|
584
659
|
}
|
|
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
660
|
}
|
|
610
661
|
//# sourceMappingURL=phase.js.map
|