Skip to content

Commit 25e5a8b

Browse files
committed
fix(init): Include AGENTS guidance in non-interactive JSON
Capture AGENTS guidance outcomes during non-interactive init and embed the status in the emitted JSON report. Include path and error details when AGENTS updates fail so automation can reason about partial success without parsing stderr. Preserve existing failure behavior by throwing after JSON emission on AGENTS errors, and extend init command tests to cover created, updated, and error statuses in JSON output.
1 parent 9ee7b29 commit 25e5a8b

2 files changed

Lines changed: 62 additions & 2 deletions

File tree

src/cli/commands/__tests__/init.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,10 @@ describe('init command', () => {
443443
const output = parseJsonOutput(stdoutSpy);
444444
expect(output.action).toBe('install');
445445
expect(output.message).toBe('Installed XcodeBuildMCP CLI skill');
446+
expect(output.agentsGuidance).toEqual({
447+
status: 'created',
448+
path: agentsPath,
449+
});
446450

447451
stdoutSpy.mockRestore();
448452
});
@@ -471,6 +475,12 @@ describe('init command', () => {
471475
const output = parseJsonOutput(stdoutSpy);
472476
expect(output.action).toBe('install');
473477
expect(output.message).toBe('Installed XcodeBuildMCP CLI skill');
478+
expect(output.agentsGuidance).toEqual({
479+
status: 'error',
480+
path: join(projectRoot, 'AGENTS.md'),
481+
error:
482+
'AGENTS.md exists and requires confirmation to update. Re-run with --force to apply the change in non-interactive mode.',
483+
});
474484

475485
stdoutSpy.mockRestore();
476486
Object.defineProperty(process.stdin, 'isTTY', { value: originalIsTTY, configurable: true });
@@ -502,6 +512,10 @@ describe('init command', () => {
502512
const output = parseJsonOutput(stdoutSpy);
503513
expect(output.action).toBe('install');
504514
expect(output.message).toBe('Installed XcodeBuildMCP CLI skill');
515+
expect(output.agentsGuidance).toEqual({
516+
status: 'updated',
517+
path: join(projectRoot, 'AGENTS.md'),
518+
});
505519

506520
stdoutSpy.mockRestore();
507521
Object.defineProperty(process.stdin, 'isTTY', { value: originalIsTTY, configurable: true });
@@ -536,6 +550,14 @@ describe('init command', () => {
536550
)?.length,
537551
).toBe(1);
538552

553+
const output = parseJsonOutput(stdoutSpy);
554+
expect(output.action).toBe('install');
555+
expect(output.message).toBe('Installed XcodeBuildMCP CLI skill');
556+
expect(output.agentsGuidance).toEqual({
557+
status: 'updated',
558+
path: join(projectRoot, 'AGENTS.md'),
559+
});
560+
539561
stdoutSpy.mockRestore();
540562
});
541563
});

src/cli/commands/init.ts

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,19 @@ interface InstallPolicyResult {
116116
skippedClients: Array<{ client: string; reason: string }>;
117117
}
118118

119+
type AgentsGuidanceStatus = 'created' | 'updated' | 'no_change' | 'skipped' | 'error';
120+
119121
interface InitReport {
120122
action: 'install' | 'uninstall';
121123
skillType?: SkillType;
122124
installed?: InstallResult[];
123125
removed?: Array<{ client: string; variant: string; path: string }>;
124126
skipped?: Array<{ client: string; reason: string }>;
127+
agentsGuidance?: {
128+
status: AgentsGuidanceStatus;
129+
path: string;
130+
error?: string;
131+
};
125132
message: string;
126133
}
127134

@@ -584,11 +591,38 @@ export function registerInitCommand(app: Argv, ctx?: { workspaceRoot: string }):
584591
results.push(result);
585592
}
586593

594+
let agentsGuidanceStatus: AgentsGuidanceStatus | undefined;
595+
let agentsGuidancePath: string | undefined;
596+
let agentsGuidanceError: string | undefined;
597+
if (!isTTY && ctx?.workspaceRoot) {
598+
const projectRoot = path.resolve(ctx.workspaceRoot);
599+
agentsGuidancePath = path.join(projectRoot, AGENTS_FILE_NAME);
600+
try {
601+
agentsGuidanceStatus = await ensureAgentsGuidance(
602+
projectRoot,
603+
argv.force as boolean,
604+
false,
605+
);
606+
} catch (error) {
607+
agentsGuidanceStatus = 'error';
608+
agentsGuidanceError = error instanceof Error ? error.message : String(error);
609+
}
610+
}
611+
587612
const report: InitReport = {
588613
action: 'install',
589614
skillType: selection.skillType,
590615
installed: results,
591616
skipped: policy.skippedClients,
617+
...(agentsGuidanceStatus && agentsGuidancePath
618+
? {
619+
agentsGuidance: {
620+
status: agentsGuidanceStatus,
621+
path: agentsGuidancePath,
622+
...(agentsGuidanceError ? { error: agentsGuidanceError } : {}),
623+
},
624+
}
625+
: {}),
592626
message: `Installed ${skillDisplayName(selection.skillType)} skill`,
593627
};
594628

@@ -605,9 +639,13 @@ export function registerInitCommand(app: Argv, ctx?: { workspaceRoot: string }):
605639
process.stdout.write(`${JSON.stringify(report)}\n`);
606640
}
607641

608-
if (ctx?.workspaceRoot) {
642+
if (agentsGuidanceStatus === 'error' && agentsGuidanceError) {
643+
throw new Error(agentsGuidanceError);
644+
}
645+
646+
if (ctx?.workspaceRoot && isTTY) {
609647
const projectRoot = path.resolve(ctx.workspaceRoot);
610-
await ensureAgentsGuidance(projectRoot, argv.force as boolean, isTTY);
648+
await ensureAgentsGuidance(projectRoot, argv.force as boolean, true);
611649
}
612650
},
613651
);

0 commit comments

Comments
 (0)