Fix(workflow): normalize SDK init phase flags (#3389)

* fix: normalize sdk init phase flags

* chore: add changeset for sdk init phase flags

* test: cover sdk phase equals flag variants
This commit is contained in:
Tom Boucher
2026-05-11 15:27:32 -04:00
committed by GitHub
parent cc00c60d9a
commit 543a8569e4
3 changed files with 82 additions and 4 deletions

View File

@@ -0,0 +1,5 @@
---
type: Fixed
pr: 3389
---
**`gsd-sdk query init.*` phase-scoped handlers now accept `--phase <N>` and `--phase=N`** - flag-style init queries no longer search for a phase literally named `--phase`.

View File

@@ -325,6 +325,20 @@ describe('initExecutePhase', () => {
expect(data.milestone_version).toBeDefined();
});
it('accepts --phase flag form for existing phase (#3387)', async () => {
const result = await initExecutePhase(['--phase', '9'], tmpDir);
const data = result.data as Record<string, unknown>;
expect(data.phase_found).toBe(true);
expect(data.phase_number).toBe('09');
});
it('accepts --phase=value flag form for existing phase (#3387)', async () => {
const result = await initExecutePhase(['--phase=9'], tmpDir);
const data = result.data as Record<string, unknown>;
expect(data.phase_found).toBe(true);
expect(data.phase_number).toBe('09');
});
it('returns error when phase arg missing', async () => {
const result = await initExecutePhase([], tmpDir);
const data = result.data as Record<string, unknown>;
@@ -360,6 +374,20 @@ describe('initPlanPhase', () => {
expect(data.project_root).toBe(tmpDir);
});
it('accepts --phase flag form for existing phase (#3387)', async () => {
const result = await initPlanPhase(['--phase', '9'], tmpDir);
const data = result.data as Record<string, unknown>;
expect(data.phase_found).toBe(true);
expect(data.phase_number).toBe('09');
});
it('accepts --phase=value flag form for existing phase (#3387)', async () => {
const result = await initPlanPhase(['--phase=9'], tmpDir);
const data = result.data as Record<string, unknown>;
expect(data.phase_found).toBe(true);
expect(data.phase_number).toBe('09');
});
it('returns error when phase arg missing', async () => {
const result = await initPlanPhase([], tmpDir);
const data = result.data as Record<string, unknown>;
@@ -448,6 +476,20 @@ describe('initVerifyWork', () => {
expect(data.project_root).toBe(tmpDir);
});
it('accepts --phase flag form for existing phase (#3387)', async () => {
const result = await initVerifyWork(['--phase', '9'], tmpDir);
const data = result.data as Record<string, unknown>;
expect(data.phase_found).toBe(true);
expect(data.phase_number).toBe('09');
});
it('accepts --phase=value flag form for existing phase (#3387)', async () => {
const result = await initVerifyWork(['--phase=9'], tmpDir);
const data = result.data as Record<string, unknown>;
expect(data.phase_found).toBe(true);
expect(data.phase_number).toBe('09');
});
it('resolves workstream-scoped phases when workstream is provided', async () => {
const wsDir = join(tmpDir, '.planning', 'workstreams', 'delivery');
await mkdir(join(wsDir, 'phases', '32-shipment-creation-tracking-numbers-print-forms'), { recursive: true });
@@ -490,6 +532,20 @@ describe('initPhaseOp', () => {
expect(data.plan_count).toBeGreaterThanOrEqual(1);
expect(data.project_root).toBe(tmpDir);
});
it('accepts --phase flag form for existing phase (#3387)', async () => {
const result = await initPhaseOp(['--phase', '9'], tmpDir);
const data = result.data as Record<string, unknown>;
expect(data.phase_found).toBe(true);
expect(data.phase_number).toBe('09');
});
it('accepts --phase=value flag form for existing phase (#3387)', async () => {
const result = await initPhaseOp(['--phase=9'], tmpDir);
const data = result.data as Record<string, unknown>;
expect(data.phase_found).toBe(true);
expect(data.phase_number).toBe('09');
});
});
describe('initTodos', () => {

View File

@@ -54,6 +54,23 @@ function generateSlugInternal(text: string): string {
.substring(0, 60);
}
function extractPhaseArg(args: string[]): string | undefined {
const equalsArg = args.find((arg) => arg.startsWith('--phase='));
if (equalsArg) {
const value = equalsArg.slice('--phase='.length).trim();
return value || undefined;
}
const flagIndex = args.indexOf('--phase');
if (flagIndex !== -1) {
const value = args[flagIndex + 1];
return value && !value.startsWith('--') ? value : undefined;
}
const first = args[0];
return first && !first.startsWith('--') ? first : undefined;
}
/**
* Check if a path exists on disk.
*/
@@ -293,7 +310,7 @@ export function withProjectRoot(
* Port of cmdInitExecutePhase from init.cjs lines 50-171.
*/
export const initExecutePhase: QueryHandler = async (args, projectDir, workstream) => {
const phase = args[0];
const phase = extractPhaseArg(args);
if (!phase) {
return { data: { error: 'phase required for init execute-phase' } };
}
@@ -376,7 +393,7 @@ export const initExecutePhase: QueryHandler = async (args, projectDir, workstrea
* Port of cmdInitPlanPhase from init.cjs lines 173-293.
*/
export const initPlanPhase: QueryHandler = async (args, projectDir, workstream) => {
const phase = args[0];
const phase = extractPhaseArg(args);
if (!phase) {
return { data: { error: 'phase required for init plan-phase' } };
}
@@ -623,7 +640,7 @@ export const initResume: QueryHandler = async (_args, projectDir) => {
* Port of cmdInitVerifyWork from init.cjs lines 538-586.
*/
export const initVerifyWork: QueryHandler = async (args, projectDir, workstream) => {
const phase = args[0];
const phase = extractPhaseArg(args);
if (!phase) {
return { data: { error: 'phase required for init verify-work' } };
}
@@ -660,7 +677,7 @@ export const initVerifyWork: QueryHandler = async (args, projectDir, workstream)
* Port of cmdInitPhaseOp from init.cjs lines 588-697.
*/
export const initPhaseOp: QueryHandler = async (args, projectDir, workstream) => {
const phase = args[0];
const phase = extractPhaseArg(args);
if (!phase) {
return { data: { error: 'phase required for init phase-op' } };
}