Skip to content

Commit bfd77a8

Browse files
committed
fix: improve dynamic tool discovery for not development tasks
1 parent 95fd240 commit bfd77a8

18 files changed

Lines changed: 174 additions & 204 deletions

docs/TOOLS.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -161,13 +161,15 @@ XcodeBuildMCP uses a **workflow-based architecture** with tools organized into
161161
- `touch` - Perform touch down/up events at specific coordinates
162162
- `type_text` - Type text (supports US keyboard characters)
163163

164-
#### 11. Simulator Environment Configuration (`simulator-environment`)
165-
**Purpose**: Simulator environment and configuration management (5 tools)
166-
- `reset_network_condition` - Resets network conditions to default in the simulator
164+
#### 11. Simulator Management (`simulator-management`)
165+
**Purpose**: Manage simulators and their environment (7 tools)
166+
- `boot_sim` - Boots an iOS simulator using its UUID
167+
- `list_sims` - Lists available iOS simulators with their UUIDs
168+
- `open_sim` - Opens the iOS Simulator app
167169
- `reset_simulator_location` - Resets the simulator's location to default
168-
- `set_network_condition` - Simulates different network conditions in the simulator
169170
- `set_sim_appearance` - Sets the appearance mode (dark/light) of an iOS simulator
170171
- `set_simulator_location` - Sets a custom GPS location for the simulator
172+
- `sim_statusbar` - Sets the data network indicator and status bar overrides in the iOS simulator
171173

172174
#### 12. Logging & Monitoring (`logging`)
173175
**Purpose**: Log capture and monitoring across platforms (4 tools)

scripts/tools-cli.js

Lines changed: 55 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -204,14 +204,14 @@ if (command === 'help' || command === 'h') {
204204
*/
205205
async function executeReloaderoo(reloaderooArgs) {
206206
const buildPath = path.resolve(__dirname, '..', 'build', 'index.js');
207-
207+
208208
if (!fs.existsSync(buildPath)) {
209209
throw new Error('Build not found. Please run "npm run build" first.');
210210
}
211-
211+
212212
const tempFile = `/tmp/reloaderoo-output-${Date.now()}.json`;
213-
const command = `npx reloaderoo@latest inspect ${reloaderooArgs.join(' ')} -- node "${buildPath}"`;
214-
213+
const command = `npx -y reloaderoo@latest inspect ${reloaderooArgs.join(' ')} -- node "${buildPath}"`;
214+
215215
return new Promise((resolve, reject) => {
216216
const child = spawn('bash', ['-c', `${command} > "${tempFile}"`], {
217217
stdio: 'inherit'
@@ -225,22 +225,22 @@ async function executeReloaderoo(reloaderooArgs) {
225225
}
226226

227227
const content = fs.readFileSync(tempFile, 'utf8');
228-
228+
229229
// Remove stderr log lines and find JSON
230230
const lines = content.split('\n');
231231
const cleanLines = [];
232-
232+
233233
for (const line of lines) {
234234
if (line.match(/^\[\d{4}-\d{2}-\d{2}T/) || line.includes('[INFO]') || line.includes('[DEBUG]') || line.includes('[ERROR]')) {
235235
continue;
236236
}
237-
237+
238238
const trimmed = line.trim();
239239
if (trimmed) {
240240
cleanLines.push(line);
241241
}
242242
}
243-
243+
244244
// Find JSON start
245245
let jsonStartIndex = -1;
246246
for (let i = 0; i < cleanLines.length; i++) {
@@ -249,12 +249,12 @@ async function executeReloaderoo(reloaderooArgs) {
249249
break;
250250
}
251251
}
252-
252+
253253
if (jsonStartIndex === -1) {
254254
reject(new Error(`No JSON response found in output.\nOutput: ${content.substring(0, 500)}...`));
255255
return;
256256
}
257-
257+
258258
const jsonText = cleanLines.slice(jsonStartIndex).join('\n');
259259
const response = JSON.parse(jsonText);
260260
resolve(response);
@@ -282,21 +282,21 @@ async function getRuntimeInfo() {
282282
try {
283283
const toolsResponse = await executeReloaderoo(['list-tools']);
284284
const resourcesResponse = await executeReloaderoo(['list-resources']);
285-
285+
286286
let tools = [];
287287
let toolCount = 0;
288-
288+
289289
if (toolsResponse.tools && Array.isArray(toolsResponse.tools)) {
290290
toolCount = toolsResponse.tools.length;
291-
tools = toolsResponse.tools.map(tool => ({
291+
tools = toolsResponse.tools.map(tool => ({
292292
name: tool.name,
293293
description: tool.description
294294
}));
295295
}
296-
296+
297297
let resources = [];
298298
let resourceCount = 0;
299-
299+
300300
if (resourcesResponse.resources && Array.isArray(resourcesResponse.resources)) {
301301
resourceCount = resourcesResponse.resources.length;
302302
resources = resourcesResponse.resources.map(resource => ({
@@ -305,7 +305,7 @@ async function getRuntimeInfo() {
305305
description: resource.title || resource.description || 'No description available'
306306
}));
307307
}
308-
308+
309309
return {
310310
tools,
311311
resources,
@@ -328,11 +328,11 @@ function isReExportFile(filePath) {
328328
const lines = content.split('\n').map(line => line.trim());
329329

330330
const codeLines = lines.filter(line => {
331-
return line.length > 0 &&
332-
!line.startsWith('//') &&
333-
!line.startsWith('/*') &&
334-
!line.startsWith('*') &&
335-
line !== '*/';
331+
return line.length > 0 &&
332+
!line.startsWith('//') &&
333+
!line.startsWith('/*') &&
334+
!line.startsWith('*') &&
335+
line !== '*/';
336336
});
337337

338338
if (codeLines.length === 0) {
@@ -352,7 +352,7 @@ function isReExportFile(filePath) {
352352
function getWorkflowDirectories() {
353353
const workflowDirs = [];
354354
const entries = fs.readdirSync(toolsDir, { withFileTypes: true });
355-
355+
356356
for (const entry of entries) {
357357
if (entry.isDirectory()) {
358358
const indexPath = path.join(toolsDir, entry.name, 'index.ts');
@@ -361,7 +361,7 @@ function getWorkflowDirectories() {
361361
}
362362
}
363363
}
364-
364+
365365
return workflowDirs;
366366
}
367367

@@ -372,7 +372,7 @@ async function getStaticInfo() {
372372
try {
373373
// Get workflow directories
374374
const workflowDirs = getWorkflowDirectories();
375-
375+
376376
// Find all tool files
377377
const files = await glob('**/*.ts', {
378378
cwd: toolsDir,
@@ -387,23 +387,23 @@ async function getStaticInfo() {
387387
for (const file of files) {
388388
const toolName = path.basename(file, '.ts');
389389
const workflowDir = path.basename(path.dirname(file));
390-
390+
391391
if (!toolsByWorkflow.has(workflowDir)) {
392392
toolsByWorkflow.set(workflowDir, { canonical: [], reExports: [] });
393393
}
394-
394+
395395
if (isReExportFile(file)) {
396-
reExportFiles.push({
397-
name: toolName,
398-
file,
396+
reExportFiles.push({
397+
name: toolName,
398+
file,
399399
workflowDir,
400400
relativePath: path.relative(projectRoot, file)
401401
});
402402
toolsByWorkflow.get(workflowDir).reExports.push(toolName);
403403
} else {
404-
canonicalTools.set(toolName, {
404+
canonicalTools.set(toolName, {
405405
name: toolName,
406-
file,
406+
file,
407407
workflowDir,
408408
relativePath: path.relative(projectRoot, file)
409409
});
@@ -431,20 +431,20 @@ async function getStaticInfo() {
431431
function displaySummary(runtimeData, staticData) {
432432
console.log(`${colors.bright}${colors.blue}📊 XcodeBuildMCP Tools Summary${colors.reset}`);
433433
console.log('═'.repeat(60));
434-
434+
435435
if (runtimeData) {
436436
console.log(`${colors.green}🚀 Runtime Analysis:${colors.reset}`);
437437
console.log(` Mode: ${runtimeData.dynamicMode ? 'Dynamic' : 'Static'}`);
438438
console.log(` Tools: ${runtimeData.toolCount}`);
439439
console.log(` Resources: ${runtimeData.resourceCount}`);
440440
console.log(` Total: ${runtimeData.toolCount + runtimeData.resourceCount}`);
441-
441+
442442
if (runtimeData.dynamicMode) {
443443
console.log(` ${colors.yellow}ℹ️ Dynamic mode: Only enabled workflow tools shown${colors.reset}`);
444444
}
445445
console.log();
446446
}
447-
447+
448448
if (staticData) {
449449
console.log(`${colors.cyan}📁 Static Analysis:${colors.reset}`);
450450
console.log(` Workflow directories: ${staticData.workflowDirs.length}`);
@@ -460,16 +460,16 @@ function displaySummary(runtimeData, staticData) {
460460
*/
461461
function displayWorkflows(staticData) {
462462
if (!options.workflows || !staticData) return;
463-
463+
464464
console.log(`${colors.bright}📂 Workflow Directories:${colors.reset}`);
465465
console.log('─'.repeat(40));
466-
466+
467467
for (const workflowDir of staticData.workflowDirs) {
468468
const workflow = staticData.toolsByWorkflow.get(workflowDir) || { canonical: [], reExports: [] };
469469
const totalTools = workflow.canonical.length + workflow.reExports.length;
470-
470+
471471
console.log(`${colors.green}${workflowDir}${colors.reset} (${totalTools} tools)`);
472-
472+
473473
if (options.verbose) {
474474
if (workflow.canonical.length > 0) {
475475
console.log(` ${colors.cyan}Canonical:${colors.reset} ${workflow.canonical.join(', ')}`);
@@ -487,11 +487,11 @@ function displayWorkflows(staticData) {
487487
*/
488488
function displayTools(runtimeData, staticData) {
489489
if (!options.tools) return;
490-
490+
491491
if (runtimeData) {
492492
console.log(`${colors.bright}🛠️ Runtime Tools (${runtimeData.toolCount}):${colors.reset}`);
493493
console.log('─'.repeat(40));
494-
494+
495495
if (runtimeData.tools.length === 0) {
496496
console.log(' No tools available');
497497
} else {
@@ -506,11 +506,11 @@ function displayTools(runtimeData, staticData) {
506506
}
507507
console.log();
508508
}
509-
509+
510510
if (staticData && options.static) {
511511
console.log(`${colors.bright}📁 Static Tools (${staticData.toolCount}):${colors.reset}`);
512512
console.log('─'.repeat(40));
513-
513+
514514
if (staticData.tools.length === 0) {
515515
console.log(' No tools found');
516516
} else {
@@ -534,10 +534,10 @@ function displayTools(runtimeData, staticData) {
534534
*/
535535
function displayResources(runtimeData) {
536536
if (!options.resources || !runtimeData) return;
537-
537+
538538
console.log(`${colors.bright}📚 Resources (${runtimeData.resourceCount}):${colors.reset}`);
539539
console.log('─'.repeat(40));
540-
540+
541541
if (runtimeData.resources.length === 0) {
542542
console.log(' No resources available');
543543
} else {
@@ -560,41 +560,41 @@ async function main() {
560560
try {
561561
let runtimeData = null;
562562
let staticData = null;
563-
563+
564564
// Gather data based on options
565565
if (options.runtime) {
566566
console.log(`${colors.cyan}🔍 Gathering runtime information...${colors.reset}`);
567567
runtimeData = await getRuntimeInfo();
568568
}
569-
569+
570570
if (options.static) {
571571
console.log(`${colors.cyan}📁 Performing static analysis...${colors.reset}`);
572572
staticData = await getStaticInfo();
573573
}
574-
574+
575575
// For default command or workflows option, always gather static data for workflow info
576576
if (options.workflows && !staticData) {
577577
console.log(`${colors.cyan}📁 Gathering workflow information...${colors.reset}`);
578578
staticData = await getStaticInfo();
579579
}
580-
580+
581581
console.log(); // Blank line after gathering
582-
582+
583583
// Display based on command
584584
switch (command) {
585585
case 'count':
586586
case 'c':
587587
displaySummary(runtimeData, staticData);
588588
displayWorkflows(staticData);
589589
break;
590-
590+
591591
case 'list':
592592
case 'l':
593593
displaySummary(runtimeData, staticData);
594594
displayTools(runtimeData, staticData);
595595
displayResources(runtimeData);
596596
break;
597-
597+
598598
case 'static':
599599
case 's':
600600
if (!staticData) {
@@ -603,7 +603,7 @@ async function main() {
603603
}
604604
displaySummary(null, staticData);
605605
displayWorkflows(staticData);
606-
606+
607607
if (options.verbose) {
608608
displayTools(null, staticData);
609609
console.log(`${colors.bright}🔄 Re-export Files (${staticData.reExportCount}):${colors.reset}`);
@@ -614,16 +614,16 @@ async function main() {
614614
});
615615
}
616616
break;
617-
617+
618618
default:
619619
// Default case (no command) - show runtime summary with workflows
620620
displaySummary(runtimeData, staticData);
621621
displayWorkflows(staticData);
622622
break;
623623
}
624-
624+
625625
console.log(`${colors.green}✅ Analysis complete!${colors.reset}`);
626-
626+
627627
} catch (error) {
628628
console.error(`${colors.red}❌ Error: ${error.message}${colors.reset}`);
629629
process.exit(1);

0 commit comments

Comments
 (0)