fix(roadmap): handle 5-column progress tables with Milestone column

The regex-based table parser captured a fixed number of columns,
so 5-column tables (Phase | Milestone | Plans | Status | Completed)
had the Milestone column eaten and Status/Date written to wrong cells.

Replaced regex with cell-based `split('|')` parsing that detects
column count (4 or 5) and updates the correct cells by index.
Affects both `cmdRoadmapUpdatePlanProgress` and `cmdPhaseComplete`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
j2h4u
2026-03-04 03:14:44 +05:00
parent efc398c554
commit e3bc614eb7
4 changed files with 108 additions and 16 deletions

View File

@@ -767,16 +767,25 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
);
roadmapContent = replaceInCurrentMilestone(roadmapContent, checkboxPattern, `$1x$2 (completed ${today})`);
// Progress table: update Status to Complete, add date
// Progress table: update Status to Complete, add date (handles 4 or 5 column tables)
const phaseEscaped = escapeRegex(phaseNum);
const tablePattern = new RegExp(
`(\\|\\s*${phaseEscaped}\\.?\\s[^|]*\\|[^|]*\\|)\\s*[^|]*(\\|)\\s*[^|]*(\\|)`,
'i'
);
roadmapContent = replaceInCurrentMilestone(
roadmapContent, tablePattern,
`$1 Complete $2 ${today} $3`
const tableRowPattern = new RegExp(
`^(\\|\\s*${phaseEscaped}\\.?\\s[^|]*(?:\\|[^\\n]*))$`,
'im'
);
roadmapContent = roadmapContent.replace(tableRowPattern, (fullRow) => {
const cells = fullRow.split('|').slice(1, -1);
if (cells.length === 5) {
// 5-col: Phase | Milestone | Plans | Status | Completed
cells[3] = ' Complete ';
cells[4] = ` ${today} `;
} else if (cells.length === 4) {
// 4-col: Phase | Plans | Status | Completed
cells[2] = ' Complete ';
cells[3] = ` ${today} `;
}
return '|' + cells.join('|') + '|';
});
// Update plan count in phase section
const planCountPattern = new RegExp(

View File

@@ -257,16 +257,27 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
let roadmapContent = fs.readFileSync(roadmapPath, 'utf-8');
const phaseEscaped = escapeRegex(phaseNum);
// Progress table row: update Plans column (summaries/plans) and Status column
const tablePattern = new RegExp(
`(\\|\\s*${phaseEscaped}\\.?\\s[^|]*\\|)[^|]*(\\|)\\s*[^|]*(\\|)\\s*[^|]*(\\|)`,
'i'
// Progress table row: update Plans/Status/Date columns (handles 4 or 5 column tables)
const tableRowPattern = new RegExp(
`^(\\|\\s*${phaseEscaped}\\.?\\s[^|]*(?:\\|[^\\n]*))$`,
'im'
);
const dateField = isComplete ? ` ${today} ` : ' ';
roadmapContent = replaceInCurrentMilestone(
roadmapContent, tablePattern,
`$1 ${summaryCount}/${planCount} $2 ${status.padEnd(11)}$3${dateField}$4`
);
roadmapContent = roadmapContent.replace(tableRowPattern, (fullRow) => {
const cells = fullRow.split('|').slice(1, -1); // drop leading/trailing empty from split
if (cells.length === 5) {
// 5-col: Phase | Milestone | Plans | Status | Completed
cells[2] = ` ${summaryCount}/${planCount} `;
cells[3] = ` ${status.padEnd(11)}`;
cells[4] = dateField;
} else if (cells.length === 4) {
// 4-col: Phase | Plans | Status | Completed
cells[1] = ` ${summaryCount}/${planCount} `;
cells[2] = ` ${status.padEnd(11)}`;
cells[3] = dateField;
}
return '|' + cells.join('|') + '|';
});
// Update plan count in phase detail section
const planCountPattern = new RegExp(