@@ -29,6 +29,11 @@ function loadInitModule() {
2929 return import ( '../init.ts' ) ;
3030}
3131
32+ function parseJsonOutput ( stdoutSpy : { mock : { calls : unknown [ ] [ ] } } ) : Record < string , unknown > {
33+ const output = stdoutSpy . mock . calls . map ( ( call ) => String ( call [ 0 ] ?? '' ) ) . join ( '' ) ;
34+ return JSON . parse ( output . trim ( ) ) as Record < string , unknown > ;
35+ }
36+
3237describe ( 'init command' , ( ) => {
3338 let tempDir : string ;
3439 let fakeResourceRoot : string ;
@@ -81,10 +86,11 @@ describe('init command', () => {
8186 expect ( existsSync ( installed ) ) . toBe ( true ) ;
8287 expect ( readFileSync ( installed , 'utf8' ) ) . toBe ( '# CLI Skill Content' ) ;
8388
84- const output = stdoutSpy . mock . calls . map ( ( c ) => String ( c [ 0 ] ) ) . join ( '' ) ;
85- expect ( output ) . toContain ( 'Installed XcodeBuildMCP CLI skill' ) ;
86- expect ( output ) . toContain ( 'Custom' ) ;
87- expect ( output ) . toContain ( installed ) ;
89+ const output = parseJsonOutput ( stdoutSpy ) ;
90+ expect ( output . action ) . toBe ( 'install' ) ;
91+ expect ( output . skillType ) . toBe ( 'cli' ) ;
92+ expect ( output . message ) . toBe ( 'Installed XcodeBuildMCP CLI skill' ) ;
93+ expect ( output . installed ) . toEqual ( [ { client : 'Custom' , location : installed } ] ) ;
8894
8995 stdoutSpy . mockRestore ( ) ;
9096 } ) ;
@@ -106,8 +112,10 @@ describe('init command', () => {
106112 expect ( existsSync ( installed ) ) . toBe ( true ) ;
107113 expect ( readFileSync ( installed , 'utf8' ) ) . toBe ( '# MCP Skill Content' ) ;
108114
109- const output = stdoutSpy . mock . calls . map ( ( c ) => String ( c [ 0 ] ) ) . join ( '' ) ;
110- expect ( output ) . toContain ( 'Installed XcodeBuildMCP (MCP server) skill' ) ;
115+ const output = parseJsonOutput ( stdoutSpy ) ;
116+ expect ( output . action ) . toBe ( 'install' ) ;
117+ expect ( output . skillType ) . toBe ( 'mcp' ) ;
118+ expect ( output . message ) . toBe ( 'Installed XcodeBuildMCP (MCP server) skill' ) ;
111119
112120 stdoutSpy . mockRestore ( ) ;
113121 } ) ;
@@ -173,8 +181,15 @@ describe('init command', () => {
173181 true ,
174182 ) ;
175183
176- const output = stdoutSpy . mock . calls . map ( ( c ) => String ( c [ 0 ] ) ) . join ( '' ) ;
177- expect ( output ) . toContain ( 'Skipped Claude Code' ) ;
184+ const output = parseJsonOutput ( stdoutSpy ) ;
185+ expect ( output . action ) . toBe ( 'install' ) ;
186+ expect ( output . skillType ) . toBe ( 'mcp' ) ;
187+ expect ( output . skipped ) . toEqual ( [
188+ {
189+ client : 'Claude Code' ,
190+ reason : 'MCP skill is unnecessary because Claude Code already uses server instructions.' ,
191+ } ,
192+ ] ) ;
178193
179194 stdoutSpy . mockRestore ( ) ;
180195 } ) ;
@@ -293,10 +308,16 @@ describe('init command', () => {
293308 expect ( existsSync ( cliSkillDir ) ) . toBe ( false ) ;
294309 expect ( existsSync ( mcpSkillDir ) ) . toBe ( false ) ;
295310
296- const output = stdoutSpy . mock . calls . map ( ( c ) => String ( c [ 0 ] ) ) . join ( '' ) ;
297- expect ( output ) . toContain ( 'Uninstalled skill directories' ) ;
298- expect ( output ) . toContain ( 'Removed (xcodebuildmcp-cli):' ) ;
299- expect ( output ) . toContain ( 'Removed (xcodebuildmcp):' ) ;
311+ const output = parseJsonOutput ( stdoutSpy ) ;
312+ expect ( output . action ) . toBe ( 'uninstall' ) ;
313+ expect ( output . message ) . toBe ( 'Uninstalled skill directories' ) ;
314+ expect ( output . removed ) . toHaveLength ( 2 ) ;
315+ expect ( output . removed ) . toEqual (
316+ expect . arrayContaining ( [
317+ { client : 'Custom' , variant : 'xcodebuildmcp-cli' , path : cliSkillDir } ,
318+ { client : 'Custom' , variant : 'xcodebuildmcp' , path : mcpSkillDir } ,
319+ ] ) ,
320+ ) ;
300321
301322 stdoutSpy . mockRestore ( ) ;
302323 } ) ;
@@ -314,8 +335,10 @@ describe('init command', () => {
314335 const stdoutSpy = vi . spyOn ( process . stdout , 'write' ) . mockImplementation ( ( ) => true ) ;
315336 await app . parseAsync ( ) ;
316337
317- const output = stdoutSpy . mock . calls . map ( ( c ) => String ( c [ 0 ] ) ) . join ( '' ) ;
318- expect ( output ) . toContain ( 'No installed skill directories found' ) ;
338+ const output = parseJsonOutput ( stdoutSpy ) ;
339+ expect ( output . action ) . toBe ( 'uninstall' ) ;
340+ expect ( output . message ) . toBe ( 'No installed skill directories found to remove.' ) ;
341+ expect ( output . removed ) . toEqual ( [ ] ) ;
319342
320343 stdoutSpy . mockRestore ( ) ;
321344 } ) ;
@@ -334,8 +357,10 @@ describe('init command', () => {
334357 const stdoutSpy = vi . spyOn ( process . stdout , 'write' ) . mockImplementation ( ( ) => true ) ;
335358 await app . parseAsync ( ) ;
336359
337- const output = stdoutSpy . mock . calls . map ( ( c ) => String ( c [ 0 ] ) ) . join ( '' ) ;
338- expect ( output ) . toContain ( 'No installed skill directories found' ) ;
360+ const output = parseJsonOutput ( stdoutSpy ) ;
361+ expect ( output . action ) . toBe ( 'uninstall' ) ;
362+ expect ( output . message ) . toBe ( 'No installed skill directories found to remove.' ) ;
363+ expect ( output . removed ) . toEqual ( [ ] ) ;
339364
340365 stdoutSpy . mockRestore ( ) ;
341366 } ) ;
@@ -415,8 +440,9 @@ describe('init command', () => {
415440 expect ( existsSync ( agentsPath ) ) . toBe ( true ) ;
416441 expect ( readFileSync ( agentsPath , 'utf8' ) ) . toContain ( agentsGuidanceLine ) ;
417442
418- const output = stdoutSpy . mock . calls . map ( ( c ) => String ( c [ 0 ] ) ) . join ( '' ) ;
419- expect ( output ) . toContain ( 'Created AGENTS.md with XcodeBuildMCP guidance' ) ;
443+ const output = parseJsonOutput ( stdoutSpy ) ;
444+ expect ( output . action ) . toBe ( 'install' ) ;
445+ expect ( output . message ) . toBe ( 'Installed XcodeBuildMCP CLI skill' ) ;
420446
421447 stdoutSpy . mockRestore ( ) ;
422448 } ) ;
@@ -442,11 +468,9 @@ describe('init command', () => {
442468 'AGENTS.md exists and requires confirmation to update' ,
443469 ) ;
444470
445- const output = stdoutSpy . mock . calls . map ( ( c ) => String ( c [ 0 ] ) ) . join ( '' ) ;
446- expect ( output ) . toContain ( 'Proposed update for' ) ;
447- expect ( output ) . toContain ( '--- AGENTS.md' ) ;
448- expect ( output ) . toContain ( '+++ AGENTS.md' ) ;
449- expect ( output ) . toContain ( agentsGuidanceLine ) ;
471+ const output = parseJsonOutput ( stdoutSpy ) ;
472+ expect ( output . action ) . toBe ( 'install' ) ;
473+ expect ( output . message ) . toBe ( 'Installed XcodeBuildMCP CLI skill' ) ;
450474
451475 stdoutSpy . mockRestore ( ) ;
452476 Object . defineProperty ( process . stdin , 'isTTY' , { value : originalIsTTY , configurable : true } ) ;
@@ -475,9 +499,9 @@ describe('init command', () => {
475499 expect ( agentsContent ) . toContain ( '# Existing' ) ;
476500 expect ( agentsContent ) . toContain ( agentsGuidanceLine ) ;
477501
478- const output = stdoutSpy . mock . calls . map ( ( c ) => String ( c [ 0 ] ) ) . join ( '' ) ;
479- expect ( output ) . toContain ( 'Proposed update for ') ;
480- expect ( output ) . toContain ( 'Updated AGENTS.md at ') ;
502+ const output = parseJsonOutput ( stdoutSpy ) ;
503+ expect ( output . action ) . toBe ( 'install ') ;
504+ expect ( output . message ) . toBe ( 'Installed XcodeBuildMCP CLI skill ') ;
481505
482506 stdoutSpy . mockRestore ( ) ;
483507 Object . defineProperty ( process . stdin , 'isTTY' , { value : originalIsTTY , configurable : true } ) ;
0 commit comments