Skip to content

Commit b356a3b

Browse files
feat: Add subsystem filter option for log capture (#158)
1 parent 2eda15d commit b356a3b

9 files changed

Lines changed: 716 additions & 185 deletions

File tree

.vscode/settings.json

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
"javascript",
55
"typescript"
66
],
7-
"eslint.runtime": "/opt/homebrew/bin/node",
87
"eslint.format.enable": true,
98
"eslint.nodePath": "${workspaceFolder}/node_modules",
109
"eslint.workingDirectories": [
@@ -29,19 +28,15 @@
2928
"editor.defaultFormatter": "vscode.typescript-language-features"
3029
},
3130
"terminal.integrated.shellIntegration.decorationsEnabled": "never",
32-
"vitest.nodeExecutable": "/opt/homebrew/bin/node",
3331
"[json]": {
3432
"editor.defaultFormatter": "vscode.json-language-features"
3533
},
3634
"[jsonc]": {
3735
"editor.defaultFormatter": "vscode.json-language-features"
3836
},
39-
"chat.mcp.serverSampling": {
40-
"XcodeBuildMCP/.vscode/mcp.json: XcodeBuildMCP-Dev": {
41-
"allowedDuringChat": true,
42-
"allowedModels": [
43-
"copilot/gpt-5.2"
44-
]
45-
}
46-
},
37+
"vitest.shellType": "child_process",
38+
"vitest.nodeExecutable": "/usr/bin/env",
39+
"vitest.nodeExecArgs": [
40+
"node"
41+
]
4742
}

src/mcp/tools/logging/__tests__/start_sim_log_cap.test.ts

Lines changed: 121 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ describe('start_sim_log_cap plugin', () => {
2323

2424
it('should have correct description', () => {
2525
expect(plugin.description).toBe(
26-
'Starts capturing logs from a specified simulator. Returns a session ID. By default, captures only structured logs.',
26+
"Starts capturing logs from a specified simulator. Returns a session ID. Use subsystemFilter to control what logs are captured: 'app' (default), 'all' (everything), 'swiftui' (includes Self._printChanges()), or custom subsystems.",
2727
);
2828
});
2929

@@ -42,6 +42,41 @@ describe('start_sim_log_cap plugin', () => {
4242
);
4343
});
4444

45+
it('should validate schema with subsystemFilter parameter', () => {
46+
const schema = z.object(plugin.schema);
47+
// Valid enum values
48+
expect(
49+
schema.safeParse({ bundleId: 'com.example.app', subsystemFilter: 'app' }).success,
50+
).toBe(true);
51+
expect(
52+
schema.safeParse({ bundleId: 'com.example.app', subsystemFilter: 'all' }).success,
53+
).toBe(true);
54+
expect(
55+
schema.safeParse({ bundleId: 'com.example.app', subsystemFilter: 'swiftui' }).success,
56+
).toBe(true);
57+
// Valid array of subsystems
58+
expect(
59+
schema.safeParse({ bundleId: 'com.example.app', subsystemFilter: ['com.apple.UIKit'] })
60+
.success,
61+
).toBe(true);
62+
expect(
63+
schema.safeParse({
64+
bundleId: 'com.example.app',
65+
subsystemFilter: ['com.apple.UIKit', 'com.apple.CoreData'],
66+
}).success,
67+
).toBe(true);
68+
// Invalid values
69+
expect(schema.safeParse({ bundleId: 'com.example.app', subsystemFilter: [] }).success).toBe(
70+
false,
71+
);
72+
expect(
73+
schema.safeParse({ bundleId: 'com.example.app', subsystemFilter: 'invalid' }).success,
74+
).toBe(false);
75+
expect(schema.safeParse({ bundleId: 'com.example.app', subsystemFilter: 123 }).success).toBe(
76+
false,
77+
);
78+
});
79+
4580
it('should reject invalid schema parameters', () => {
4681
const schema = z.object(plugin.schema);
4782
expect(schema.safeParse({ bundleId: null }).success).toBe(false);
@@ -79,6 +114,7 @@ describe('start_sim_log_cap plugin', () => {
79114
{
80115
simulatorId: 'test-uuid',
81116
bundleId: 'com.example.app',
117+
subsystemFilter: 'app',
82118
},
83119
mockExecutor,
84120
logCaptureStub,
@@ -103,15 +139,93 @@ describe('start_sim_log_cap plugin', () => {
103139
{
104140
simulatorId: 'test-uuid',
105141
bundleId: 'com.example.app',
142+
subsystemFilter: 'app',
106143
},
107144
mockExecutor,
108145
logCaptureStub,
109146
);
110147

111148
expect(result.isError).toBeUndefined();
112149
expect(result.content[0].text).toBe(
113-
"Log capture started successfully. Session ID: test-uuid-123.\n\nNote: Only structured logs are being captured.\n\nNext Steps:\n1. Interact with your simulator and app.\n2. Use 'stop_sim_log_cap' with session ID 'test-uuid-123' to stop capture and retrieve logs.",
150+
"Log capture started successfully. Session ID: test-uuid-123.\n\nOnly structured logs from the app subsystem are being captured.\n\nNext Steps:\n1. Interact with your simulator and app.\n2. Use 'stop_sim_log_cap' with session ID 'test-uuid-123' to stop capture and retrieve logs.",
151+
);
152+
});
153+
154+
it('should indicate swiftui capture when subsystemFilter is swiftui', async () => {
155+
const mockExecutor = createMockExecutor({ success: true, output: '' });
156+
const logCaptureStub = (params: any, executor: any) => {
157+
return Promise.resolve({
158+
sessionId: 'test-uuid-123',
159+
logFilePath: '/tmp/test.log',
160+
processes: [],
161+
error: undefined,
162+
});
163+
};
164+
165+
const result = await start_sim_log_capLogic(
166+
{
167+
simulatorId: 'test-uuid',
168+
bundleId: 'com.example.app',
169+
subsystemFilter: 'swiftui',
170+
},
171+
mockExecutor,
172+
logCaptureStub,
173+
);
174+
175+
expect(result.isError).toBeUndefined();
176+
expect(result.content[0].text).toContain('SwiftUI logs');
177+
expect(result.content[0].text).toContain('Self._printChanges()');
178+
});
179+
180+
it('should indicate all logs capture when subsystemFilter is all', async () => {
181+
const mockExecutor = createMockExecutor({ success: true, output: '' });
182+
const logCaptureStub = (params: any, executor: any) => {
183+
return Promise.resolve({
184+
sessionId: 'test-uuid-123',
185+
logFilePath: '/tmp/test.log',
186+
processes: [],
187+
error: undefined,
188+
});
189+
};
190+
191+
const result = await start_sim_log_capLogic(
192+
{
193+
simulatorId: 'test-uuid',
194+
bundleId: 'com.example.app',
195+
subsystemFilter: 'all',
196+
},
197+
mockExecutor,
198+
logCaptureStub,
199+
);
200+
201+
expect(result.isError).toBeUndefined();
202+
expect(result.content[0].text).toContain('all system logs');
203+
});
204+
205+
it('should indicate custom subsystems when array is provided', async () => {
206+
const mockExecutor = createMockExecutor({ success: true, output: '' });
207+
const logCaptureStub = (params: any, executor: any) => {
208+
return Promise.resolve({
209+
sessionId: 'test-uuid-123',
210+
logFilePath: '/tmp/test.log',
211+
processes: [],
212+
error: undefined,
213+
});
214+
};
215+
216+
const result = await start_sim_log_capLogic(
217+
{
218+
simulatorId: 'test-uuid',
219+
bundleId: 'com.example.app',
220+
subsystemFilter: ['com.apple.UIKit', 'com.apple.CoreData'],
221+
},
222+
mockExecutor,
223+
logCaptureStub,
114224
);
225+
226+
expect(result.isError).toBeUndefined();
227+
expect(result.content[0].text).toContain('com.apple.UIKit');
228+
expect(result.content[0].text).toContain('com.apple.CoreData');
115229
});
116230

117231
it('should indicate console capture when captureConsole is true', async () => {
@@ -130,14 +244,14 @@ describe('start_sim_log_cap plugin', () => {
130244
simulatorId: 'test-uuid',
131245
bundleId: 'com.example.app',
132246
captureConsole: true,
247+
subsystemFilter: 'app',
133248
},
134249
mockExecutor,
135250
logCaptureStub,
136251
);
137252

138-
expect(result.content[0].text).toBe(
139-
"Log capture started successfully. Session ID: test-uuid-123.\n\nNote: Your app was relaunched to capture console output.\n\nNext Steps:\n1. Interact with your simulator and app.\n2. Use 'stop_sim_log_cap' with session ID 'test-uuid-123' to stop capture and retrieve logs.",
140-
);
253+
expect(result.content[0].text).toContain('Your app was relaunched to capture console output');
254+
expect(result.content[0].text).toContain('test-uuid-123');
141255
});
142256

143257
it('should create correct spawn commands for console capture', async () => {
@@ -190,6 +304,7 @@ describe('start_sim_log_cap plugin', () => {
190304
simulatorId: 'test-uuid',
191305
bundleId: 'com.example.app',
192306
captureConsole: true,
307+
subsystemFilter: 'app',
193308
},
194309
mockExecutor,
195310
logCaptureStub,
@@ -259,6 +374,7 @@ describe('start_sim_log_cap plugin', () => {
259374
simulatorId: 'test-uuid',
260375
bundleId: 'com.example.app',
261376
captureConsole: false,
377+
subsystemFilter: 'app',
262378
},
263379
mockExecutor,
264380
logCaptureStub,

0 commit comments

Comments
 (0)