fix(sdk): extractCurrentMilestone Backlog leak + state.begin-phase flag parsing (#2455)
* fix(sdk): extractCurrentMilestone Backlog leak + state.begin-phase flag parsing Closes #2422 Closes #2420 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: patch-version semver in milestone boundary regex + flag-parser validation Two follow-on correctness issues identified in code review: 1. roadmap.ts: currentVersionMatch and nextMilestoneRegex only captured major.minor (v(\d+\.\d+)), collapsing v2.0.1 to "2.0". A sub-heading "## v2.0.2 Phase Details" would match the same prefix and be incorrectly skipped. Both patterns updated to v(\d+(?:\.\d+)+) to capture full semver. 2. state-mutation.ts: pair-wise flag parsing loop advanced i by 2 unconditionally, so a missing flag value caused the next flag token to be assigned as the value (e.g. flags['phase'] = '--name'). Fix: iterate with i++ and validate that the candidate value exists and does not start with '--' before assigning; throw GSDError('missing value for --<key>') on invalid input. Added regression test. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -331,9 +331,32 @@ export const statePatch: QueryHandler = async (args, projectDir) => {
|
||||
* @returns QueryResult with { phase, name, plan_count }
|
||||
*/
|
||||
export const stateBeginPhase: QueryHandler = async (args, projectDir) => {
|
||||
const phaseNumber = args[0];
|
||||
const phaseName = args[1] || '';
|
||||
const planCount = args[2] || '?';
|
||||
// Accept both flag form (--phase 04 --name "Title" --plans 3) and positional form (04 "Title" 3).
|
||||
let phaseNumber: string;
|
||||
let phaseName: string;
|
||||
let planCount: string;
|
||||
|
||||
if (args.length > 0 && typeof args[0] === 'string' && args[0].startsWith('--')) {
|
||||
const flags: Record<string, string> = {};
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const token = args[i];
|
||||
if (typeof token !== 'string' || !token.startsWith('--')) continue;
|
||||
const key = token.slice(2);
|
||||
const value = args[i + 1];
|
||||
if (value === undefined || (typeof value === 'string' && value.startsWith('--'))) {
|
||||
throw new GSDError(`missing value for --${key}`, ErrorClassification.Validation);
|
||||
}
|
||||
flags[key] = value as string;
|
||||
i += 1;
|
||||
}
|
||||
phaseNumber = flags['phase'] ?? '';
|
||||
phaseName = flags['name'] ?? '';
|
||||
planCount = flags['plans'] ?? flags['plan-count'] ?? '?';
|
||||
} else {
|
||||
phaseNumber = args[0] ?? '';
|
||||
phaseName = args[1] || '';
|
||||
planCount = args[2] || '?';
|
||||
}
|
||||
|
||||
if (!phaseNumber) {
|
||||
throw new GSDError('phase number required', ErrorClassification.Validation);
|
||||
|
||||
Reference in New Issue
Block a user