Skip to content

Commit 1fea4d5

Browse files
fix: add platform parameter to clean tool with iOS default (#100)
* fix: add platform parameter to clean tool with iOS default - Add optional platform parameter to clean tool schema with enum validation - Default to iOS platform instead of hardcoded macOS for iOS-only projects - Maintain backward compatibility - existing calls use iOS by default - Add comprehensive tests for platform parameter functionality - Update tool description to document new platform parameter Fixes #99 Co-authored-by: Cameron Cooke <cameroncooke@users.noreply.github.com> * fix: properly type platform mapping and ensure simulator platforms are mapped to device equivalents for clean operations - Fix TypeScript error by properly typing cleanPlatformMap as Partial<Record<XcodePlatform, XcodePlatform>> - Simulator platforms (iOS Simulator, watchOS Simulator, etc.) are mapped to their device equivalents (iOS, watchOS, etc.) since build products are shared between device and simulator platforms - This allows clean operations to work correctly for all platform types including simulators - All tests passing and local testing confirms iOS, iOS Simulator, and macOS platforms work correctly * Update workflows * Fix workflow * Fix --------- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> Co-authored-by: Cameron Cooke <cameroncooke@users.noreply.github.com>
1 parent 8db94df commit 1fea4d5

4 files changed

Lines changed: 140 additions & 64 deletions

File tree

.github/workflows/ci.yml

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,29 @@ jobs:
1616

1717
steps:
1818
- uses: actions/checkout@v3
19-
19+
2020
- name: Use Node.js ${{ matrix.node-version }}
2121
uses: actions/setup-node@v3
2222
with:
2323
node-version: ${{ matrix.node-version }}
2424
cache: 'npm'
25-
25+
2626
- name: Install dependencies
2727
run: npm ci
28-
28+
2929
- name: Build
3030
run: npm run build
31-
31+
3232
- name: Lint
3333
run: npm run lint
34-
34+
3535
- name: Check formatting
3636
run: npm run format:check
37-
37+
3838
- name: Type check
3939
run: npm run typecheck
4040

4141
- name: Run tests
4242
run: npm test
43+
44+
- run: npx pkg-pr-new publish

.github/workflows/pkg-pr-new.yml

Lines changed: 0 additions & 56 deletions
This file was deleted.

src/mcp/tools/utilities/__tests__/clean.test.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,80 @@ describe('clean (unified) tool', () => {
4848
const text = String(result.content?.[1]?.text ?? result.content?.[0]?.text ?? '');
4949
expect(text).toContain('Invalid parameters');
5050
});
51+
52+
it('uses iOS platform by default', async () => {
53+
let capturedCommand: string[] = [];
54+
const mockExecutor = async (command: string[]) => {
55+
capturedCommand = command;
56+
return { success: true, output: 'clean success' };
57+
};
58+
59+
const result = await cleanLogic(
60+
{ projectPath: '/p.xcodeproj', scheme: 'App' } as any,
61+
mockExecutor,
62+
);
63+
expect(result.isError).not.toBe(true);
64+
65+
// Check that the command contains iOS platform destination
66+
const commandStr = capturedCommand.join(' ');
67+
expect(commandStr).toContain('-destination');
68+
expect(commandStr).toContain('platform=iOS');
69+
});
70+
71+
it('accepts custom platform parameter', async () => {
72+
let capturedCommand: string[] = [];
73+
const mockExecutor = async (command: string[]) => {
74+
capturedCommand = command;
75+
return { success: true, output: 'clean success' };
76+
};
77+
78+
const result = await cleanLogic(
79+
{
80+
projectPath: '/p.xcodeproj',
81+
scheme: 'App',
82+
platform: 'macOS',
83+
} as any,
84+
mockExecutor,
85+
);
86+
expect(result.isError).not.toBe(true);
87+
88+
// Check that the command contains macOS platform destination
89+
const commandStr = capturedCommand.join(' ');
90+
expect(commandStr).toContain('-destination');
91+
expect(commandStr).toContain('platform=macOS');
92+
});
93+
94+
it('accepts iOS Simulator platform parameter (maps to iOS for clean)', async () => {
95+
let capturedCommand: string[] = [];
96+
const mockExecutor = async (command: string[]) => {
97+
capturedCommand = command;
98+
return { success: true, output: 'clean success' };
99+
};
100+
101+
const result = await cleanLogic(
102+
{
103+
projectPath: '/p.xcodeproj',
104+
scheme: 'App',
105+
platform: 'iOS Simulator',
106+
} as any,
107+
mockExecutor,
108+
);
109+
expect(result.isError).not.toBe(true);
110+
111+
// For clean operations, iOS Simulator should be mapped to iOS platform
112+
const commandStr = capturedCommand.join(' ');
113+
expect(commandStr).toContain('-destination');
114+
expect(commandStr).toContain('platform=iOS');
115+
});
116+
117+
it('handler validation: rejects invalid platform values', async () => {
118+
const result = await (tool as any).handler({
119+
projectPath: '/p.xcodeproj',
120+
scheme: 'App',
121+
platform: 'InvalidPlatform',
122+
});
123+
expect(result.isError).toBe(true);
124+
const text = String(result.content?.[1]?.text ?? result.content?.[0]?.text ?? '');
125+
expect(text).toContain('Invalid parameters');
126+
});
51127
});

src/mcp/tools/utilities/clean.ts

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,22 @@ const baseOptions = {
3232
.describe(
3333
'If true, prefers xcodebuild over the experimental incremental build system, useful for when incremental build system fails.',
3434
),
35+
platform: z
36+
.enum([
37+
'macOS',
38+
'iOS',
39+
'iOS Simulator',
40+
'watchOS',
41+
'watchOS Simulator',
42+
'tvOS',
43+
'tvOS Simulator',
44+
'visionOS',
45+
'visionOS Simulator',
46+
])
47+
.optional()
48+
.describe(
49+
'Optional: Platform to clean for (defaults to iOS). Choose from macOS, iOS, iOS Simulator, watchOS, watchOS Simulator, tvOS, tvOS Simulator, visionOS, visionOS Simulator',
50+
),
3551
};
3652

3753
const baseSchemaObject = z.object({
@@ -67,6 +83,32 @@ export async function cleanLogic(
6783
'Invalid parameters:\nscheme: scheme is required when workspacePath is provided.',
6884
);
6985
}
86+
87+
// Use provided platform or default to iOS
88+
const targetPlatform = params.platform ?? 'iOS';
89+
90+
// Map human-friendly platform names to XcodePlatform enum values
91+
// This is safer than direct key lookup and handles the space-containing simulator names
92+
const platformMap = {
93+
macOS: XcodePlatform.macOS,
94+
iOS: XcodePlatform.iOS,
95+
'iOS Simulator': XcodePlatform.iOSSimulator,
96+
watchOS: XcodePlatform.watchOS,
97+
'watchOS Simulator': XcodePlatform.watchOSSimulator,
98+
tvOS: XcodePlatform.tvOS,
99+
'tvOS Simulator': XcodePlatform.tvOSSimulator,
100+
visionOS: XcodePlatform.visionOS,
101+
'visionOS Simulator': XcodePlatform.visionOSSimulator,
102+
};
103+
104+
const platformEnum = platformMap[targetPlatform];
105+
if (!platformEnum) {
106+
return createErrorResponse(
107+
'Parameter validation failed',
108+
`Invalid parameters:\nplatform: unsupported value "${targetPlatform}".`,
109+
);
110+
}
111+
70112
const hasProjectPath = typeof params.projectPath === 'string';
71113
const typedParams: SharedBuildParams = {
72114
...(hasProjectPath
@@ -80,10 +122,22 @@ export async function cleanLogic(
80122
extraArgs: params.extraArgs,
81123
};
82124

125+
// For clean operations, simulator platforms should be mapped to their device equivalents
126+
// since clean works at the build product level, not runtime level, and build products
127+
// are shared between device and simulator platforms
128+
const cleanPlatformMap: Partial<Record<XcodePlatform, XcodePlatform>> = {
129+
[XcodePlatform.iOSSimulator]: XcodePlatform.iOS,
130+
[XcodePlatform.watchOSSimulator]: XcodePlatform.watchOS,
131+
[XcodePlatform.tvOSSimulator]: XcodePlatform.tvOS,
132+
[XcodePlatform.visionOSSimulator]: XcodePlatform.visionOS,
133+
};
134+
135+
const cleanPlatform = cleanPlatformMap[platformEnum] ?? platformEnum;
136+
83137
return executeXcodeBuildCommand(
84138
typedParams,
85139
{
86-
platform: XcodePlatform.macOS,
140+
platform: cleanPlatform,
87141
logPrefix: 'Clean',
88142
},
89143
false,
@@ -95,7 +149,7 @@ export async function cleanLogic(
95149
export default {
96150
name: 'clean',
97151
description:
98-
"Cleans build products for either a project or a workspace using xcodebuild. Provide exactly one of projectPath or workspacePath. Example: clean({ projectPath: '/path/to/MyProject.xcodeproj', scheme: 'MyScheme' })",
152+
"Cleans build products for either a project or a workspace using xcodebuild. Provide exactly one of projectPath or workspacePath. Platform defaults to iOS if not specified. Example: clean({ projectPath: '/path/to/MyProject.xcodeproj', scheme: 'MyScheme', platform: 'iOS' })",
99153
schema: baseSchemaObject.shape,
100154
handler: createTypedTool<CleanParams>(
101155
cleanSchema as z.ZodType<CleanParams>,

0 commit comments

Comments
 (0)