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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/assets/CHANGELOG.md +26 -0
- package/dist/assets/dashboard/client/assets/index-CxFKStBk.css +32 -0
- package/dist/assets/dashboard/client/assets/{index-CZ8WC97G.js → index-wtQDvXzr.js} +64 -64
- package/dist/assets/dashboard/client/index.html +2 -2
- package/dist/assets/dashboard/server.js +5 -1
- package/dist/assets/templates/agents/AGENTS.md +82 -0
- package/dist/assets/templates/commands/maxsim/settings.md +1 -1
- package/dist/assets/templates/skills/code-review/SKILL.md +151 -0
- package/dist/assets/templates/skills/memory-management/SKILL.md +174 -0
- package/dist/assets/templates/skills/simplify/SKILL.md +137 -0
- package/dist/assets/templates/skills/using-maxsim/SKILL.md +115 -0
- package/dist/assets/templates/templates/config.json +1 -1
- package/dist/assets/templates/workflows/add-tests.md +3 -3
- package/dist/assets/templates/workflows/complete-milestone.md +1 -1
- package/dist/assets/templates/workflows/execute-phase.md +4 -14
- package/dist/assets/templates/workflows/execute-plan.md +10 -0
- package/dist/assets/templates/workflows/init-existing.md +7 -3
- package/dist/assets/templates/workflows/new-milestone.md +4 -0
- package/dist/assets/templates/workflows/new-project.md +6 -2
- package/dist/assets/templates/workflows/plan-phase.md +2 -2
- package/dist/assets/templates/workflows/settings.md +8 -4
- package/dist/assets/templates/workflows/verify-work.md +1 -1
- package/dist/cli.cjs +818 -599
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +78 -204
- 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 +40 -35
- package/dist/core/commands.js.map +1 -1
- package/dist/core/core.d.ts +39 -1
- package/dist/core/core.d.ts.map +1 -1
- package/dist/core/core.js +122 -47
- package/dist/core/core.js.map +1 -1
- package/dist/core/dashboard-launcher.d.ts +56 -0
- package/dist/core/dashboard-launcher.d.ts.map +1 -0
- package/dist/core/dashboard-launcher.js +243 -0
- package/dist/core/dashboard-launcher.js.map +1 -0
- package/dist/core/index.d.ts +4 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +20 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/init.d.ts +2 -3
- package/dist/core/init.d.ts.map +1 -1
- package/dist/core/init.js +33 -52
- 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 +282 -225
- package/dist/core/phase.js.map +1 -1
- package/dist/core/roadmap.d.ts.map +1 -1
- package/dist/core/roadmap.js +17 -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 +51 -42
- 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 +4 -4
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +1 -2
- 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/adapters.d.ts +15 -0
- package/dist/install/adapters.d.ts.map +1 -0
- package/dist/install/adapters.js +203 -0
- package/dist/install/adapters.js.map +1 -0
- package/dist/install/copy.d.ts +15 -0
- package/dist/install/copy.d.ts.map +1 -0
- package/dist/install/copy.js +191 -0
- package/dist/install/copy.js.map +1 -0
- package/dist/install/dashboard.d.ts +16 -0
- package/dist/install/dashboard.d.ts.map +1 -0
- package/dist/install/dashboard.js +273 -0
- package/dist/install/dashboard.js.map +1 -0
- package/dist/install/hooks.d.ts +32 -0
- package/dist/install/hooks.d.ts.map +1 -0
- package/dist/install/hooks.js +285 -0
- package/dist/install/hooks.js.map +1 -0
- package/dist/install/index.d.ts +2 -0
- package/dist/install/index.d.ts.map +1 -0
- package/dist/install/index.js +598 -0
- package/dist/install/index.js.map +1 -0
- package/dist/install/manifest.d.ts +20 -0
- package/dist/install/manifest.d.ts.map +1 -0
- package/dist/install/manifest.js +135 -0
- package/dist/install/manifest.js.map +1 -0
- package/dist/install/patches.d.ts +11 -0
- package/dist/install/patches.d.ts.map +1 -0
- package/dist/install/patches.js +136 -0
- package/dist/install/patches.js.map +1 -0
- package/dist/install/shared.d.ts +50 -0
- package/dist/install/shared.d.ts.map +1 -0
- package/dist/install/shared.js +142 -0
- package/dist/install/shared.js.map +1 -0
- package/dist/install/uninstall.d.ts +6 -0
- package/dist/install/uninstall.d.ts.map +1 -0
- package/dist/install/uninstall.js +280 -0
- package/dist/install/uninstall.js.map +1 -0
- package/dist/install.cjs +782 -705
- package/dist/install.cjs.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/assets/dashboard/client/assets/index-DzJChB-D.css +0 -32
- package/dist/install.d.ts +0 -2
- package/dist/install.d.ts.map +0 -1
- package/dist/install.js +0 -1804
- package/dist/install.js.map +0 -1
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;
|
|
@@ -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
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
148
|
-
const summaries = phaseFiles.filter(
|
|
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
|
|
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
|
|
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(
|
|
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
|
-
|
|
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(
|
|
192
|
-
const summaryFiles = phaseFiles.filter(
|
|
193
|
-
const completedPlanIds = new Set(summaryFiles.map(
|
|
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
|
|
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(
|
|
417
|
+
const hasSummary = completedPlanIds.has(id);
|
|
218
418
|
if (!hasSummary) {
|
|
219
|
-
incomplete.push(
|
|
419
|
+
incomplete.push(id);
|
|
220
420
|
}
|
|
221
421
|
const plan = {
|
|
222
|
-
id
|
|
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(
|
|
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
|
-
|
|
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);
|
|
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
|
-
|
|
271
|
-
|
|
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
|
|
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
|
-
}
|
|
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
|
-
|
|
309
|
-
|
|
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
|
|
344
|
-
const
|
|
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(
|
|
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
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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(
|
|
469
|
-
const targetEscaped =
|
|
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(
|
|
615
|
+
node_fs_1.default.writeFileSync(rmPath, roadmapContent, 'utf-8');
|
|
493
616
|
// Update STATE.md phase count
|
|
494
|
-
const
|
|
495
|
-
if (node_fs_1.default.existsSync(
|
|
496
|
-
let stateContent = node_fs_1.default.readFileSync(
|
|
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(
|
|
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(
|
|
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
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
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
|
-
|
|
582
|
-
|
|
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
|