Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions NEXT_STEPS_MIGRATION_TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Next Steps Migration TODO

Generated: 2026-02-11 19:12:27 UTC

## Remaining tool files with inline nextSteps
- [x] src/mcp/tools/debugging/debug_attach_sim.ts:148
- [x] src/mcp/tools/device/get_device_app_path.ts:140
- [x] src/mcp/tools/device/launch_app_device.ts:135
- [x] src/mcp/tools/device/list_devices.ts:391
- [x] src/mcp/tools/logging/start_device_log_cap.ts:665
- [x] src/mcp/tools/logging/start_sim_log_cap.ts:81
- [x] src/mcp/tools/macos/get_mac_app_path.ts:167
- [x] src/mcp/tools/project-discovery/get_app_bundle_id.ts:91
- [x] src/mcp/tools/project-discovery/get_mac_bundle_id.ts:88
- [x] src/mcp/tools/project-discovery/list_schemes.ts:83
- [x] src/mcp/tools/project-discovery/show_build_settings.ts:88
- [x] src/mcp/tools/project-scaffolding/scaffold_ios_project.ts:366
- [x] src/mcp/tools/project-scaffolding/scaffold_macos_project.ts:340
- [x] src/mcp/tools/simulator/boot_sim.ts:69
- [x] src/mcp/tools/simulator/build_run_sim.ts:486
- [x] src/mcp/tools/simulator/get_sim_app_path.ts:244
- [x] src/mcp/tools/simulator/install_app_sim.ts:93
- [x] src/mcp/tools/simulator/launch_app_logs_sim.ts:100
- [x] src/mcp/tools/simulator/launch_app_sim.ts:127
- [x] src/mcp/tools/simulator/list_sims.ts:196
- [x] src/mcp/tools/simulator/open_sim.ts:42
- [x] src/mcp/tools/simulator/record_sim_video.ts:130
2 changes: 1 addition & 1 deletion docs/TOOLS-CLI.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,4 +189,4 @@ XcodeBuildMCP provides 73 canonical tools organized into 13 workflow groups.

---

*This documentation is automatically generated by `scripts/update-tools-docs.ts` from the tools manifest. Last updated: 2026-02-16T21:53:02.251Z UTC*
*This documentation is automatically generated by `scripts/update-tools-docs.ts` from the tools manifest. Last updated: 2026-02-16T22:19:28.295Z UTC*
2 changes: 1 addition & 1 deletion docs/TOOLS.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,4 +204,4 @@ This document lists MCP tool names as exposed to MCP clients. XcodeBuildMCP prov

---

*This documentation is automatically generated by `scripts/update-tools-docs.ts` from the tools manifest. Last updated: 2026-02-16T21:53:02.251Z UTC*
*This documentation is automatically generated by `scripts/update-tools-docs.ts` from the tools manifest. Last updated: 2026-02-16T22:19:28.295Z UTC*
2 changes: 1 addition & 1 deletion docs/dev/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ Not all parts are required for every tool. For example, `swift_package_build` ha

### Testing Principles

XcodeBuildMCP uses a strict **Dependency Injection (DI)** pattern for testing, which completely bans the use of traditional mocking libraries like Vitest's `vi.mock` or `vi.fn`. This ensures that tests are robust, maintainable, and verify the actual integration between components.
XcodeBuildMCP uses a **Dependency Injection (DI)** pattern for testing external boundaries (command execution, filesystem, and other side effects). Vitest mocking libraries (`vi.mock`, `vi.fn`, etc.) are acceptable for internal collaborators when needed. This keeps tests robust while preserving deterministic behavior at external boundaries.

For detailed guidelines, see the [Testing Guide](TESTING.md).

Expand Down
15 changes: 7 additions & 8 deletions docs/dev/CODE_QUALITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ XcodeBuildMCP enforces several architectural patterns that cannot be expressed t
- Logic functions accepting `executor?: CommandExecutor` parameter

❌ **Forbidden**:
- Direct use of `vi.mock()`, `vi.fn()`, or any Vitest mocking
- Direct calls to `execSync`, `spawn`, or `exec` in production code
- Testing handler functions directly
- Real external side effects in unit tests (xcodebuild/xcrun/filesystem writes outside mocks)

### 2. Handler Signature Compliance

Expand Down Expand Up @@ -145,7 +145,7 @@ The pattern checker enforces XcodeBuildMCP-specific architectural rules:
node scripts/check-code-patterns.js

# Check specific pattern type
node scripts/check-code-patterns.js --pattern=vitest
node scripts/check-code-patterns.js
node scripts/check-code-patterns.js --pattern=execsync
node scripts/check-code-patterns.js --pattern=handler
node scripts/check-code-patterns.js --pattern=handler-testing
Expand All @@ -172,12 +172,11 @@ npm run tools:all

The pattern checker identifies the following violations:

### 1. Vitest Mocking Violations

**What**: Any use of Vitest mocking functions
**Why**: Breaks dependency injection architecture
**Fix**: Use `createMockExecutor()` instead
### 1. External-Boundary Mocking Violations

**What**: Tests that mock external side effects without injected executors/filesystem dependencies
**Why**: Breaks deterministic external-boundary testing
**Fix**: Use `createMockExecutor()` / `createMockFileSystemExecutor()` for external dependencies
### 2. ExecSync Violations

**What**: Direct use of Node.js child_process functions in production code
Expand Down Expand Up @@ -251,7 +250,7 @@ node scripts/check-code-patterns.js # Check architectural compliance
### 3. Writing Tests

1. Import the logic function, not the default export
2. Use `createMockExecutor()` for mocking
2. Use `createMockExecutor()` / `createMockFileSystemExecutor()` for external side effects
3. Test three dimensions: validation, command generation, output processing
4. Never test handlers directly

Expand Down
4 changes: 2 additions & 2 deletions docs/dev/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,8 @@ Before making changes, please familiarize yourself with:
All contributions must adhere to the testing standards outlined in the [**XcodeBuildMCP Plugin Testing Guidelines (TESTING.md)**](TESTING.md). This is the canonical source of truth for all testing practices.

**Key Principles (Summary):**
- **No Vitest Mocking**: All forms of `vi.mock`, `vi.fn`, `vi.spyOn`, etc., are strictly forbidden.
- **Dependency Injection**: All external dependencies (command execution, file system access) must be injected into tool logic functions using the `CommandExecutor` and `FileSystemExecutor` patterns.
- **Dependency Injection for External Boundaries**: All external dependencies (command execution, file system access) must be injected into tool logic functions using the `CommandExecutor` and `FileSystemExecutor` patterns.
- **Internal Mocking Is Allowed**: Vitest mocking (`vi.mock`, `vi.fn`, `vi.spyOn`, etc.) is acceptable for internal modules/collaborators.
- **Test Production Code**: Tests must import and execute the actual tool logic, not mock implementations.
- **Comprehensive Coverage**: Tests must cover input validation, command generation, and output processing.

Expand Down
77 changes: 32 additions & 45 deletions docs/dev/TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,53 +18,40 @@ This document provides comprehensive testing guidelines for XcodeBuildMCP plugin

## Testing Philosophy

### 🚨 CRITICAL: No Vitest Mocking Allowed

### ABSOLUTE RULE: ALL VITEST MOCKING IS COMPLETELY BANNED

### FORBIDDEN PATTERNS (will cause immediate test failure):

#### Vitest Mocking (COMPLETELY BANNED):
- `vi.mock()` - BANNED
- `vi.fn()` - BANNED
- `vi.mocked()` - BANNED
- `vi.spyOn()` - BANNED
- `.mockResolvedValue()` - BANNED
- `.mockRejectedValue()` - BANNED
- `.mockReturnValue()` - BANNED
- `.mockImplementation()` - BANNED
- `.toHaveBeenCalled()` - BANNED
- `.toHaveBeenCalledWith()` - BANNED
- `MockedFunction` type - BANNED

#### Manual Mock Implementations (BANNED - use our utilities instead):
- `const mockExecutor = async (...) => { ... }` - Use `createMockExecutor()` instead
- `const mockFsDeps = { readFile: async () => ... }` - Use `createMockFileSystemExecutor()` instead
- `const mockServer = { ... }` - Refactor to use dependency injection pattern
- Any manual async function implementations for mocking behavior

### ONLY ALLOWED MOCKING:
- `createMockExecutor({ success: true, output: 'result' })` - command execution
- `createMockFileSystemExecutor({ readFile: async () => 'content' })` - file system operations
### 🚨 CRITICAL: External Dependency Mocking Rules

### ABSOLUTE RULE: External side effects must use dependency injection utilities

### Use dependency-injection mocks for EXTERNAL dependencies:
- `createMockExecutor()` / `createNoopExecutor()` for command execution (`xcrun`, `xcodebuild`, AXe, etc.)
- `createMockFileSystemExecutor()` / `createNoopFileSystemExecutor()` for file system interactions

### Internal mocking guidance:
- Vitest mocking (`vi.fn`, `vi.mock`, `vi.spyOn`, `.mockResolvedValue`, etc.) is allowed for internal modules and in-memory collaborators
- Prefer straightforward, readable test doubles over over-engineered mocks

### Still forbidden:
- Hitting real external systems in unit tests (real `xcodebuild`, `xcrun`, AXe, filesystem writes/reads outside test harness)
- Bypassing dependency injection for external effects

### OUR CORE PRINCIPLE

**Simple Rule**: No mocking other than `createMockExecutor()` and `createMockFileSystemExecutor()` (and their noop variants).
**Simple Rule**: Use dependency-injection mock executors for external boundaries; use Vitest mocking only for internal behavior.

**Why This Rule Exists**:
1. **Consistency**: All tests use the same mocking utilities, making them predictable and maintainable
2. **Reliability**: Our utilities are thoroughly tested and handle edge cases properly
3. **Architectural Enforcement**: Prevents bypassing our dependency injection patterns
4. **Simplicity**: One clear rule instead of complex guidelines about what mocking is acceptable
1. **Reliability**: External side effects stay deterministic and hermetic
2. **Clarity**: Internal collaboration assertions remain concise and readable
3. **Architectural Enforcement**: External boundaries are explicit in tool logic signatures
4. **Maintainability**: Tests fail for behavior regressions, not incidental environment differences

### Integration Testing with Dependency Injection

XcodeBuildMCP follows a **pure dependency injection** testing philosophy that eliminates vitest mocking:
XcodeBuildMCP follows a dependency-injection testing philosophy for external boundaries:

- ✅ **Test plugin interfaces** (public API contracts)
- ✅ **Test integration flows** (plugin → utilities → external tools)
- ✅ **Use dependency injection** with createMockExecutor()
- **Never mock vitest functions** (vi.mock, vi.fn, etc.)
- ✅ **Use dependency injection** with createMockExecutor()/createMockFileSystemExecutor() for external dependencies
- **Use Vitest mocking when needed** for internal modules and collaborators

### Benefits

Expand All @@ -76,7 +63,7 @@ XcodeBuildMCP follows a **pure dependency injection** testing philosophy that el

### Automated Violation Checking

To enforce the no-mocking policy, the project includes a script that automatically checks for banned testing patterns.
To enforce external-boundary testing policy, the project includes a script that checks for architectural test-pattern violations.

```bash
# Run the script to check for violations
Expand All @@ -91,7 +78,7 @@ This script is part of the standard development workflow and should be run befor
- Manual mock executors: `const mockExecutor = async (...) => { ... }`
- Manual filesystem mocks: `const mockFsDeps = { readFile: async () => ... }`
- Manual server mocks: `const mockServer = { ... }`
- Vitest mocking patterns: `vi.mock()`, `vi.fn()`, etc.
- External side-effect patterns that bypass injected executors/filesystem dependencies

#### ❌ FALSE POSITIVES (should NOT be flagged):
- Test data tracking: `commandCalls.push({ ... })` - This is just collecting test data, not mocking behavior
Expand All @@ -118,7 +105,7 @@ Test → Plugin Handler → utilities → [DEPENDENCY INJECTION] createMockExecu
### What Gets Mocked
- Command execution via `createMockExecutor()`
- File system operations via `createMockFileSystemExecutor()`
- Nothing else - all vitest mocking is banned
- Internal modules can use Vitest mocks where appropriate

## Dependency Injection Strategy

Expand Down Expand Up @@ -359,8 +346,8 @@ describe('simulator-project re-exports', () => {
import { vi, describe, it, expect, beforeEach } from 'vitest';
import { z } from 'zod';

// CRITICAL: NO VITEST MOCKING ALLOWED
// Import ONLY what you need - no mock setup
// Use dependency-injection mocks for external boundaries.
// Vitest mocks are acceptable for internal collaborators when needed.

import tool from '../tool_name.ts';
import { createMockExecutor } from '../../utils/command.js';
Expand Down Expand Up @@ -1266,8 +1253,8 @@ npm run test:coverage -- src/plugins/simulator-workspace/
### Validation Scripts

```bash
# Check for vitest mocking violations
node scripts/check-code-patterns.js --pattern=vitest
# Check for architectural pattern violations
node scripts/check-code-patterns.js

# Check dependency injection compliance
node scripts/audit-dependency-container.js
Expand All @@ -1278,12 +1265,12 @@ node scripts/audit-dependency-container.js
## Best Practices Summary

1. **Dependency injection**: Always use createMockExecutor() and createMockFileSystemExecutor()
2. **No vitest mocking**: All vi.mock, vi.fn, etc. patterns are banned
2. **External boundaries via DI**: mock command execution/filesystem with injected executors
3. **Three dimensions**: Test input validation, command execution, and output processing
4. **Literal expectations**: Use exact strings in assertions to catch regressions
5. **Performance**: Ensure fast execution through proper mocking
6. **Coverage**: Aim for 95%+ with focus on error paths
7. **Consistency**: Follow standard patterns across all plugin tests
8. **Test safety**: Default executors prevent accidental real system calls

This testing strategy ensures robust, maintainable tests that provide confidence in plugin functionality while remaining resilient to implementation changes and completely eliminating vitest mocking dependencies.
This testing strategy ensures robust, maintainable tests that provide confidence in plugin functionality while remaining resilient to implementation changes and keeping external boundaries deterministic.
16 changes: 16 additions & 0 deletions manifests/tools/boot_sim.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,19 @@ description: Boot iOS simulator.
annotations:
title: Boot Simulator
destructiveHint: true
nextSteps:
- label: Open the Simulator app (makes it visible)
toolId: open_sim
priority: 1
- label: Install an app
toolId: install_app_sim
params:
simulatorId: ${simulatorId}
appPath: PATH_TO_YOUR_APP
priority: 2
- label: Launch an app
toolId: launch_app_sim
params:
simulatorId: ${simulatorId}
bundleId: YOUR_APP_BUNDLE_ID
priority: 3
10 changes: 10 additions & 0 deletions manifests/tools/build_run_sim.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,13 @@ predicates:
annotations:
title: Build Run Simulator
destructiveHint: true
nextSteps:
- label: Capture structured logs (app continues running)
toolId: start_sim_log_cap
priority: 1
- label: Capture console + structured logs (app restarts)
toolId: start_sim_log_cap
priority: 2
- label: Launch app with logs in one step
toolId: launch_app_logs_sim
priority: 3
10 changes: 10 additions & 0 deletions manifests/tools/debug_attach_sim.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,13 @@ names:
description: Attach LLDB to sim app.
routing:
stateful: true
nextSteps:
- label: Add a breakpoint
toolId: debug_breakpoint_add
priority: 1
- label: Continue execution
toolId: debug_continue
priority: 2
- label: Show call stack
toolId: debug_stack
priority: 3
13 changes: 13 additions & 0 deletions manifests/tools/get_app_bundle_id.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,16 @@ description: Extract bundle id from .app.
annotations:
title: Get App Bundle ID
readOnlyHint: true
nextSteps:
- label: Install on simulator
toolId: install_app_sim
priority: 1
- label: Launch on simulator
toolId: launch_app_sim
priority: 2
- label: Install on device
toolId: install_app_device
priority: 3
- label: Launch on device
toolId: launch_app_device
priority: 4
10 changes: 10 additions & 0 deletions manifests/tools/get_device_app_path.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,13 @@ description: Get device built app path.
annotations:
title: Get Device App Path
readOnlyHint: true
nextSteps:
- label: Get bundle ID
toolId: get_app_bundle_id
priority: 1
- label: Install app on device
toolId: install_app_device
priority: 2
- label: Launch app on device
toolId: launch_app_device
priority: 3
7 changes: 7 additions & 0 deletions manifests/tools/get_mac_app_path.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,10 @@ description: Get macOS built app path.
annotations:
title: Get macOS App Path
readOnlyHint: true
nextSteps:
- label: Get bundle ID
toolId: get_mac_bundle_id
priority: 1
- label: Launch app
toolId: launch_mac_app
priority: 2
7 changes: 7 additions & 0 deletions manifests/tools/get_mac_bundle_id.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,10 @@ description: Extract bundle id from macOS .app.
annotations:
title: Get Mac Bundle ID
readOnlyHint: true
nextSteps:
- label: Launch the app
toolId: launch_mac_app
priority: 1
- label: Build again
toolId: build_macos
priority: 2
13 changes: 13 additions & 0 deletions manifests/tools/get_sim_app_path.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,16 @@ description: Get sim built app path.
annotations:
title: Get Simulator App Path
readOnlyHint: true
nextSteps:
- label: Get bundle ID
toolId: get_app_bundle_id
priority: 1
- label: Boot simulator
toolId: boot_sim
priority: 2
- label: Install app
toolId: install_app_sim
priority: 3
- label: Launch app
toolId: launch_app_sim
priority: 4
7 changes: 7 additions & 0 deletions manifests/tools/install_app_sim.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,10 @@ description: Install app on sim.
annotations:
title: Install App Simulator
destructiveHint: true
nextSteps:
- label: Open the Simulator app
toolId: open_sim
priority: 1
- label: Launch the app
toolId: launch_app_sim
priority: 2
4 changes: 4 additions & 0 deletions manifests/tools/launch_app_device.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ description: Launch app on device.
annotations:
title: Launch App Device
destructiveHint: true
nextSteps:
- label: Stop the app
toolId: stop_app_device
priority: 1
4 changes: 4 additions & 0 deletions manifests/tools/launch_app_logs_sim.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@ routing:
annotations:
title: Launch App Logs Simulator
destructiveHint: true
nextSteps:
- label: Stop capture and retrieve logs
toolId: stop_sim_log_cap
priority: 1
Loading
Loading