fix(boulder): support both structured and simple plan formats in getPlanProgress
Structured plans (with ## TODOs section) use strict numbered-label parsing. Simple plans (without sections) fall back to regex checkbox counting. This fixes 9 test failures from the #3066 merge.
This commit is contained in:
@@ -224,57 +224,86 @@ export function getPlanProgress(planPath: string): PlanProgress {
|
|||||||
try {
|
try {
|
||||||
const content = readFileSync(planPath, "utf-8")
|
const content = readFileSync(planPath, "utf-8")
|
||||||
const lines = content.split(/\r?\n/)
|
const lines = content.split(/\r?\n/)
|
||||||
let section: ProgressSection = "other"
|
|
||||||
let total = 0
|
|
||||||
let completed = 0
|
|
||||||
|
|
||||||
for (const line of lines) {
|
// Check if the plan has structured sections (## TODOs / ## Final Verification Wave)
|
||||||
if (SECOND_LEVEL_HEADING_PATTERN.test(line)) {
|
const hasStructuredSections = lines.some((line) => TODO_HEADING_PATTERN.test(line))
|
||||||
section = TODO_HEADING_PATTERN.test(line)
|
|
||||||
? "todo"
|
|
||||||
: FINAL_VERIFICATION_HEADING_PATTERN.test(line)
|
|
||||||
? "final-wave"
|
|
||||||
: "other"
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (section !== "todo" && section !== "final-wave") {
|
if (hasStructuredSections) {
|
||||||
continue
|
// Structured plan: only count top-level checkboxes with numbered labels
|
||||||
}
|
// under ## TODOs and ## Final Verification Wave sections
|
||||||
|
return getStructuredPlanProgress(lines)
|
||||||
const checkedMatch = line.match(CHECKED_CHECKBOX_PATTERN)
|
|
||||||
const uncheckedMatch = checkedMatch ? null : line.match(UNCHECKED_CHECKBOX_PATTERN)
|
|
||||||
const match = checkedMatch ?? uncheckedMatch
|
|
||||||
if (!match) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (match[1].length > 0) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
const taskBody = match[2].trim()
|
|
||||||
const labelPattern = section === "todo" ? TODO_TASK_PATTERN : FINAL_WAVE_TASK_PATTERN
|
|
||||||
if (!labelPattern.test(taskBody)) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
total++
|
|
||||||
if (checkedMatch) {
|
|
||||||
completed++
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
// Simple plan: count all top-level checkboxes anywhere
|
||||||
total,
|
return getSimplePlanProgress(content)
|
||||||
completed,
|
|
||||||
isComplete: total > 0 && completed === total,
|
|
||||||
}
|
|
||||||
} catch {
|
} catch {
|
||||||
return { total: 0, completed: 0, isComplete: true }
|
return { total: 0, completed: 0, isComplete: true }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getStructuredPlanProgress(lines: string[]): PlanProgress {
|
||||||
|
let section: ProgressSection = "other"
|
||||||
|
let total = 0
|
||||||
|
let completed = 0
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
if (SECOND_LEVEL_HEADING_PATTERN.test(line)) {
|
||||||
|
section = TODO_HEADING_PATTERN.test(line)
|
||||||
|
? "todo"
|
||||||
|
: FINAL_VERIFICATION_HEADING_PATTERN.test(line)
|
||||||
|
? "final-wave"
|
||||||
|
: "other"
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (section !== "todo" && section !== "final-wave") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkedMatch = line.match(CHECKED_CHECKBOX_PATTERN)
|
||||||
|
const uncheckedMatch = checkedMatch ? null : line.match(UNCHECKED_CHECKBOX_PATTERN)
|
||||||
|
const match = checkedMatch ?? uncheckedMatch
|
||||||
|
if (!match) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match[1].length > 0) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const taskBody = match[2].trim()
|
||||||
|
const labelPattern = section === "todo" ? TODO_TASK_PATTERN : FINAL_WAVE_TASK_PATTERN
|
||||||
|
if (!labelPattern.test(taskBody)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
total++
|
||||||
|
if (checkedMatch) {
|
||||||
|
completed++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
total,
|
||||||
|
completed,
|
||||||
|
isComplete: total > 0 && completed === total,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSimplePlanProgress(content: string): PlanProgress {
|
||||||
|
const uncheckedMatches = content.match(/^\s*[-*]\s*\[\s*\]/gm) || []
|
||||||
|
const checkedMatches = content.match(/^\s*[-*]\s*\[[xX]\]/gm) || []
|
||||||
|
|
||||||
|
const total = uncheckedMatches.length + checkedMatches.length
|
||||||
|
const completed = checkedMatches.length
|
||||||
|
|
||||||
|
return {
|
||||||
|
total,
|
||||||
|
completed,
|
||||||
|
isComplete: total > 0 && completed === total,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract plan name from file path.
|
* Extract plan name from file path.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user