fix(phase-lifecycle): prevent plan-line overwrite when **Plans:** is bare

\s* after **Plans:** matches newlines, causing [^\n]+ to consume the first
plan checkbox when the **Plans:** field has no value on the same line.
Additionally, the lazy [\s\S]*? could cross section boundaries when the
current section had no **Plans:** value, corrupting a later section.

Fix 1: replace \s* with [ \t]* to restrict post-colon match to horizontal
whitespace only.
Fix 2: replace [\s\S]*? with (?:(?!\n#{2,4})[\s\S])*? to prevent the
pattern from crossing into a new section heading.

Closes #2728
This commit is contained in:
Tom Boucher
2026-04-26 09:46:57 -04:00
parent 394403ae06
commit a9f49f8f9d
2 changed files with 2 additions and 2 deletions

2
sdk/package-lock.json generated
View File

@@ -22,7 +22,7 @@
"vitest": "^3.1.1"
},
"engines": {
"node": ">=20"
"node": ">=22.0.0"
}
},
"node_modules/@anthropic-ai/claude-agent-sdk": {

View File

@@ -1194,7 +1194,7 @@ export const phaseComplete: QueryHandler = async (args, projectDir, workstream)
// Update plan count in phase section
const planCountPattern = new RegExp(
`(#{2,4}\\s*Phase\\s+${phaseEscaped}[\\s\\S]*?\\*\\*Plans:\\*\\*\\s*)[^\\n]+`,
`(#{2,4}\\s*Phase\\s+${phaseEscaped}(?:(?!\\n#{2,4})[\\s\\S])*?\\*\\*Plans:\\*\\*[ \\t]*)[^\\n]+`,
'i',
);
roadmapContent = replaceInCurrentMilestone(