#
tokens: 49815/50000 124/195 files (page 1/5)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 1 of 5. Use http://codebase.md/stefan-nitu/mcp-xcode-server?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .claude
│   └── settings.local.json
├── .github
│   └── workflows
│       └── ci.yml
├── .gitignore
├── .vscode
│   └── settings.json
├── CLAUDE.md
├── CONTRIBUTING.md
├── docs
│   ├── ARCHITECTURE.md
│   ├── ERROR-HANDLING.md
│   └── TESTING-PHILOSOPHY.md
├── examples
│   └── screenshot-demo.js
├── jest.config.cjs
├── jest.e2e.config.cjs
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
├── scripts
│   └── xcode-sync.swift
├── src
│   ├── application
│   │   └── ports
│   │       ├── ArtifactPorts.ts
│   │       ├── BuildPorts.ts
│   │       ├── CommandPorts.ts
│   │       ├── ConfigPorts.ts
│   │       ├── LoggingPorts.ts
│   │       ├── MappingPorts.ts
│   │       ├── OutputFormatterPorts.ts
│   │       ├── OutputParserPorts.ts
│   │       └── SimulatorPorts.ts
│   ├── cli.ts
│   ├── config.ts
│   ├── domain
│   │   ├── errors
│   │   │   └── DomainError.ts
│   │   ├── services
│   │   │   └── PlatformDetector.ts
│   │   ├── shared
│   │   │   └── Result.ts
│   │   └── tests
│   │       └── unit
│   │           └── PlatformDetector.unit.test.ts
│   ├── features
│   │   ├── app-management
│   │   │   ├── controllers
│   │   │   │   └── InstallAppController.ts
│   │   │   ├── domain
│   │   │   │   ├── InstallRequest.ts
│   │   │   │   └── InstallResult.ts
│   │   │   ├── factories
│   │   │   │   └── InstallAppControllerFactory.ts
│   │   │   ├── index.ts
│   │   │   ├── infrastructure
│   │   │   │   └── AppInstallerAdapter.ts
│   │   │   ├── tests
│   │   │   │   ├── e2e
│   │   │   │   │   ├── InstallAppController.e2e.test.ts
│   │   │   │   │   └── InstallAppMCP.e2e.test.ts
│   │   │   │   ├── integration
│   │   │   │   │   └── InstallAppController.integration.test.ts
│   │   │   │   └── unit
│   │   │   │       ├── AppInstallerAdapter.unit.test.ts
│   │   │   │       ├── InstallAppController.unit.test.ts
│   │   │   │       ├── InstallAppUseCase.unit.test.ts
│   │   │   │       ├── InstallRequest.unit.test.ts
│   │   │   │       └── InstallResult.unit.test.ts
│   │   │   └── use-cases
│   │   │       └── InstallAppUseCase.ts
│   │   ├── build
│   │   │   ├── controllers
│   │   │   │   └── BuildXcodeController.ts
│   │   │   ├── domain
│   │   │   │   ├── BuildDestination.ts
│   │   │   │   ├── BuildIssue.ts
│   │   │   │   ├── BuildRequest.ts
│   │   │   │   ├── BuildResult.ts
│   │   │   │   └── PlatformInfo.ts
│   │   │   ├── factories
│   │   │   │   └── BuildXcodeControllerFactory.ts
│   │   │   ├── index.ts
│   │   │   ├── infrastructure
│   │   │   │   ├── BuildArtifactLocatorAdapter.ts
│   │   │   │   ├── BuildDestinationMapperAdapter.ts
│   │   │   │   ├── XcbeautifyFormatterAdapter.ts
│   │   │   │   ├── XcbeautifyOutputParserAdapter.ts
│   │   │   │   └── XcodeBuildCommandAdapter.ts
│   │   │   ├── tests
│   │   │   │   ├── e2e
│   │   │   │   │   ├── BuildXcodeController.e2e.test.ts
│   │   │   │   │   └── BuildXcodeMCP.e2e.test.ts
│   │   │   │   ├── integration
│   │   │   │   │   └── BuildXcodeController.integration.test.ts
│   │   │   │   └── unit
│   │   │   │       ├── BuildArtifactLocatorAdapter.unit.test.ts
│   │   │   │       ├── BuildDestinationMapperAdapter.unit.test.ts
│   │   │   │       ├── BuildIssue.unit.test.ts
│   │   │   │       ├── BuildProjectUseCase.unit.test.ts
│   │   │   │       ├── BuildRequest.unit.test.ts
│   │   │   │       ├── BuildResult.unit.test.ts
│   │   │   │       ├── BuildXcodeController.unit.test.ts
│   │   │   │       ├── BuildXcodePresenter.unit.test.ts
│   │   │   │       ├── PlatformInfo.unit.test.ts
│   │   │   │       ├── XcbeautifyFormatterAdapter.unit.test.ts
│   │   │   │       ├── XcbeautifyOutputParserAdapter.unit.test.ts
│   │   │   │       └── XcodeBuildCommandAdapter.unit.test.ts
│   │   │   └── use-cases
│   │   │       └── BuildProjectUseCase.ts
│   │   └── simulator
│   │       ├── controllers
│   │       │   ├── BootSimulatorController.ts
│   │       │   ├── ListSimulatorsController.ts
│   │       │   └── ShutdownSimulatorController.ts
│   │       ├── domain
│   │       │   ├── BootRequest.ts
│   │       │   ├── BootResult.ts
│   │       │   ├── ListSimulatorsRequest.ts
│   │       │   ├── ListSimulatorsResult.ts
│   │       │   ├── ShutdownRequest.ts
│   │       │   ├── ShutdownResult.ts
│   │       │   └── SimulatorState.ts
│   │       ├── factories
│   │       │   ├── BootSimulatorControllerFactory.ts
│   │       │   ├── ListSimulatorsControllerFactory.ts
│   │       │   └── ShutdownSimulatorControllerFactory.ts
│   │       ├── index.ts
│   │       ├── infrastructure
│   │       │   ├── SimulatorControlAdapter.ts
│   │       │   └── SimulatorLocatorAdapter.ts
│   │       ├── tests
│   │       │   ├── e2e
│   │       │   │   ├── BootSimulatorController.e2e.test.ts
│   │       │   │   ├── BootSimulatorMCP.e2e.test.ts
│   │       │   │   ├── ListSimulatorsController.e2e.test.ts
│   │       │   │   ├── ListSimulatorsMCP.e2e.test.ts
│   │       │   │   ├── ShutdownSimulatorController.e2e.test.ts
│   │       │   │   └── ShutdownSimulatorMCP.e2e.test.ts
│   │       │   ├── integration
│   │       │   │   ├── BootSimulatorController.integration.test.ts
│   │       │   │   ├── ListSimulatorsController.integration.test.ts
│   │       │   │   └── ShutdownSimulatorController.integration.test.ts
│   │       │   └── unit
│   │       │       ├── BootRequest.unit.test.ts
│   │       │       ├── BootResult.unit.test.ts
│   │       │       ├── BootSimulatorController.unit.test.ts
│   │       │       ├── BootSimulatorUseCase.unit.test.ts
│   │       │       ├── ListSimulatorsController.unit.test.ts
│   │       │       ├── ListSimulatorsUseCase.unit.test.ts
│   │       │       ├── ShutdownRequest.unit.test.ts
│   │       │       ├── ShutdownResult.unit.test.ts
│   │       │       ├── ShutdownSimulatorUseCase.unit.test.ts
│   │       │       ├── SimulatorControlAdapter.unit.test.ts
│   │       │       └── SimulatorLocatorAdapter.unit.test.ts
│   │       └── use-cases
│   │           ├── BootSimulatorUseCase.ts
│   │           ├── ListSimulatorsUseCase.ts
│   │           └── ShutdownSimulatorUseCase.ts
│   ├── index.ts
│   ├── infrastructure
│   │   ├── repositories
│   │   │   └── DeviceRepository.ts
│   │   ├── services
│   │   │   └── DependencyChecker.ts
│   │   └── tests
│   │       └── unit
│   │           ├── DependencyChecker.unit.test.ts
│   │           └── DeviceRepository.unit.test.ts
│   ├── logger.ts
│   ├── presentation
│   │   ├── decorators
│   │   │   └── DependencyCheckingDecorator.ts
│   │   ├── formatters
│   │   │   ├── ErrorFormatter.ts
│   │   │   └── strategies
│   │   │       ├── BuildIssuesStrategy.ts
│   │   │       ├── DefaultErrorStrategy.ts
│   │   │       ├── ErrorFormattingStrategy.ts
│   │   │       └── OutputFormatterErrorStrategy.ts
│   │   ├── interfaces
│   │   │   ├── IDependencyChecker.ts
│   │   │   ├── MCPController.ts
│   │   │   └── MCPResponse.ts
│   │   ├── presenters
│   │   │   └── BuildXcodePresenter.ts
│   │   └── tests
│   │       └── unit
│   │           ├── BuildIssuesStrategy.unit.test.ts
│   │           ├── DefaultErrorStrategy.unit.test.ts
│   │           ├── DependencyCheckingDecorator.unit.test.ts
│   │           └── ErrorFormatter.unit.test.ts
│   ├── shared
│   │   ├── domain
│   │   │   ├── AppPath.ts
│   │   │   ├── DeviceId.ts
│   │   │   ├── Platform.ts
│   │   │   └── ProjectPath.ts
│   │   ├── index.ts
│   │   ├── infrastructure
│   │   │   ├── ConfigProviderAdapter.ts
│   │   │   └── ShellCommandExecutorAdapter.ts
│   │   └── tests
│   │       ├── mocks
│   │       │   ├── promisifyExec.ts
│   │       │   ├── selectiveExecMock.ts
│   │       │   └── xcodebuildHelpers.ts
│   │       ├── skipped
│   │       │   ├── cli.e2e.test.skip
│   │       │   ├── hook-e2e.test.skip
│   │       │   ├── hook-path.e2e.test.skip
│   │       │   └── hook.test.skip
│   │       ├── types
│   │       │   └── execTypes.ts
│   │       ├── unit
│   │       │   ├── AppPath.unit.test.ts
│   │       │   ├── ConfigProviderAdapter.unit.test.ts
│   │       │   ├── ProjectPath.unit.test.ts
│   │       │   └── ShellCommandExecutorAdapter.unit.test.ts
│   │       └── utils
│   │           ├── gitResetTestArtifacts.ts
│   │           ├── mockHelpers.ts
│   │           ├── TestEnvironmentCleaner.ts
│   │           ├── TestErrorInjector.ts
│   │           ├── testHelpers.ts
│   │           ├── TestProjectManager.ts
│   │           └── TestSimulatorManager.ts
│   ├── types.ts
│   ├── utils
│   │   ├── devices
│   │   │   ├── Devices.ts
│   │   │   ├── SimulatorApps.ts
│   │   │   ├── SimulatorBoot.ts
│   │   │   ├── SimulatorDevice.ts
│   │   │   ├── SimulatorInfo.ts
│   │   │   ├── SimulatorReset.ts
│   │   │   └── SimulatorUI.ts
│   │   ├── errors
│   │   │   ├── index.ts
│   │   │   └── xcbeautify-parser.ts
│   │   ├── index.ts
│   │   ├── LogManager.ts
│   │   ├── LogManagerInstance.ts
│   │   └── projects
│   │       ├── SwiftBuild.ts
│   │       ├── SwiftPackage.ts
│   │       ├── SwiftPackageInfo.ts
│   │       ├── Xcode.ts
│   │       ├── XcodeArchive.ts
│   │       ├── XcodeBuild.ts
│   │       ├── XcodeErrors.ts
│   │       ├── XcodeInfo.ts
│   │       └── XcodeProject.ts
│   └── utils.ts
├── test_artifacts
│   ├── Test.xcworkspace
│   │   ├── contents.xcworkspacedata
│   │   └── xcuserdata
│   │       └── stefan.xcuserdatad
│   │           └── UserInterfaceState.xcuserstate
│   ├── TestProjectSwiftTesting
│   │   ├── TestProjectSwiftTesting
│   │   │   ├── Assets.xcassets
│   │   │   │   ├── AccentColor.colorset
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── AppIcon.appiconset
│   │   │   │   │   └── Contents.json
│   │   │   │   └── Contents.json
│   │   │   ├── ContentView.swift
│   │   │   ├── Item.swift
│   │   │   ├── TestProjectSwiftTesting.entitlements
│   │   │   └── TestProjectSwiftTestingApp.swift
│   │   ├── TestProjectSwiftTesting.xcodeproj
│   │   │   ├── project.pbxproj
│   │   │   ├── project.xcworkspace
│   │   │   │   ├── contents.xcworkspacedata
│   │   │   │   └── xcuserdata
│   │   │   │       └── stefan.xcuserdatad
│   │   │   │           └── UserInterfaceState.xcuserstate
│   │   │   └── xcuserdata
│   │   │       └── stefan.xcuserdatad
│   │   │           └── xcschemes
│   │   │               └── xcschememanagement.plist
│   │   ├── TestProjectSwiftTestingTests
│   │   │   └── TestProjectSwiftTestingTests.swift
│   │   └── TestProjectSwiftTestingUITests
│   │       ├── TestProjectSwiftTestingUITests.swift
│   │       └── TestProjectSwiftTestingUITestsLaunchTests.swift
│   ├── TestProjectWatchOS
│   │   ├── TestProjectWatchOS
│   │   │   ├── Assets.xcassets
│   │   │   │   ├── AccentColor.colorset
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── AppIcon.appiconset
│   │   │   │   │   └── Contents.json
│   │   │   │   └── Contents.json
│   │   │   ├── ContentView.swift
│   │   │   └── TestProjectWatchOSApp.swift
│   │   ├── TestProjectWatchOS Watch App
│   │   │   ├── Assets.xcassets
│   │   │   │   ├── AccentColor.colorset
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── AppIcon.appiconset
│   │   │   │   │   └── Contents.json
│   │   │   │   └── Contents.json
│   │   │   ├── ContentView.swift
│   │   │   └── TestProjectWatchOSApp.swift
│   │   ├── TestProjectWatchOS Watch AppTests
│   │   │   └── TestProjectWatchOS_Watch_AppTests.swift
│   │   ├── TestProjectWatchOS Watch AppUITests
│   │   │   ├── TestProjectWatchOS_Watch_AppUITests.swift
│   │   │   └── TestProjectWatchOS_Watch_AppUITestsLaunchTests.swift
│   │   ├── TestProjectWatchOS.xcodeproj
│   │   │   ├── project.pbxproj
│   │   │   └── project.xcworkspace
│   │   │       └── contents.xcworkspacedata
│   │   ├── TestProjectWatchOSTests
│   │   │   └── TestProjectWatchOSTests.swift
│   │   └── TestProjectWatchOSUITests
│   │       ├── TestProjectWatchOSUITests.swift
│   │       └── TestProjectWatchOSUITestsLaunchTests.swift
│   ├── TestProjectXCTest
│   │   ├── TestProjectXCTest
│   │   │   ├── Assets.xcassets
│   │   │   │   ├── AccentColor.colorset
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── AppIcon.appiconset
│   │   │   │   │   └── Contents.json
│   │   │   │   └── Contents.json
│   │   │   ├── ContentView.swift
│   │   │   ├── Item.swift
│   │   │   ├── TestProjectXCTest.entitlements
│   │   │   └── TestProjectXCTestApp.swift
│   │   ├── TestProjectXCTest.xcodeproj
│   │   │   ├── project.pbxproj
│   │   │   ├── project.xcworkspace
│   │   │   │   ├── contents.xcworkspacedata
│   │   │   │   └── xcuserdata
│   │   │   │       └── stefan.xcuserdatad
│   │   │   │           └── UserInterfaceState.xcuserstate
│   │   │   └── xcuserdata
│   │   │       └── stefan.xcuserdatad
│   │   │           └── xcschemes
│   │   │               └── xcschememanagement.plist
│   │   ├── TestProjectXCTestTests
│   │   │   └── TestProjectXCTestTests.swift
│   │   └── TestProjectXCTestUITests
│   │       ├── TestProjectXCTestUITests.swift
│   │       └── TestProjectXCTestUITestsLaunchTests.swift
│   ├── TestSwiftPackageSwiftTesting
│   │   ├── .gitignore
│   │   ├── Package.swift
│   │   ├── Sources
│   │   │   ├── TestSwiftPackageSwiftTesting
│   │   │   │   └── TestSwiftPackageSwiftTesting.swift
│   │   │   └── TestSwiftPackageSwiftTestingExecutable
│   │   │       └── main.swift
│   │   └── Tests
│   │       └── TestSwiftPackageSwiftTestingTests
│   │           └── TestSwiftPackageSwiftTestingTests.swift
│   └── TestSwiftPackageXCTest
│       ├── .gitignore
│       ├── Package.swift
│       ├── Sources
│       │   ├── TestSwiftPackageXCTest
│       │   │   └── TestSwiftPackageXCTest.swift
│       │   └── TestSwiftPackageXCTestExecutable
│       │       └── main.swift
│       └── Tests
│           └── TestSwiftPackageXCTestTests
│               └── TestSwiftPackageXCTestTests.swift
├── tsconfig.json
└── XcodeProjectModifier
    ├── Package.resolved
    ├── Package.swift
    └── Sources
        └── XcodeProjectModifier
            └── main.swift
```

# Files

--------------------------------------------------------------------------------
/test_artifacts/TestSwiftPackageSwiftTesting/.gitignore:
--------------------------------------------------------------------------------

```
1 | .DS_Store
2 | /.build
3 | /Packages
4 | xcuserdata/
5 | DerivedData/
6 | .swiftpm/configuration/registries.json
7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
8 | .netrc
9 | 
```

--------------------------------------------------------------------------------
/test_artifacts/TestSwiftPackageXCTest/.gitignore:
--------------------------------------------------------------------------------

```
1 | .DS_Store
2 | /.build
3 | /Packages
4 | xcuserdata/
5 | DerivedData/
6 | .swiftpm/configuration/registries.json
7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
8 | .netrc
9 | 
```

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
 1 | node_modules/
 2 | dist/
 3 | coverage/
 4 | DerivedData/
 5 | *.log
 6 | .DS_Store
 7 | .env
 8 | .env.local
 9 | .env.*.local
10 | *.xcuserdata
11 | *.xcworkspace/xcuserdata/
12 | *.xcodeproj/xcuserdata/
13 | /build/
14 | *.app
15 | *.ipa
16 | *.dSYM.zip
17 | *.dSYM
18 | 
19 | # Swift Package Manager
20 | .build/
21 | .swiftpm/
22 | 
23 | # XcodeProjectModifier build artifacts
24 | XcodeProjectModifier/.build/
25 | XcodeProjectModifier/.swiftpm/
26 | 
27 | # Test artifacts
28 | test-results*.log
29 | test_artifacts/**/*.xcuserstate
30 | test_artifacts/**/*.xcuserdatad/
31 | test_artifacts/**/xcuserdata/
32 | 
33 | # Temporary files
34 | *.tmp
35 | .tmp/
36 | /tmp/
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # MCP Xcode Server
  2 | 
  3 | [![CI](https://github.com/stefan-nitu/mcp-xcode-server/actions/workflows/ci.yml/badge.svg)](https://github.com/stefan-nitu/mcp-xcode-server/actions/workflows/ci.yml)
  4 | 
  5 | A Model Context Protocol (MCP) server that enables AI assistants to build, test, run, and manage Apple platform projects through natural language interactions.
  6 | 
  7 | ## Version: 0.6.0
  8 | 
  9 | ## Purpose
 10 | 
 11 | This MCP server bridges the gap between AI assistants and Apple's development ecosystem. It allows AI tools like Claude to directly execute Xcode and Swift Package Manager commands, enabling automated development workflows without manual intervention. The server is designed for token efficiency, providing concise output while maintaining comprehensive error reporting and debugging capabilities.
 12 | 
 13 | ## Why Use MCP Xcode Server?
 14 | 
 15 | ### Key Advantages
 16 | 
 17 | - **AI-Native Development**: Enables AI assistants to build, test, and run iOS/macOS apps directly
 18 | - **Token Efficiency**: Optimized output shows only essential information (errors, warnings, test results)
 19 | - **Smart Error Handling**: Parses build errors and provides actionable suggestions
 20 | - **Visual Debugging**: Capture simulator screenshots to verify UI changes
 21 | - **Automatic Simulator Management**: Intelligently reuses running simulators to save time
 22 | - **Xcode Integration**: Auto-syncs file operations with Xcode projects via hooks
 23 | - **Persistent Logging**: All operations saved to `~/.mcp-xcode-server/logs/` for debugging
 24 | - **Multi-Platform**: Supports iOS, macOS, tvOS, watchOS, and visionOS from a single interface
 25 | 
 26 | ### Use Cases
 27 | 
 28 | - **Automated Testing**: AI can run your test suites and analyze failures
 29 | - **Build Verification**: Quickly verify code changes compile across platforms
 30 | - **UI Development**: Build and screenshot apps to verify visual changes
 31 | - **Dependency Management**: Add, update, or remove Swift packages programmatically
 32 | - **Cross-Platform Development**: Test the same code on multiple Apple platforms
 33 | - **CI/CD Integration**: Automate build and test workflows through natural language
 34 | 
 35 | ## Limitations
 36 | 
 37 | ### What It Can't Do
 38 | 
 39 | - **No SwiftUI Previews**: Xcode's live preview requires the full IDE
 40 | - **No Interactive UI Testing**: Cannot simulate user interactions (taps, swipes)
 41 | - **No Physical Devices**: Simulator-only for iOS/tvOS/watchOS/visionOS
 42 | - **No Debugging**: No breakpoints, step-through debugging, or LLDB access
 43 | - **No Xcode UI Features**: Project configuration, storyboard editing require Xcode
 44 | - **Platform Requirements**: Requires macOS 14+, Xcode 16+, iOS 17+ simulators
 45 | 
 46 | ### When You Still Need Xcode
 47 | 
 48 | - Designing UI with Interface Builder or SwiftUI previews
 49 | - Debugging with breakpoints and variable inspection
 50 | - Profiling with Instruments
 51 | - Managing certificates and provisioning profiles
 52 | - Testing on physical devices
 53 | - Using Xcode-specific features (Playgrounds, AR tools, etc.)
 54 | 
 55 | ## Core Features
 56 | 
 57 | ### Build & Test Automation
 58 | - Build and run Xcode projects/workspaces
 59 | - Execute Swift Package Manager packages
 60 | - Run XCTest and Swift Testing suites
 61 | - **Xcode projects**: Support for custom build configurations (Debug, Release, Beta, Staging, etc.)
 62 | - **Swift packages**: Standard SPM configurations (Debug/Release only - SPM limitation)
 63 | 
 64 | ### Simulator Management
 65 | - List and boot simulators for any Apple platform
 66 | - Capture screenshots for visual verification
 67 | - Install/uninstall apps
 68 | - Retrieve device logs with filtering
 69 | 
 70 | ### Error Intelligence
 71 | - **Compile Errors**: Shows file, line, column with error message
 72 | - **Scheme Errors**: Suggests using `list_schemes` tool
 73 | - **Code Signing**: Identifies certificate and provisioning issues
 74 | - **Dependencies**: Detects missing modules and version conflicts
 75 | 
 76 | ### File Sync Hooks
 77 | - Automatically syncs file operations with Xcode projects
 78 | - Intelligently assigns files to correct build phases (Sources, Resources, etc.)
 79 | - Respects `.no-xcode-sync` opt-out files
 80 | - Maintains proper group structure in Xcode
 81 | 
 82 | ## Installation
 83 | 
 84 | ### Prerequisites
 85 | 
 86 | - macOS 14.0 or later
 87 | - Xcode 16.0 or later
 88 | - Node.js 18+
 89 | - Xcode Command Line Tools
 90 | - Simulators for target platforms
 91 | 
 92 | ### Quick Setup
 93 | 
 94 | ```bash
 95 | # Install globally
 96 | npm install -g mcp-xcode-server
 97 | 
 98 | # Run interactive setup
 99 | mcp-xcode-server setup
100 | ```
101 | 
102 | The setup wizard will:
103 | - Configure the MCP server for Claude
104 | - Optionally set up Xcode sync hooks
105 | - Build necessary helper tools
106 | 
107 | ### Manual Configuration
108 | 
109 | Add to `~/.claude.json` (global) or `.claude/settings.json` (project):
110 | 
111 | ```json
112 | {
113 |   "mcpServers": {
114 |     "mcp-xcode-server": {
115 |       "type": "stdio",
116 |       "command": "mcp-xcode-server",
117 |       "args": ["serve"],
118 |       "env": {}
119 |     }
120 |   }
121 | }
122 | ```
123 | 
124 | ## Available Tools
125 | 
126 | ### Building
127 | 
128 | - **`build_xcode`**: Build Xcode projects/workspaces (supports custom configurations)
129 | - **`build_swift_package`**: Build Swift packages (Debug/Release only per SPM spec)
130 | - **`run_xcode`**: Build and run on simulator/macOS
131 | - **`run_swift_package`**: Execute Swift package executables
132 | 
133 | ### Testing
134 | 
135 | - **`test_xcode`**: Run XCTest/Swift Testing suites
136 | - **`test_swift_package`**: Test Swift packages
137 | - Supports test filtering by class/method
138 | 
139 | ### Project Information
140 | 
141 | - **`list_schemes`**: Get available Xcode schemes
142 | - **`get_project_info`**: Comprehensive project details
143 | - **`list_targets`**: List all build targets
144 | - **`get_build_settings`**: Get scheme configuration
145 | 
146 | ### Simulator Management
147 | 
148 | - **`list_simulators`**: Show available devices
149 | - **`boot_simulator`**: Start a simulator
150 | - **`shutdown_simulator`**: Stop a simulator
151 | - **`view_simulator_screen`**: Capture screenshot
152 | 
153 | ### App Management
154 | 
155 | - **`install_app`**: Install app on simulator
156 | - **`uninstall_app`**: Remove app by bundle ID
157 | - **`get_device_logs`**: Retrieve filtered device logs
158 | 
159 | ### Distribution
160 | 
161 | - **`archive_project`**: Create .xcarchive
162 | - **`export_ipa`**: Export IPA from archive
163 | 
164 | ### Maintenance
165 | 
166 | - **`clean_build`**: Clean build artifacts/DerivedData
167 | - **`manage_dependencies`**: Add/remove/update Swift packages
168 | 
169 | ## Platform Support
170 | 
171 | | Platform | Simulator Required | Default Device | Min Version |
172 | |----------|-------------------|----------------|-------------|
173 | | iOS | Yes | iPhone 16 Pro | iOS 17+ |
174 | | macOS | No | Host machine | macOS 14+ |
175 | | tvOS | Yes | Apple TV | tvOS 17+ |
176 | | watchOS | Yes | Apple Watch Series 10 | watchOS 10+ |
177 | | visionOS | Yes | Apple Vision Pro | visionOS 1.0+ |
178 | 
179 | ## Architecture
180 | 
181 | The server follows Clean/Hexagonal Architecture with SOLID principles:
182 | 
183 | ### Core Structure
184 | ```
185 | src/
186 | ├── features/         # Feature-based vertical slices
187 | │   ├── build/       # Build feature
188 | │   │   ├── domain/  # Build domain objects
189 | │   │   ├── use-cases/
190 | │   │   ├── infrastructure/
191 | │   │   ├── controllers/
192 | │   │   └── factories/
193 | │   ├── simulator/   # Simulator management
194 | │   │   └── ...same structure...
195 | │   └── app-management/ # App installation
196 | │       └── ...same structure...
197 | ├── shared/          # Cross-feature shared code
198 | │   ├── domain/      # Shared value objects
199 | │   └── infrastructure/
200 | ├── application/     # Application layer ports
201 | │   └── ports/       # Interface definitions
202 | ├── presentation/    # MCP presentation layer
203 | │   ├── interfaces/  # MCP contracts
204 | │   └── formatters/  # Output formatting
205 | └── infrastructure/  # Shared infrastructure
206 |     ├── repositories/
207 |     └── services/
208 | ```
209 | 
210 | ### Key Design Principles
211 | - **Clean Architecture**: Dependency rule - inner layers know nothing about outer layers
212 | - **Domain-Driven Design**: Rich domain models with embedded validation
213 | - **Type Safety**: Full TypeScript with domain primitives and parse-don't-validate pattern
214 | - **Security First**: Path validation, command injection protection at boundaries
215 | - **Error Recovery**: Typed domain errors with graceful handling and helpful suggestions
216 | 
217 | ## Logging
218 | 
219 | All operations are logged to `~/.mcp-xcode-server/logs/`:
220 | - Daily folders (e.g., `2025-01-27/`)
221 | - 7-day automatic retention
222 | - Full xcodebuild/swift output preserved
223 | - Symlinks to latest logs for easy access
224 | 
225 | ## Development
226 | 
227 | ```bash
228 | # Build
229 | npm run build
230 | 
231 | # Test
232 | npm test              # All tests
233 | npm run test:unit    # Unit tests only
234 | npm run test:e2e     # End-to-end tests
235 | npm run test:coverage # With coverage
236 | 
237 | # Development
238 | npm run dev          # Build and run
239 | ```
240 | 
241 | ## Contributing
242 | 
243 | Contributions welcome! Please ensure:
244 | - Tests pass (`npm test`)
245 | - Code follows SOLID principles
246 | - New tools include tests
247 | - Documentation updated
248 | 
249 | ## License
250 | 
251 | MIT
252 | 
253 | ## Support
254 | 
255 | - Report issues: [GitHub Issues](https://github.com/yourusername/mcp-xcode-server/issues)
256 | - Documentation: [MCP Protocol](https://modelcontextprotocol.io)
```

--------------------------------------------------------------------------------
/CLAUDE.md:
--------------------------------------------------------------------------------

```markdown
 1 | # MANDATORY INITIALIZATION - DO THIS IMMEDIATELY
 2 | 
 3 | ## ⚠️ STOP - READ THIS FIRST ⚠️
 4 | 
 5 | **YOU MUST READ THESE DOCUMENTS IMMEDIATELY UPON STARTING ANY CONVERSATION ABOUT THIS PROJECT.**
 6 | **DO NOT WAIT TO BE ASKED. DO NOT PROCEED WITHOUT READING THEM FIRST.**
 7 | 
 8 | ### Required Documents (READ NOW):
 9 | 1. `docs/TESTING-PHILOSOPHY.md` - Critical testing patterns and approaches
10 | 2. `docs/ARCHITECTURE.md` - Clean/Hexagonal Architecture structure
11 | 3. `docs/ERROR-HANDLING.md` - Error handling patterns and presentation conventions
12 | 
13 | ### Verification Checklist:
14 | - [ ] I have read `docs/TESTING-PHILOSOPHY.md` completely
15 | - [ ] I have read `docs/ARCHITECTURE.md` completely
16 | - [ ] I have read `docs/ERROR-HANDLING.md` completely
17 | - [ ] I understand the testing philosophy (integration focus, proper mocking, behavior testing)
18 | - [ ] I understand the architecture layers (Domain, Application, Infrastructure, Presentation)
19 | - [ ] I understand error handling patterns (typed errors, emoji prefixes, separation of concerns)
20 | 
21 | If you haven't read these documents yet, STOP and read them now using the Read tool.
22 | Only after reading all three documents should you proceed to help the user.
23 | 
24 | ## Project Context
25 | 
26 | This is an MCP (Model Context Protocol) server for Xcode operations. The codebase follows:
27 | - Clean/Hexagonal Architecture principles
28 | - Integration-focused testing (60% integration, 25% unit, 10% E2E, 5% static)
29 | - Parse-don't-validate pattern with domain validation
30 | - Domain primitives over primitive types
31 | - Type-safe enum comparisons (always use `SimulatorState.Booted`, never `'Booted'`)
```

--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------

```markdown
 1 | # Contributing to MCP Apple Simulator
 2 | 
 3 | ## Branching Strategy
 4 | 
 5 | We use **GitHub Flow** - a simple, effective branching model:
 6 | 
 7 | ### Branches
 8 | - **`main`** - Always stable, production-ready code. Every commit should be deployable.
 9 | - **`feature/*`** - Feature branches for new functionality
10 | - **`fix/*`** - Bug fix branches
11 | - **`docs/*`** - Documentation updates
12 | 
13 | ### Workflow
14 | 
15 | 1. **Create a feature branch** from main:
16 |    ```bash
17 |    git checkout main
18 |    git pull origin main
19 |    git checkout -b feature/your-feature-name
20 |    ```
21 | 
22 | 2. **Make your changes** and commit:
23 |    ```bash
24 |    git add .
25 |    git commit -m "feat: add new simulator tool"
26 |    ```
27 | 
28 | 3. **Push and create a Pull Request**:
29 |    ```bash
30 |    git push origin feature/your-feature-name
31 |    ```
32 | 
33 | 4. **After review and CI passes**, merge to main
34 | 
35 | ### Commit Message Convention
36 | 
37 | We use conventional commits:
38 | - `feat:` - New feature
39 | - `fix:` - Bug fix
40 | - `docs:` - Documentation changes
41 | - `test:` - Test additions or changes
42 | - `refactor:` - Code refactoring
43 | - `chore:` - Maintenance tasks
44 | 
45 | ### Why No Develop Branch?
46 | 
47 | We intentionally don't use a `develop` branch because:
48 | - **Simplicity** - Fewer branches to manage
49 | - **No sync issues** - No divergence between develop and main
50 | - **Continuous deployment** - Every merge to main is ready for users
51 | - **Local tool** - Users update when they want, not on a release schedule
52 | 
53 | ### Pull Request Guidelines
54 | 
55 | 1. **All tests must pass** - Run `npm test` locally first
56 | 2. **Update documentation** - If adding features, update README
57 | 3. **Small, focused PRs** - Easier to review and less likely to conflict
58 | 4. **Clean commit history** - Squash commits if needed
59 | 
60 | ### Testing
61 | 
62 | Before submitting a PR:
63 | ```bash
64 | npm run build          # Build TypeScript
65 | npm run test:unit      # Run unit tests
66 | npm run test:coverage  # Check coverage (aim for >75%)
67 | ```
68 | 
69 | ### Code Style
70 | 
71 | - TypeScript strict mode enabled
72 | - No `any` types without good reason
73 | - Follow existing patterns in the codebase
74 | - Use dependency injection for testability
75 | 
76 | ### Questions?
77 | 
78 | Open an issue for discussion before making large changes.
```

--------------------------------------------------------------------------------
/scripts/xcode-sync.swift:
--------------------------------------------------------------------------------

```swift
1 | 
```

--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------

```json
1 | {
2 | }
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectSwiftTesting/TestProjectSwiftTesting/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "info" : {
3 |     "author" : "xcode",
4 |     "version" : 1
5 |   }
6 | }
7 | 
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectWatchOS/TestProjectWatchOS Watch App/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "info" : {
3 |     "author" : "xcode",
4 |     "version" : 1
5 |   }
6 | }
7 | 
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectWatchOS/TestProjectWatchOS/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "info" : {
3 |     "author" : "xcode",
4 |     "version" : 1
5 |   }
6 | }
7 | 
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectXCTest/TestProjectXCTest/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "info" : {
3 |     "author" : "xcode",
4 |     "version" : 1
5 |   }
6 | }
7 | 
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectSwiftTesting/TestProjectSwiftTesting/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "colors" : [
 3 |     {
 4 |       "idiom" : "universal"
 5 |     }
 6 |   ],
 7 |   "info" : {
 8 |     "author" : "xcode",
 9 |     "version" : 1
10 |   }
11 | }
12 | 
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectWatchOS/TestProjectWatchOS Watch App/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "colors" : [
 3 |     {
 4 |       "idiom" : "universal"
 5 |     }
 6 |   ],
 7 |   "info" : {
 8 |     "author" : "xcode",
 9 |     "version" : 1
10 |   }
11 | }
12 | 
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectWatchOS/TestProjectWatchOS/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "colors" : [
 3 |     {
 4 |       "idiom" : "universal"
 5 |     }
 6 |   ],
 7 |   "info" : {
 8 |     "author" : "xcode",
 9 |     "version" : 1
10 |   }
11 | }
12 | 
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectXCTest/TestProjectXCTest/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "colors" : [
 3 |     {
 4 |       "idiom" : "universal"
 5 |     }
 6 |   ],
 7 |   "info" : {
 8 |     "author" : "xcode",
 9 |     "version" : 1
10 |   }
11 | }
12 | 
```

--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------

```typescript
1 | import { exec } from 'child_process';
2 | import { promisify } from 'util';
3 | 
4 | /**
5 |  * Promisified version of exec for async/await usage
6 |  */
7 | export const execAsync = promisify(exec);
```

--------------------------------------------------------------------------------
/src/presentation/formatters/strategies/ErrorFormattingStrategy.ts:
--------------------------------------------------------------------------------

```typescript
1 | /**
2 |  * Strategy interface for formatting different types of errors
3 |  */
4 | export interface ErrorFormattingStrategy {
5 |   canFormat(error: any): boolean;
6 |   format(error: any): string;
7 | }
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectWatchOS/TestProjectWatchOS Watch App/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "images" : [
 3 |     {
 4 |       "idiom" : "universal",
 5 |       "platform" : "watchos",
 6 |       "size" : "1024x1024"
 7 |     }
 8 |   ],
 9 |   "info" : {
10 |     "author" : "xcode",
11 |     "version" : 1
12 |   }
13 | }
14 | 
```

--------------------------------------------------------------------------------
/test_artifacts/TestSwiftPackageXCTest/Sources/TestSwiftPackageXCTest/TestSwiftPackageXCTest.swift:
--------------------------------------------------------------------------------

```swift
1 | // The Swift Programming Language
2 | // https://docs.swift.org/swift-book
3 | 
4 | public struct TestSwiftPackageXCTest {
5 |     public let text = "Hello, TestSwiftPackageXCTest!"
6 |     
7 |     public init() {}
8 | }
9 | 
```

--------------------------------------------------------------------------------
/test_artifacts/TestSwiftPackageSwiftTesting/Sources/TestSwiftPackageSwiftTesting/TestSwiftPackageSwiftTesting.swift:
--------------------------------------------------------------------------------

```swift
1 | // The Swift Programming Language
2 | // https://docs.swift.org/swift-book
3 | 
4 | public struct TestSwiftPackageSwiftTesting {
5 |     public let text = "Hello, TestSwiftPackageSwiftTesting!"
6 |     
7 |     public init() {}
8 | }
9 | 
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectXCTest/TestProjectXCTest/Item.swift:
--------------------------------------------------------------------------------

```swift
 1 | //
 2 | //  Item.swift
 3 | //  TestProjectXCTest
 4 | //
 5 | //  Created by Stefan Dragos Nitu on 17/08/2025.
 6 | //
 7 | 
 8 | import Foundation
 9 | import SwiftData
10 | 
11 | @Model
12 | final class Item {
13 |     var timestamp: Date
14 |     
15 |     init(timestamp: Date) {
16 |         self.timestamp = timestamp
17 |     }
18 | }
19 | 
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectSwiftTesting/TestProjectSwiftTesting/Item.swift:
--------------------------------------------------------------------------------

```swift
 1 | //
 2 | //  Item.swift
 3 | //  TestProjectSwiftTesting
 4 | //
 5 | //  Created by Stefan Dragos Nitu on 17/08/2025.
 6 | //
 7 | 
 8 | import Foundation
 9 | import SwiftData
10 | 
11 | @Model
12 | final class Item {
13 |     var timestamp: Date
14 |     
15 |     init(timestamp: Date) {
16 |         self.timestamp = timestamp
17 |     }
18 | }
19 | 
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectWatchOS/TestProjectWatchOS/TestProjectWatchOSApp.swift:
--------------------------------------------------------------------------------

```swift
 1 | //
 2 | //  TestProjectWatchOSApp.swift
 3 | //  TestProjectWatchOS
 4 | //
 5 | //  Created by Stefan Dragos Nitu on 22/08/2025.
 6 | //
 7 | 
 8 | import SwiftUI
 9 | 
10 | @main
11 | struct TestProjectWatchOSApp: App {
12 |     var body: some Scene {
13 |         WindowGroup {
14 |             ContentView()
15 |         }
16 |     }
17 | }
18 | 
```

--------------------------------------------------------------------------------
/src/application/ports/ArtifactPorts.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Port interfaces for build artifact management
 3 |  * 
 4 |  * These ports define how the application layer locates
 5 |  * and manages build artifacts (apps, frameworks, etc.)
 6 |  */
 7 | 
 8 | export interface IAppLocator {
 9 |   findApp(derivedDataPath: string): Promise<string | undefined>;
10 | }
```

--------------------------------------------------------------------------------
/src/shared/tests/mocks/xcodebuildHelpers.ts:
--------------------------------------------------------------------------------

```typescript
1 | /**
2 |  * Checks if a command is an xcodebuild command
3 |  */
4 | export function isXcodebuildCommand(cmd: string): boolean {
5 |   return cmd.startsWith('xcodebuild') || 
6 |          cmd.includes(' xcodebuild ') || 
7 |          cmd.includes('|xcodebuild') || 
8 |          cmd.includes('&& xcodebuild');
9 | }
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectWatchOS/TestProjectWatchOS Watch App/TestProjectWatchOSApp.swift:
--------------------------------------------------------------------------------

```swift
 1 | //
 2 | //  TestProjectWatchOSApp.swift
 3 | //  TestProjectWatchOS Watch App
 4 | //
 5 | //  Created by Stefan Dragos Nitu on 22/08/2025.
 6 | //
 7 | 
 8 | import SwiftUI
 9 | 
10 | @main
11 | struct TestProjectWatchOS_Watch_AppApp: App {
12 |     var body: some Scene {
13 |         WindowGroup {
14 |             ContentView()
15 |         }
16 |     }
17 | }
18 | 
```

--------------------------------------------------------------------------------
/src/presentation/interfaces/MCPResponse.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * MCP Framework Response Model
 3 |  * 
 4 |  * Defines the response format expected by the MCP (Model Context Protocol) framework.
 5 |  * This is a presentation layer contract for formatting output to the MCP client.
 6 |  */
 7 | export interface MCPResponse {
 8 |   content: Array<{
 9 |     type: string;
10 |     text: string;
11 |   }>;
12 | }
```

--------------------------------------------------------------------------------
/src/shared/tests/types/execTypes.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Node.js exec error with stdout/stderr attached
 3 |  * This is how Node.js exec actually behaves - it attaches stdout/stderr to the error
 4 |  */
 5 | export interface NodeExecError extends Error {
 6 |   code?: number;
 7 |   stdout?: string;
 8 |   stderr?: string;
 9 | }
10 | 
11 | /**
12 |  * Mock call type for exec function  
13 |  */
14 | export type ExecMockCall = [string, ...unknown[]];
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectWatchOS/TestProjectWatchOSTests/TestProjectWatchOSTests.swift:
--------------------------------------------------------------------------------

```swift
 1 | //
 2 | //  TestProjectWatchOSTests.swift
 3 | //  TestProjectWatchOSTests
 4 | //
 5 | //  Created by Stefan Dragos Nitu on 22/08/2025.
 6 | //
 7 | 
 8 | import Testing
 9 | @testable import TestProjectWatchOS
10 | 
11 | struct TestProjectWatchOSTests {
12 | 
13 |     @Test func example() async throws {
14 |         // Write your test here and use APIs like `#expect(...)` to check expected conditions.
15 |     }
16 | 
17 | }
18 | 
```

--------------------------------------------------------------------------------
/src/application/ports/OutputFormatterPorts.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Port for formatting build output
 3 |  * This allows us to swap between different formatters (xcbeautify, raw, custom)
 4 |  */
 5 | export interface IOutputFormatter {
 6 |   /**
 7 |    * Format raw build output into a more readable format
 8 |    * @param rawOutput The raw output from xcodebuild
 9 |    * @returns Formatted output
10 |    */
11 |   format(rawOutput: string): Promise<string>;
12 | }
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectWatchOS/TestProjectWatchOS Watch AppTests/TestProjectWatchOS_Watch_AppTests.swift:
--------------------------------------------------------------------------------

```swift
 1 | //
 2 | //  TestProjectWatchOS_Watch_AppTests.swift
 3 | //  TestProjectWatchOS Watch AppTests
 4 | //
 5 | //  Created by Stefan Dragos Nitu on 22/08/2025.
 6 | //
 7 | 
 8 | import Testing
 9 | @testable import TestProjectWatchOS_Watch_App
10 | 
11 | struct TestProjectWatchOS_Watch_AppTests {
12 | 
13 |     @Test func example() async throws {
14 |         // Write your test here and use APIs like `#expect(...)` to check expected conditions.
15 |     }
16 | 
17 | }
18 | 
```

--------------------------------------------------------------------------------
/src/application/ports/LoggingPorts.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Interface for log management operations
 3 |  * Cross-cutting concern used by multiple use cases
 4 |  */
 5 | export interface ILogManager {
 6 |   saveLog(
 7 |     operation: 'build' | 'test' | 'run' | 'archive' | 'clean',
 8 |     content: string,
 9 |     projectName?: string,
10 |     metadata?: Record<string, any>
11 |   ): string;
12 |   
13 |   saveDebugData(
14 |     operation: string,
15 |     data: any,
16 |     projectName?: string
17 |   ): string;
18 | }
```

--------------------------------------------------------------------------------
/src/domain/shared/Result.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Generic Result type for operations that can fail
 3 |  * Used throughout the domain layer to avoid throwing
 4 |  */
 5 | export type Result<T, E = Error> =
 6 |   | { success: true; value: T }
 7 |   | { success: false; error: E };
 8 | 
 9 | export const Result = {
10 |   ok<T>(value: T): Result<T, never> {
11 |     return { success: true, value };
12 |   },
13 | 
14 |   fail<E>(error: E): Result<never, E> {
15 |     return { success: false, error };
16 |   }
17 | };
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectWatchOS/TestProjectWatchOS/ContentView.swift:
--------------------------------------------------------------------------------

```swift
 1 | //
 2 | //  ContentView.swift
 3 | //  TestProjectWatchOS
 4 | //
 5 | //  Created by Stefan Dragos Nitu on 22/08/2025.
 6 | //
 7 | 
 8 | import SwiftUI
 9 | 
10 | struct ContentView: View {
11 |     var body: some View {
12 |         VStack {
13 |             Image(systemName: "globe")
14 |                 .imageScale(.large)
15 |                 .foregroundStyle(.tint)
16 |             Text("Hello, world!")
17 |         }
18 |         .padding()
19 |     }
20 | }
21 | 
22 | #Preview {
23 |     ContentView()
24 | }
25 | 
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectWatchOS/TestProjectWatchOS Watch App/ContentView.swift:
--------------------------------------------------------------------------------

```swift
 1 | //
 2 | //  ContentView.swift
 3 | //  TestProjectWatchOS Watch App
 4 | //
 5 | //  Created by Stefan Dragos Nitu on 22/08/2025.
 6 | //
 7 | 
 8 | import SwiftUI
 9 | 
10 | struct ContentView: View {
11 |     var body: some View {
12 |         VStack {
13 |             Image(systemName: "globe")
14 |                 .imageScale(.large)
15 |                 .foregroundStyle(.tint)
16 |             Text("Hello, world!")
17 |         }
18 |         .padding()
19 |     }
20 | }
21 | 
22 | #Preview {
23 |     ContentView()
24 | }
25 | 
```

--------------------------------------------------------------------------------
/src/shared/index.ts:
--------------------------------------------------------------------------------

```typescript
1 | // Domain Value Objects
2 | export { Platform } from './domain/Platform.js';
3 | export { DeviceId } from './domain/DeviceId.js';
4 | export { ProjectPath } from './domain/ProjectPath.js';
5 | export { AppPath } from './domain/AppPath.js';
6 | 
7 | // Infrastructure
8 | export { ShellCommandExecutorAdapter } from './infrastructure/ShellCommandExecutorAdapter.js';
9 | export { ConfigProviderAdapter } from './infrastructure/ConfigProviderAdapter.js';
```

--------------------------------------------------------------------------------
/src/application/ports/OutputParserPorts.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { BuildIssue } from '../../features/build/domain/BuildIssue.js';
 2 | 
 3 | /**
 4 |  * Port interface for parsing build output
 5 |  * This is an application-level abstraction
 6 |  */
 7 | 
 8 | export interface ParsedOutput {
 9 |   issues: BuildIssue[];
10 | }
11 | 
12 | export interface IOutputParser {
13 |   /**
14 |    * Parse build output and extract issues (errors/warnings)
15 |    * Only parses - no formatting or adding messages
16 |    */
17 |   parseBuildOutput(output: string): ParsedOutput;
18 | }
```

--------------------------------------------------------------------------------
/src/features/app-management/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // Controllers
 2 | export { InstallAppController } from './controllers/InstallAppController.js';
 3 | 
 4 | // Use Cases
 5 | export { InstallAppUseCase } from './use-cases/InstallAppUseCase.js';
 6 | 
 7 | // Factories
 8 | export { InstallAppControllerFactory } from './factories/InstallAppControllerFactory.js';
 9 | 
10 | // Domain
11 | export { InstallRequest } from './domain/InstallRequest.js';
12 | 
13 | // Infrastructure
14 | export { AppInstallerAdapter } from './infrastructure/AppInstallerAdapter.js';
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "ES2022",
 4 |     "module": "ES2022",
 5 |     "moduleResolution": "node",
 6 |     "lib": ["ES2022"],
 7 |     "outDir": "./dist",
 8 |     "rootDir": "./src",
 9 |     "strict": true,
10 |     "esModuleInterop": true,
11 |     "skipLibCheck": true,
12 |     "forceConsistentCasingInFileNames": true,
13 |     "resolveJsonModule": true,
14 |     "declaration": true,
15 |     "declarationMap": true,
16 |     "sourceMap": true
17 |   },
18 |   "include": ["src/**/*"],
19 |   "exclude": ["node_modules", "dist"]
20 | }
```

--------------------------------------------------------------------------------
/src/features/simulator/domain/BootRequest.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { DeviceId } from '../../../shared/domain/DeviceId.js';
 2 | 
 3 | /**
 4 |  * Value object representing a request to boot a simulator
 5 |  *
 6 |  * Encapsulates the device identifier (can be UUID or name)
 7 |  */
 8 | export class BootRequest {
 9 |   private constructor(
10 |     public readonly deviceId: string
11 |   ) {
12 |     Object.freeze(this);
13 |   }
14 | 
15 |   /**
16 |    * Create a boot request from a DeviceId
17 |    */
18 |   static create(deviceId: DeviceId): BootRequest {
19 |     return new BootRequest(deviceId.toString());
20 |   }
21 | }
```

--------------------------------------------------------------------------------
/src/presentation/interfaces/IDependencyChecker.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Represents a missing dependency
 3 |  */
 4 | export interface MissingDependency {
 5 |   readonly name: string;
 6 |   readonly installCommand?: string;
 7 | }
 8 | 
 9 | /**
10 |  * Checks for system dependencies required by MCP tools
11 |  */
12 | export interface IDependencyChecker {
13 |   /**
14 |    * Check if the specified dependencies are available
15 |    * @param dependencies List of dependency names to check
16 |    * @returns List of missing dependencies
17 |    */
18 |   check(dependencies: string[]): Promise<MissingDependency[]>;
19 | }
```

--------------------------------------------------------------------------------
/.claude/settings.local.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "permissions": {
 3 |     "allow": [
 4 |       "Bash(npm test:*)",
 5 |       "WebSearch",
 6 |       "Bash(npm run build:*)",
 7 |       "Bash(npm run test:coverage:*)",
 8 |       "WebFetch(domain:gist.github.com)",
 9 |       "WebFetch(domain:github.com)",
10 |       "Bash(npm run test:e2e:*)",
11 |       "Bash(npm run:*)",
12 |       "Bash(npx jest:*)",
13 |       "Bash(timeout 60 npx jest:*)",
14 |       "Bash(grep:*)",
15 |       "WebFetch(domain:docs.anthropic.com)",
16 |       "Bash(xcodebuild:*)"
17 |     ],
18 |     "deny": [],
19 |     "ask": []
20 |   }
21 | }
```

--------------------------------------------------------------------------------
/src/application/ports/CommandPorts.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Port interfaces for command execution
 3 |  * 
 4 |  * These are general infrastructure ports that can be used
 5 |  * by any use case that needs to execute external commands.
 6 |  */
 7 | 
 8 | export interface ExecutionResult {
 9 |   stdout: string;
10 |   stderr: string;
11 |   exitCode: number;
12 | }
13 | 
14 | export interface ExecutionOptions {
15 |   maxBuffer?: number;
16 |   timeout?: number;
17 |   shell?: string;
18 | }
19 | 
20 | export interface ICommandExecutor {
21 |   execute(
22 |     command: string,
23 |     options?: ExecutionOptions
24 |   ): Promise<ExecutionResult>;
25 | }
```

--------------------------------------------------------------------------------
/src/features/simulator/domain/ShutdownRequest.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { DeviceId } from '../../../shared/domain/DeviceId.js';
 2 | 
 3 | /**
 4 |  * Value object representing a request to shutdown a simulator
 5 |  *
 6 |  * Encapsulates the device identifier (can be UUID or name)
 7 |  */
 8 | export class ShutdownRequest {
 9 |   private constructor(
10 |     public readonly deviceId: string
11 |   ) {
12 |     Object.freeze(this);
13 |   }
14 | 
15 |   /**
16 |    * Create a shutdown request from a DeviceId
17 |    */
18 |   static create(deviceId: DeviceId): ShutdownRequest {
19 |     return new ShutdownRequest(deviceId.toString());
20 |   }
21 | }
```

--------------------------------------------------------------------------------
/src/application/ports/MappingPorts.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Port interfaces for mapping between domain and infrastructure concepts
 3 |  */
 4 | 
 5 | import { BuildDestination } from '../../features/build/domain/BuildDestination.js';
 6 | 
 7 | /**
 8 |  * Maps BuildDestination to xcodebuild-specific options
 9 |  */
10 | export interface IBuildDestinationMapper {
11 |   /**
12 |    * Map a domain BuildDestination to xcodebuild destination string and settings
13 |    */
14 |   toXcodeBuildOptions(destination: BuildDestination): Promise<{
15 |     destination: string;
16 |     additionalSettings?: string[];
17 |   }>;
18 | }
```

--------------------------------------------------------------------------------
/src/presentation/formatters/strategies/OutputFormatterErrorStrategy.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { OutputFormatterError } from '../../../features/build/domain/BuildResult.js';
 2 | import { ErrorFormattingStrategy } from './ErrorFormattingStrategy.js';
 3 | 
 4 | /**
 5 |  * Formats output formatter errors (e.g., xcbeautify not installed)
 6 |  */
 7 | export class OutputFormatterErrorStrategy implements ErrorFormattingStrategy {
 8 |   canFormat(error: any): boolean {
 9 |     return error instanceof OutputFormatterError;
10 |   }
11 |   
12 |   format(error: OutputFormatterError): string {
13 |     return `xcbeautify failed: ${error.stderr}`;
14 |   }
15 | }
```

--------------------------------------------------------------------------------
/src/features/simulator/domain/ListSimulatorsRequest.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { Platform } from '../../../shared/domain/Platform.js';
 2 | import { SimulatorState } from './SimulatorState.js';
 3 | 
 4 | /**
 5 |  * Value object for list simulators request
 6 |  */
 7 | export class ListSimulatorsRequest {
 8 |   constructor(
 9 |     public readonly platform?: Platform,
10 |     public readonly state?: SimulatorState,
11 |     public readonly name?: string
12 |   ) {}
13 | 
14 |   static create(
15 |     platform?: Platform,
16 |     state?: SimulatorState,
17 |     name?: string
18 |   ): ListSimulatorsRequest {
19 |     return new ListSimulatorsRequest(platform, state, name);
20 |   }
21 | }
```

--------------------------------------------------------------------------------
/src/application/ports/BuildPorts.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Port interfaces specific to build operations
 3 |  * 
 4 |  * These ports define how the application layer
 5 |  * interacts with build-specific infrastructure.
 6 |  */
 7 | 
 8 | // Options for the build command builder (infrastructure concerns)
 9 | export interface BuildCommandOptions {
10 |   scheme: string;
11 |   configuration?: string;
12 |   destination: string;  // Already mapped destination string
13 |   additionalSettings?: string[];
14 |   derivedDataPath?: string;
15 | }
16 | 
17 | export interface IBuildCommand {
18 |   build(
19 |     projectPath: string,
20 |     isWorkspace: boolean,
21 |     options: BuildCommandOptions
22 |   ): string;
23 | }
```

--------------------------------------------------------------------------------
/XcodeProjectModifier/Package.swift:
--------------------------------------------------------------------------------

```swift
 1 | // swift-tools-version: 6.0
 2 | import PackageDescription
 3 | 
 4 | let package = Package(
 5 |     name: "XcodeProjectModifier",
 6 |     platforms: [.macOS(.v10_15)],
 7 |     dependencies: [
 8 |         .package(url: "https://github.com/tuist/XcodeProj.git", from: "8.0.0"),
 9 |         .package(url: "https://github.com/apple/swift-argument-parser", from: "1.0.0")
10 |     ],
11 |     targets: [
12 |         .executableTarget(
13 |             name: "XcodeProjectModifier",
14 |             dependencies: [
15 |                 "XcodeProj",
16 |                 .product(name: "ArgumentParser", package: "swift-argument-parser")
17 |             ]
18 |         )
19 |     ]
20 | )
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectWatchOS/TestProjectWatchOS/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "images" : [
 3 |     {
 4 |       "idiom" : "universal",
 5 |       "platform" : "ios",
 6 |       "size" : "1024x1024"
 7 |     },
 8 |     {
 9 |       "appearances" : [
10 |         {
11 |           "appearance" : "luminosity",
12 |           "value" : "dark"
13 |         }
14 |       ],
15 |       "idiom" : "universal",
16 |       "platform" : "ios",
17 |       "size" : "1024x1024"
18 |     },
19 |     {
20 |       "appearances" : [
21 |         {
22 |           "appearance" : "luminosity",
23 |           "value" : "tinted"
24 |         }
25 |       ],
26 |       "idiom" : "universal",
27 |       "platform" : "ios",
28 |       "size" : "1024x1024"
29 |     }
30 |   ],
31 |   "info" : {
32 |     "author" : "xcode",
33 |     "version" : 1
34 |   }
35 | }
36 | 
```

--------------------------------------------------------------------------------
/test_artifacts/TestSwiftPackageXCTest/Tests/TestSwiftPackageXCTestTests/TestSwiftPackageXCTestTests.swift:
--------------------------------------------------------------------------------

```swift
 1 | import XCTest
 2 | @testable import TestSwiftPackageXCTest
 3 | 
 4 | final class LegacyTests: XCTestCase {
 5 |     func testExample() throws {
 6 |         let spm = TestSwiftPackageXCTest()
 7 |         XCTAssertEqual(spm.text, "Hello, TestSwiftPackageXCTest!")
 8 |     }
 9 |     
10 |     func testFailingTest() async throws {
11 |         // This test is designed to fail for MCP testing
12 |         XCTFail("Test MCP failing test reporting")
13 |     }
14 |     
15 |     func testAnotherFailure() async throws {
16 |         // Another failing test to verify multiple failures are handled
17 |         XCTAssertEqual(42, 100, "Expected 42 to equal 100 but it doesn't")
18 |     }
19 | }
20 | 
```

--------------------------------------------------------------------------------
/src/application/ports/ConfigPorts.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Port interface for configuration access
 3 |  * This is an application-level abstraction
 4 |  */
 5 | 
 6 | export interface IConfigProvider {
 7 |   /**
 8 |    * Get the path for DerivedData
 9 |    * @param projectPath Optional project path to generate project-specific derived data path
10 |    */
11 |   getDerivedDataPath(projectPath?: string): string;
12 |   
13 |   /**
14 |    * Get timeout for build operations in milliseconds
15 |    */
16 |   getBuildTimeout(): number;
17 |   
18 |   /**
19 |    * Check if xcbeautify is enabled
20 |    */
21 |   isXcbeautifyEnabled(): boolean;
22 |   
23 |   /**
24 |    * Get any custom build settings
25 |    */
26 |   getCustomBuildSettings(): Record<string, string>;
27 | }
```

--------------------------------------------------------------------------------
/src/config.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { homedir } from 'os';
 2 | import path from 'path';
 3 | 
 4 | /**
 5 |  * Configuration for MCP Xcode Server
 6 |  */
 7 | export const config = {
 8 |   /**
 9 |    * Base path for MCP-Xcode DerivedData
10 |    * Uses Xcode's standard location but in MCP-Xcode subfolder
11 |    */
12 |   derivedDataBasePath: path.join(homedir(), 'Library', 'Developer', 'Xcode', 'DerivedData', 'MCP-Xcode'),
13 |   
14 |   /**
15 |    * Get DerivedData path for a specific project
16 |    */
17 |   getDerivedDataPath(projectPath: string): string {
18 |     const projectName = path.basename(projectPath, path.extname(projectPath));
19 |     return path.join(this.derivedDataBasePath, projectName);
20 |   }
21 | };
```

--------------------------------------------------------------------------------
/test_artifacts/TestSwiftPackageSwiftTesting/Sources/TestSwiftPackageSwiftTestingExecutable/main.swift:
--------------------------------------------------------------------------------

```swift
 1 | import Foundation
 2 | 
 3 | // Simple executable for testing
 4 | print("TestSwiftPackageSwiftTesting Executable Running")
 5 | print("Arguments: \(CommandLine.arguments)")
 6 | print("Current Date: \(Date())")
 7 | 
 8 | // Test argument handling
 9 | if CommandLine.arguments.count > 1 {
10 |     print("Received \(CommandLine.arguments.count - 1) arguments:")
11 |     for (index, arg) in CommandLine.arguments.dropFirst().enumerated() {
12 |         print("  Arg \(index + 1): \(arg)")
13 |     }
14 | }
15 | 
16 | // Test exit codes
17 | if CommandLine.arguments.contains("--fail") {
18 |     print("Error: Requested failure via --fail flag")
19 |     exit(1)
20 | }
21 | 
22 | print("Execution completed successfully")
23 | 
```

--------------------------------------------------------------------------------
/test_artifacts/TestSwiftPackageXCTest/Sources/TestSwiftPackageXCTestExecutable/main.swift:
--------------------------------------------------------------------------------

```swift
 1 | import Foundation
 2 | 
 3 | // Simple executable for testing
 4 | print("TestSwiftPackageXCTestExecutable Executable Running")
 5 | print("Arguments: \(CommandLine.arguments)")
 6 | print("Current Date: \(Date())")
 7 | 
 8 | // Test argument handling
 9 | if CommandLine.arguments.count > 1 {
10 |     print("Received \(CommandLine.arguments.count - 1) arguments:")
11 |     for (index, arg) in CommandLine.arguments.dropFirst().enumerated() {
12 |         print("  Arg \(index + 1): \(arg)")
13 |     }
14 | }
15 | 
16 | // Test exit codes
17 | if CommandLine.arguments.contains("--fail") {
18 |     print("Error: Requested failure via --fail flag")
19 |     exit(1)
20 | }
21 | 
22 | print("Execution completed successfully")
23 | 
```

--------------------------------------------------------------------------------
/src/features/app-management/infrastructure/AppInstallerAdapter.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { IAppInstaller } from '../../../application/ports/SimulatorPorts.js';
 2 | import { ICommandExecutor } from '../../../application/ports/CommandPorts.js';
 3 | 
 4 | /**
 5 |  * Installs apps on simulators using xcrun simctl
 6 |  */
 7 | export class AppInstallerAdapter implements IAppInstaller {
 8 |   constructor(private executor: ICommandExecutor) {}
 9 | 
10 |   async installApp(appPath: string, simulatorId: string): Promise<void> {
11 |     const result = await this.executor.execute(
12 |       `xcrun simctl install "${simulatorId}" "${appPath}"`
13 |     );
14 |     
15 |     if (result.exitCode !== 0) {
16 |       throw new Error(result.stderr || 'Failed to install app');
17 |     }
18 |   }
19 | }
```

--------------------------------------------------------------------------------
/src/presentation/formatters/strategies/DefaultErrorStrategy.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { ErrorFormattingStrategy } from './ErrorFormattingStrategy.js';
 2 | 
 3 | /**
 4 |  * Default strategy for plain error messages
 5 |  */
 6 | export class DefaultErrorStrategy implements ErrorFormattingStrategy {
 7 |   canFormat(_error: any): boolean {
 8 |     return true; // Always can format as fallback
 9 |   }
10 |   
11 |   format(error: any): string {
12 |     if (error && error.message) {
13 |       // Clean up common prefixes
14 |       let message = error.message;
15 |       message = message.replace(/^Error:\s*/i, '');
16 |       message = message.replace(/^Invalid arguments:\s*/i, '');
17 |       message = message.replace(/^Validation failed:\s*/i, '');
18 |       return message;
19 |     }
20 |     return 'An error occurred';
21 |   }
22 | }
```

--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Type definitions for Apple Simulator MCP Server
 3 |  */
 4 | 
 5 | // Re-export Platform from domain for backward compatibility
 6 | // TODO: Update all imports to use domain directly
 7 | export { Platform } from './shared/domain/Platform.js';
 8 | 
 9 | export interface SimulatorDevice {
10 |   udid: string;
11 |   name: string;
12 |   state: 'Booted' | 'Shutdown' | 'Creating' | 'Booting' | 'ShuttingDown';
13 |   deviceTypeIdentifier: string;
14 |   runtime: string;
15 |   isAvailable?: boolean;
16 | }
17 | 
18 | export interface TestResult {
19 |   success: boolean;
20 |   output: string;
21 |   errors?: string;
22 |   testCount?: number;
23 |   failureCount?: number;
24 | }
25 | 
26 | export interface Tool {
27 |   execute(args: any): Promise<any>;
28 |   getToolDefinition(): any;
29 | }
```

--------------------------------------------------------------------------------
/jest.e2e.config.cjs:
--------------------------------------------------------------------------------

```
 1 | /** @type {import('ts-jest').JestConfigWithTsJest} */
 2 | module.exports = {
 3 |   preset: 'ts-jest',
 4 |   testEnvironment: 'node',
 5 |   testTimeout: 120000,
 6 |   transform: {
 7 |     '^.+\\.ts$': ['ts-jest', {
 8 |       tsconfig: {
 9 |         module: 'commonjs',
10 |         esModuleInterop: true,
11 |         allowSyntheticDefaultImports: true,
12 |       },
13 |     }],
14 |   },
15 |   moduleNameMapper: {
16 |     '^(\\.{1,2}/.*)\\.js$': '$1',
17 |   },
18 |   testMatch: [
19 |     '**/*.e2e.test.ts'
20 |   ],
21 |   collectCoverageFrom: [
22 |     'src/**/*.ts',
23 |     '!src/**/*.d.ts',
24 |     '!src/**/*.test.ts',
25 |     '!src/**/tests/**/*',
26 |     '!src/__tests__/**/*',
27 |     '!src/index.ts',
28 |   ],
29 |   coverageDirectory: 'coverage',
30 |   coverageReporters: ['text', 'lcov', 'html'],
31 | };
```

--------------------------------------------------------------------------------
/src/infrastructure/repositories/DeviceRepository.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { ICommandExecutor } from '../../application/ports/CommandPorts.js';
 2 | 
 3 | export interface RawDevice {
 4 |   udid: string;
 5 |   name: string;
 6 |   state: string;
 7 |   isAvailable: boolean;
 8 |   deviceTypeIdentifier?: string;
 9 |   dataPath?: string;
10 |   dataPathSize?: number;
11 |   logPath?: string;
12 | }
13 | 
14 | export interface DeviceList {
15 |   [runtime: string]: RawDevice[];
16 | }
17 | 
18 | /**
19 |  * Repository for accessing simulator device information
20 |  */
21 | export class DeviceRepository {
22 |   constructor(private executor: ICommandExecutor) {}
23 | 
24 |   async getAllDevices(): Promise<DeviceList> {
25 |     const result = await this.executor.execute('xcrun simctl list devices --json');
26 |     const data = JSON.parse(result.stdout);
27 |     return data.devices as DeviceList;
28 |   }
29 | }
```

--------------------------------------------------------------------------------
/jest.config.cjs:
--------------------------------------------------------------------------------

```
 1 | /** @type {import('ts-jest').JestConfigWithTsJest} */
 2 | module.exports = {
 3 |   preset: 'ts-jest',
 4 |   testEnvironment: 'node',
 5 |   testTimeout: 10000,
 6 |   transform: {
 7 |     '^.+\\.ts$': ['ts-jest', {
 8 |       tsconfig: {
 9 |         module: 'commonjs',
10 |         esModuleInterop: true,
11 |         allowSyntheticDefaultImports: true,
12 |       },
13 |     }],
14 |   },
15 |   moduleNameMapper: {
16 |     '^(\\.{1,2}/.*)\\.js$': '$1',
17 |   },
18 |   testMatch: [
19 |     '**/*.unit.test.ts',
20 |     '**/*.integration.test.ts'
21 |   ],
22 |   collectCoverageFrom: [
23 |     'src/**/*.ts',
24 |     '!src/**/*.d.ts',
25 |     '!src/**/*.test.ts',
26 |     '!src/**/tests/**/*',
27 |     '!src/__tests__/**/*',
28 |     '!src/index.ts',
29 |   ],
30 |   coverageDirectory: 'coverage',
31 |   coverageReporters: ['text', 'lcov', 'html'],
32 | };
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectXCTest/TestProjectXCTest/TestProjectXCTestApp.swift:
--------------------------------------------------------------------------------

```swift
 1 | //
 2 | //  TestProjectXCTestApp.swift
 3 | //  TestProjectXCTest
 4 | //
 5 | //  Created by Stefan Dragos Nitu on 17/08/2025.
 6 | //
 7 | 
 8 | import SwiftUI
 9 | import SwiftData
10 | 
11 | @main
12 | struct TestProjectXCTestApp: App {
13 |     var sharedModelContainer: ModelContainer = {
14 |         let schema = Schema([
15 |             Item.self,
16 |         ])
17 |         let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
18 | 
19 |         do {
20 |             return try ModelContainer(for: schema, configurations: [modelConfiguration])
21 |         } catch {
22 |             fatalError("Could not create ModelContainer: \(error)")
23 |         }
24 |     }()
25 | 
26 |     var body: some Scene {
27 |         WindowGroup {
28 |             ContentView()
29 |         }
30 |         .modelContainer(sharedModelContainer)
31 |     }
32 | }
33 | 
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectSwiftTesting/TestProjectSwiftTesting/TestProjectSwiftTestingApp.swift:
--------------------------------------------------------------------------------

```swift
 1 | //
 2 | //  TestProjectSwiftTestingApp.swift
 3 | //  TestProjectSwiftTesting
 4 | //
 5 | //  Created by Stefan Dragos Nitu on 17/08/2025.
 6 | //
 7 | 
 8 | import SwiftUI
 9 | import SwiftData
10 | 
11 | @main
12 | struct TestProjectSwiftTestingApp: App {
13 |     var sharedModelContainer: ModelContainer = {
14 |         let schema = Schema([
15 |             Item.self,
16 |         ])
17 |         let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
18 | 
19 |         do {
20 |             return try ModelContainer(for: schema, configurations: [modelConfiguration])
21 |         } catch {
22 |             fatalError("Could not create ModelContainer: \(error)")
23 |         }
24 |     }()
25 | 
26 |     var body: some Scene {
27 |         WindowGroup {
28 |             ContentView()
29 |         }
30 |         .modelContainer(sharedModelContainer)
31 |     }
32 | }
33 | 
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectSwiftTesting/TestProjectSwiftTestingTests/TestProjectSwiftTestingTests.swift:
--------------------------------------------------------------------------------

```swift
 1 | //
 2 | //  TestProjectSwiftTestingTests.swift
 3 | //  TestProjectSwiftTestingTests
 4 | //
 5 | //  Created by Stefan Dragos Nitu on 17/08/2025.
 6 | //
 7 | 
 8 | import Testing
 9 | 
10 | struct TestProjectSwiftTestingTests {
11 | 
12 |     @Test func example() async throws {
13 |         // Write your test here and use APIs like `#expect(...)` to check expected conditions.
14 |         #expect(true)
15 |     }
16 |     
17 |     @Test func testFailingTest() async throws {
18 |         // This test intentionally fails to test failure reporting
19 |         #expect(false, "This test is designed to fail for testing MCP failure reporting")
20 |     }
21 |     
22 |     @Test func testAnotherFailure() async throws {
23 |         // Another failing test to verify multiple failures are handled
24 |         let result = 42
25 |         #expect(result == 100, "Expected result to be 100 but got \(result)")
26 |     }
27 | 
28 | }
29 | 
```

--------------------------------------------------------------------------------
/src/shared/tests/mocks/promisifyExec.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import type { ExecOptions, ExecException } from 'child_process';
 2 | 
 3 | type ExecCallback = (error: ExecException | null, stdout: string, stderr: string) => void;
 4 | type ExecFunction = (command: string, options: ExecOptions, callback: ExecCallback) => void;
 5 | 
 6 | /**
 7 |  * Creates a promisified version of exec that matches Node's util.promisify behavior
 8 |  * Returns {stdout, stderr} on success, attaches them to error on failure
 9 |  */
10 | export function createPromisifiedExec(execFn: ExecFunction) {
11 |   return (cmd: string, options?: ExecOptions) => 
12 |     new Promise<{ stdout: string; stderr: string }>((resolve, reject) => {
13 |       execFn(cmd, options || {}, (error, stdout, stderr) => {
14 |         if (error) {
15 |           Object.assign(error, { stdout, stderr });
16 |           reject(error);
17 |         } else {
18 |           resolve({ stdout, stderr });
19 |         }
20 |       });
21 |     });
22 | }
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectXCTest/TestProjectXCTestUITests/TestProjectXCTestUITestsLaunchTests.swift:
--------------------------------------------------------------------------------

```swift
 1 | //
 2 | //  TestProjectXCTestUITestsLaunchTests.swift
 3 | //  TestProjectXCTestUITests
 4 | //
 5 | //  Created by Stefan Dragos Nitu on 17/08/2025.
 6 | //
 7 | 
 8 | import XCTest
 9 | 
10 | final class TestProjectXCTestUITestsLaunchTests: XCTestCase {
11 | 
12 |     override class var runsForEachTargetApplicationUIConfiguration: Bool {
13 |         true
14 |     }
15 | 
16 |     override func setUpWithError() throws {
17 |         continueAfterFailure = false
18 |     }
19 | 
20 |     @MainActor
21 |     func testLaunch() throws {
22 |         let app = XCUIApplication()
23 |         app.launch()
24 | 
25 |         // Insert steps here to perform after app launch but before taking a screenshot,
26 |         // such as logging into a test account or navigating somewhere in the app
27 | 
28 |         let attachment = XCTAttachment(screenshot: app.screenshot())
29 |         attachment.name = "Launch Screen"
30 |         attachment.lifetime = .keepAlways
31 |         add(attachment)
32 |     }
33 | }
34 | 
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectWatchOS/TestProjectWatchOSUITests/TestProjectWatchOSUITestsLaunchTests.swift:
--------------------------------------------------------------------------------

```swift
 1 | //
 2 | //  TestProjectWatchOSUITestsLaunchTests.swift
 3 | //  TestProjectWatchOSUITests
 4 | //
 5 | //  Created by Stefan Dragos Nitu on 22/08/2025.
 6 | //
 7 | 
 8 | import XCTest
 9 | 
10 | final class TestProjectWatchOSUITestsLaunchTests: XCTestCase {
11 | 
12 |     override class var runsForEachTargetApplicationUIConfiguration: Bool {
13 |         true
14 |     }
15 | 
16 |     override func setUpWithError() throws {
17 |         continueAfterFailure = false
18 |     }
19 | 
20 |     @MainActor
21 |     func testLaunch() throws {
22 |         let app = XCUIApplication()
23 |         app.launch()
24 | 
25 |         // Insert steps here to perform after app launch but before taking a screenshot,
26 |         // such as logging into a test account or navigating somewhere in the app
27 | 
28 |         let attachment = XCTAttachment(screenshot: app.screenshot())
29 |         attachment.name = "Launch Screen"
30 |         attachment.lifetime = .keepAlways
31 |         add(attachment)
32 |     }
33 | }
34 | 
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectSwiftTesting/TestProjectSwiftTestingUITests/TestProjectSwiftTestingUITestsLaunchTests.swift:
--------------------------------------------------------------------------------

```swift
 1 | //
 2 | //  TestProjectSwiftTestingUITestsLaunchTests.swift
 3 | //  TestProjectSwiftTestingUITests
 4 | //
 5 | //  Created by Stefan Dragos Nitu on 17/08/2025.
 6 | //
 7 | 
 8 | import XCTest
 9 | 
10 | final class TestProjectSwiftTestingUITestsLaunchTests: XCTestCase {
11 | 
12 |     override class var runsForEachTargetApplicationUIConfiguration: Bool {
13 |         true
14 |     }
15 | 
16 |     override func setUpWithError() throws {
17 |         continueAfterFailure = false
18 |     }
19 | 
20 |     @MainActor
21 |     func testLaunch() throws {
22 |         let app = XCUIApplication()
23 |         app.launch()
24 | 
25 |         // Insert steps here to perform after app launch but before taking a screenshot,
26 |         // such as logging into a test account or navigating somewhere in the app
27 | 
28 |         let attachment = XCTAttachment(screenshot: app.screenshot())
29 |         attachment.name = "Launch Screen"
30 |         attachment.lifetime = .keepAlways
31 |         add(attachment)
32 |     }
33 | }
34 | 
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectWatchOS/TestProjectWatchOS Watch AppUITests/TestProjectWatchOS_Watch_AppUITestsLaunchTests.swift:
--------------------------------------------------------------------------------

```swift
 1 | //
 2 | //  TestProjectWatchOS_Watch_AppUITestsLaunchTests.swift
 3 | //  TestProjectWatchOS Watch AppUITests
 4 | //
 5 | //  Created by Stefan Dragos Nitu on 22/08/2025.
 6 | //
 7 | 
 8 | import XCTest
 9 | 
10 | final class TestProjectWatchOS_Watch_AppUITestsLaunchTests: XCTestCase {
11 | 
12 |     override class var runsForEachTargetApplicationUIConfiguration: Bool {
13 |         true
14 |     }
15 | 
16 |     override func setUpWithError() throws {
17 |         continueAfterFailure = false
18 |     }
19 | 
20 |     @MainActor
21 |     func testLaunch() throws {
22 |         let app = XCUIApplication()
23 |         app.launch()
24 | 
25 |         // Insert steps here to perform after app launch but before taking a screenshot,
26 |         // such as logging into a test account or navigating somewhere in the app
27 | 
28 |         let attachment = XCTAttachment(screenshot: app.screenshot())
29 |         attachment.name = "Launch Screen"
30 |         attachment.lifetime = .keepAlways
31 |         add(attachment)
32 |     }
33 | }
34 | 
```

--------------------------------------------------------------------------------
/src/features/simulator/factories/ListSimulatorsControllerFactory.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { ListSimulatorsController } from '../controllers/ListSimulatorsController.js';
 2 | import { ListSimulatorsUseCase } from '../use-cases/ListSimulatorsUseCase.js';
 3 | import { DeviceRepository } from '../../../infrastructure/repositories/DeviceRepository.js';
 4 | import { ShellCommandExecutorAdapter } from '../../../shared/infrastructure/ShellCommandExecutorAdapter.js';
 5 | import { exec } from 'child_process';
 6 | import { promisify } from 'util';
 7 | 
 8 | /**
 9 |  * Factory for creating ListSimulatorsController with all dependencies
10 |  */
11 | export class ListSimulatorsControllerFactory {
12 |   static create(): ListSimulatorsController {
13 |     const execAsync = promisify(exec);
14 |     const executor = new ShellCommandExecutorAdapter(execAsync);
15 |     const deviceRepository = new DeviceRepository(executor);
16 |     const useCase = new ListSimulatorsUseCase(deviceRepository);
17 | 
18 |     return new ListSimulatorsController(useCase);
19 |   }
20 | }
```

--------------------------------------------------------------------------------
/src/domain/services/PlatformDetector.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { Platform } from '../../shared/domain/Platform.js';
 2 | import { BuildDestination } from '../../features/build/domain/BuildDestination.js';
 3 | 
 4 | /**
 5 |  * Domain Service: Detects platform from build destination
 6 |  * 
 7 |  * Pure domain logic - no external dependencies
 8 |  * Used to determine which platform a build destination targets
 9 |  */
10 | export class PlatformDetector {
11 |   /**
12 |    * Extract platform from a build destination
13 |    */
14 |   static fromDestination(destination: BuildDestination): Platform {
15 |     if (destination.startsWith('iOS')) return Platform.iOS;
16 |     if (destination.startsWith('macOS')) return Platform.macOS;
17 |     if (destination.startsWith('tvOS')) return Platform.tvOS;
18 |     if (destination.startsWith('watchOS')) return Platform.watchOS;
19 |     if (destination.startsWith('visionOS')) return Platform.visionOS;
20 |     
21 |     // Default to iOS if unknown (shouldn't happen with proper validation)
22 |     return Platform.iOS;
23 |   }
24 | }
```

--------------------------------------------------------------------------------
/src/presentation/interfaces/MCPController.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { MCPResponse } from './MCPResponse.js';
 2 | 
 3 | /**
 4 |  * Interface for MCP Tool Controllers
 5 |  *
 6 |  * All MCP controllers must implement this interface to ensure
 7 |  * consistent tool definition and execution patterns
 8 |  */
 9 | export interface MCPController {
10 |   /** MCP tool name (e.g., 'build_xcode', 'install_app') */
11 |   readonly name: string;
12 |   
13 |   /** Human-readable description of what the tool does */
14 |   readonly description: string;
15 |   
16 |   /** JSON Schema for input validation */
17 |   readonly inputSchema: object;
18 |   
19 |   /**
20 |    * Get the complete MCP tool definition
21 |    * Used by the MCP server to register the tool
22 |    */
23 |   getToolDefinition(): {
24 |     name: string;
25 |     description: string;
26 |     inputSchema: object;
27 |   };
28 |   
29 |   /**
30 |    * Execute the tool with given arguments
31 |    * @param args - Unknown input that will be validated
32 |    * @returns MCP-formatted response with content array
33 |    */
34 |   execute(args: unknown): Promise<MCPResponse>;
35 | }
```

--------------------------------------------------------------------------------
/src/utils/projects/XcodeErrors.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Error types for Xcode operations
 3 |  */
 4 | export enum XcodeErrorType {
 5 |   ProjectNotFound = 'PROJECT_NOT_FOUND',
 6 |   InvalidProjectType = 'INVALID_PROJECT_TYPE',
 7 |   UnknownError = 'UNKNOWN_ERROR'
 8 | }
 9 | 
10 | /**
11 |  * Custom error class for Xcode operations
12 |  */
13 | export class XcodeError extends Error {
14 |   constructor(
15 |     public readonly type: XcodeErrorType,
16 |     public readonly path: string,
17 |     message?: string
18 |   ) {
19 |     super(message || XcodeError.getDefaultMessage(type, path));
20 |     this.name = 'XcodeError';
21 |   }
22 | 
23 |   private static getDefaultMessage(type: XcodeErrorType, path: string): string {
24 |     switch (type) {
25 |       case XcodeErrorType.ProjectNotFound:
26 |         return `No Xcode project or Swift package found at: ${path}`;
27 |       case XcodeErrorType.InvalidProjectType:
28 |         return `Invalid project type at: ${path}`;
29 |       case XcodeErrorType.UnknownError:
30 |         return `Unknown error opening project at: ${path}`;
31 |     }
32 |   }
33 | }
```

--------------------------------------------------------------------------------
/src/utils/devices/SimulatorReset.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { execAsync } from '../../utils.js';
 2 | import { createModuleLogger } from '../../logger.js';
 3 | 
 4 | const logger = createModuleLogger('SimulatorReset');
 5 | 
 6 | /**
 7 |  * Utility class for resetting simulator state
 8 |  * Single responsibility: Reset a simulator (erase all content and settings)
 9 |  */
10 | export class SimulatorReset {
11 |   /**
12 |    * Reset a simulator by erasing all content and settings
13 |    * Equivalent to "Device > Erase All Content and Settings" in Simulator app
14 |    */
15 |   async reset(deviceId: string): Promise<void> {
16 |     try {
17 |       logger.info({ deviceId }, 'Resetting simulator - erasing all content and settings');
18 |       await execAsync(`xcrun simctl erase "${deviceId}"`);
19 |       logger.debug({ deviceId }, 'Successfully reset simulator');
20 |     } catch (error: any) {
21 |       logger.error({ error: error.message, deviceId }, 'Failed to reset simulator');
22 |       throw new Error(`Failed to reset simulator: ${error.message}`);
23 |     }
24 |   }
25 | }
```

--------------------------------------------------------------------------------
/test_artifacts/TestSwiftPackageSwiftTesting/Tests/TestSwiftPackageSwiftTestingTests/TestSwiftPackageSwiftTestingTests.swift:
--------------------------------------------------------------------------------

```swift
 1 | import Testing
 2 | @testable import TestSwiftPackageSwiftTesting
 3 | 
 4 | struct ModernTests {
 5 |     @Test func testExample() async throws {
 6 |         // Simple test to verify the module can be imported and tested
 7 |         let spm = TestSwiftPackageSwiftTesting()
 8 |         #expect(spm.text == "Hello, TestSwiftPackageSwiftTesting!")
 9 |     }
10 | 
11 |     @Test func testFailingTest() async throws {
12 |         // This test is designed to fail for MCP testing
13 |         #expect(1 == 2, """
14 |             Test MCP failing test reporting.
15 |             This is a multi-line message to test
16 |             how Swift Testing handles longer error descriptions.
17 |             Line 4 of the message.
18 |             Line 5 with special characters: @#$%^&*()
19 |             """)
20 |     }
21 |     
22 |     @Test func testAnotherFailure() async throws {
23 |         // Another failing test to verify multiple failures are handled
24 |         let result = 42
25 |         #expect(result == 100, "Expected result to be 100 but got \(result)")
26 |     }
27 | }
28 | 
```

--------------------------------------------------------------------------------
/src/presentation/formatters/ErrorFormatter.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { ErrorFormattingStrategy } from './strategies/ErrorFormattingStrategy.js';
 2 | import { BuildIssuesStrategy } from './strategies/BuildIssuesStrategy.js';
 3 | import { OutputFormatterErrorStrategy } from './strategies/OutputFormatterErrorStrategy.js';
 4 | import { DefaultErrorStrategy } from './strategies/DefaultErrorStrategy.js';
 5 | 
 6 | /**
 7 |  * Main error formatter that uses strategies
 8 |  */
 9 | export class ErrorFormatter {
10 |   private static strategies: ErrorFormattingStrategy[] = [
11 |     new BuildIssuesStrategy(),
12 |     new OutputFormatterErrorStrategy(),
13 |     new DefaultErrorStrategy() // Must be last - catches all other errors
14 |   ];
15 |   
16 |   /**
17 |    * Format any error into a user-friendly message
18 |    */
19 |   static format(error: Error | any): string {
20 |     for (const strategy of this.strategies) {
21 |       if (strategy.canFormat(error)) {
22 |         return strategy.format(error);
23 |       }
24 |     }
25 |     
26 |     // Shouldn't reach here due to DefaultErrorStrategy
27 |     return 'Unknown error';
28 |   }
29 | }
```

--------------------------------------------------------------------------------
/src/features/app-management/domain/InstallRequest.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { AppPath } from '../../../shared/domain/AppPath.js';
 2 | import { DeviceId } from '../../../shared/domain/DeviceId.js';
 3 | 
 4 | /**
 5 |  * Domain Value Object: Represents an app installation request
 6 |  *
 7 |  * Contains all the data needed to install an app:
 8 |  * - What: appPath (the .app bundle to install)
 9 |  * - Where: simulatorId (optional - uses booted simulator if not specified)
10 |  */
11 | export class InstallRequest {
12 |   private constructor(
13 |     public readonly appPath: AppPath,
14 |     public readonly simulatorId?: DeviceId
15 |   ) {}
16 | 
17 |   /**
18 |    * Create an InstallRequest from raw inputs
19 |    * Validates the inputs and creates an immutable request object
20 |    */
21 |   static create(
22 |     appPath: unknown,
23 |     simulatorId?: unknown
24 |   ): InstallRequest {
25 |     // Validate app path using AppPath value object
26 |     const validatedAppPath = AppPath.create(appPath);
27 | 
28 |     // Validate simulator ID if provided
29 |     const validatedDeviceId = DeviceId.createOptional(simulatorId);
30 | 
31 |     return new InstallRequest(validatedAppPath, validatedDeviceId);
32 |   }
33 | }
```

--------------------------------------------------------------------------------
/src/utils/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // Export device management
 2 | export { Devices, devices } from './devices/Devices.js';
 3 | export { SimulatorDevice } from './devices/SimulatorDevice.js';
 4 | 
 5 | // Export individual simulator components for testing
 6 | export { SimulatorBoot } from './devices/SimulatorBoot.js';
 7 | export { SimulatorReset } from './devices/SimulatorReset.js';
 8 | export { SimulatorApps } from './devices/SimulatorApps.js';
 9 | export { SimulatorUI } from './devices/SimulatorUI.js';
10 | export { SimulatorInfo } from './devices/SimulatorInfo.js';
11 | 
12 | // Export Xcode project management
13 | export { Xcode, xcode } from './projects/Xcode.js';
14 | export { XcodeProject } from './projects/XcodeProject.js';
15 | export { SwiftPackage } from './projects/SwiftPackage.js';
16 | 
17 | // Export Xcode components for testing
18 | export { XcodeBuild } from './projects/XcodeBuild.js';
19 | export { XcodeArchive } from './projects/XcodeArchive.js';
20 | export { XcodeInfo } from './projects/XcodeInfo.js';
21 | export { SwiftBuild } from './projects/SwiftBuild.js';
22 | export { SwiftPackageInfo } from './projects/SwiftPackageInfo.js';
```

--------------------------------------------------------------------------------
/src/domain/errors/DomainError.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Base domain error classes that ensure consistent error messages
 3 |  * Each domain object can extend these for type-safe, consistent errors
 4 |  */
 5 | 
 6 | export abstract class DomainError extends Error {
 7 |   constructor(message: string) {
 8 |     super(message);
 9 |     this.name = this.constructor.name;
10 |   }
11 | }
12 | 
13 | // Common validation error patterns with consistent messages
14 | export abstract class DomainEmptyError extends DomainError {
15 |   constructor(fieldDisplayName: string) {
16 |     super(`${fieldDisplayName} cannot be empty`);
17 |   }
18 | }
19 | 
20 | export abstract class DomainRequiredError extends DomainError {
21 |   constructor(fieldDisplayName: string) {
22 |     super(`${fieldDisplayName} is required`);
23 |   }
24 | }
25 | 
26 | export abstract class DomainInvalidTypeError extends DomainError {
27 |   constructor(fieldDisplayName: string, expectedType: string) {
28 |     super(`${fieldDisplayName} must be a ${expectedType}`);
29 |   }
30 | }
31 | 
32 | export abstract class DomainInvalidFormatError extends DomainError {
33 |   // This one varies by context, so just pass the message
34 |   constructor(message: string) {
35 |     super(message);
36 |   }
37 | }
```

--------------------------------------------------------------------------------
/test_artifacts/TestSwiftPackageXCTest/Package.swift:
--------------------------------------------------------------------------------

```swift
 1 | // swift-tools-version: 6.0
 2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
 3 | 
 4 | import PackageDescription
 5 | 
 6 | let package = Package(
 7 |     name: "TestSwiftPackageXCTest",
 8 |     products: [
 9 |         // Products define the executables and libraries a package produces, making them visible to other packages.
10 |         .library(
11 |             name: "TestSwiftPackageXCTest",
12 |             targets: ["TestSwiftPackageXCTest"]),
13 |         .executable(
14 |             name: "TestSwiftPackageXCTestExecutable",
15 |             targets: ["TestSwiftPackageXCTestExecutable"])
16 |     ],
17 |     targets: [
18 |         // Targets are the basic building blocks of a package, defining a module or a test suite.
19 |         // Targets can depend on other targets in this package and products from dependencies.
20 |         .target(
21 |             name: "TestSwiftPackageXCTest"),
22 |         .executableTarget(
23 |             name: "TestSwiftPackageXCTestExecutable"),
24 |         .testTarget(
25 |             name: "TestSwiftPackageXCTestTests",
26 |             dependencies: ["TestSwiftPackageXCTest"]
27 |         ),
28 |     ]
29 | )
30 | 
```

--------------------------------------------------------------------------------
/src/features/simulator/domain/ListSimulatorsResult.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { Platform } from '../../../shared/domain/Platform.js';
 2 | import { SimulatorState } from './SimulatorState.js';
 3 | 
 4 | export interface SimulatorInfo {
 5 |   udid: string;
 6 |   name: string;
 7 |   state: SimulatorState;
 8 |   platform: string;
 9 |   runtime: string;
10 | }
11 | 
12 | // Base class for all list simulators errors
13 | export abstract class ListSimulatorsError extends Error {}
14 | 
15 | // Specific error types
16 | export class SimulatorListParseError extends ListSimulatorsError {
17 |   constructor() {
18 |     super('Failed to parse simulator list: not valid JSON');
19 |     this.name = 'SimulatorListParseError';
20 |   }
21 | }
22 | 
23 | /**
24 |  * Result of listing simulators operation
25 |  */
26 | export class ListSimulatorsResult {
27 |   private constructor(
28 |     public readonly simulators: SimulatorInfo[],
29 |     public readonly error?: Error
30 |   ) {}
31 | 
32 |   static success(simulators: SimulatorInfo[]): ListSimulatorsResult {
33 |     return new ListSimulatorsResult(simulators);
34 |   }
35 | 
36 |   static failed(error: Error): ListSimulatorsResult {
37 |     return new ListSimulatorsResult([], error);
38 |   }
39 | 
40 |   get isSuccess(): boolean {
41 |     return !this.error;
42 |   }
43 | 
44 |   get count(): number {
45 |     return this.simulators.length;
46 |   }
47 | }
```

--------------------------------------------------------------------------------
/test_artifacts/TestSwiftPackageSwiftTesting/Package.swift:
--------------------------------------------------------------------------------

```swift
 1 | // swift-tools-version: 6.0
 2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
 3 | 
 4 | import PackageDescription
 5 | 
 6 | let package = Package(
 7 |     name: "TestSwiftPackageSwiftTesting",
 8 |     products: [
 9 |         // Products define the executables and libraries a package produces, making them visible to other packages.
10 |         .library(
11 |             name: "TestSwiftPackageSwiftTesting",
12 |             targets: ["TestSwiftPackageSwiftTesting"]),
13 |         .executable(
14 |             name: "TestSwiftPackageSwiftTestingExecutable",
15 |             targets: ["TestSwiftPackageSwiftTestingExecutable"])
16 |     ],
17 |     targets: [
18 |         // Targets are the basic building blocks of a package, defining a module or a test suite.
19 |         // Targets can depend on other targets in this package and products from dependencies.
20 |         .target(
21 |             name: "TestSwiftPackageSwiftTesting"),
22 |         .executableTarget(
23 |             name: "TestSwiftPackageSwiftTestingExecutable"),
24 |         .testTarget(
25 |             name: "TestSwiftPackageSwiftTestingTests",
26 |             dependencies: ["TestSwiftPackageSwiftTesting"]
27 |         ),
28 |     ]
29 | )
30 | 
```

--------------------------------------------------------------------------------
/src/features/simulator/infrastructure/SimulatorControlAdapter.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { ISimulatorControl } from '../../../application/ports/SimulatorPorts.js';
 2 | import { ICommandExecutor } from '../../../application/ports/CommandPorts.js';
 3 | 
 4 | /**
 5 |  * Controls simulator lifecycle using xcrun simctl
 6 |  */
 7 | export class SimulatorControlAdapter implements ISimulatorControl {
 8 |   constructor(private executor: ICommandExecutor) {}
 9 | 
10 |   async boot(simulatorId: string): Promise<void> {
11 |     const result = await this.executor.execute(`xcrun simctl boot "${simulatorId}"`);
12 |     
13 |     // Already booted is not an error
14 |     if (result.exitCode !== 0 && 
15 |         !result.stderr.includes('Unable to boot device in current state: Booted')) {
16 |       // Throw raw error - presentation layer will format it
17 |       throw new Error(result.stderr);
18 |     }
19 |   }
20 | 
21 |   async shutdown(simulatorId: string): Promise<void> {
22 |     const result = await this.executor.execute(`xcrun simctl shutdown "${simulatorId}"`);
23 |     
24 |     // Already shutdown is not an error
25 |     if (result.exitCode !== 0 && 
26 |         !result.stderr.includes('Unable to shutdown device in current state: Shutdown')) {
27 |       // Throw raw error - presentation layer will format it
28 |       throw new Error(result.stderr);
29 |     }
30 |   }
31 | }
```

--------------------------------------------------------------------------------
/src/application/ports/SimulatorPorts.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { SimulatorState } from '../../features/simulator/domain/SimulatorState.js';
 2 | 
 3 | /**
 4 |  * Port interfaces for simulator operations
 5 |  * 
 6 |  * Focused, single-responsibility interfaces following ISP
 7 |  */
 8 | 
 9 | export interface ISimulatorLocator {
10 |   /**
11 |    * Find a simulator by ID or name
12 |    * Returns null if not found
13 |    */
14 |   findSimulator(idOrName: string): Promise<SimulatorInfo | null>;
15 |   
16 |   /**
17 |    * Find first booted simulator
18 |    * Returns null if none are booted
19 |    * If multiple are booted, returns one (implementation-defined which)
20 |    */
21 |   findBootedSimulator(): Promise<SimulatorInfo | null>;
22 | }
23 | 
24 | export interface ISimulatorControl {
25 |   /**
26 |    * Boot a simulator
27 |    */
28 |   boot(simulatorId: string): Promise<void>;
29 |   
30 |   /**
31 |    * Shutdown a simulator
32 |    */
33 |   shutdown(simulatorId: string): Promise<void>;
34 | }
35 | 
36 | export interface IAppInstaller {
37 |   /**
38 |    * Install an app bundle on a specific simulator
39 |    * Throws if installation fails
40 |    */
41 |   installApp(appPath: string, simulatorId: string): Promise<void>;
42 | }
43 | 
44 | /**
45 |  * Simulator information snapshot
46 |  * Includes current state at time of query (not cached)
47 |  */
48 | export interface SimulatorInfo {
49 |   readonly id: string;
50 |   readonly name: string;
51 |   readonly state: SimulatorState;
52 |   readonly platform: string;
53 |   readonly runtime: string;
54 | }
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectXCTest/TestProjectXCTestUITests/TestProjectXCTestUITests.swift:
--------------------------------------------------------------------------------

```swift
 1 | //
 2 | //  TestProjectXCTestUITests.swift
 3 | //  TestProjectXCTestUITests
 4 | //
 5 | //  Created by Stefan Dragos Nitu on 17/08/2025.
 6 | //
 7 | 
 8 | import XCTest
 9 | 
10 | final class TestProjectXCTestUITests: XCTestCase {
11 | 
12 |     override func setUpWithError() throws {
13 |         // Put setup code here. This method is called before the invocation of each test method in the class.
14 | 
15 |         // In UI tests it is usually best to stop immediately when a failure occurs.
16 |         continueAfterFailure = false
17 | 
18 |         // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
19 |     }
20 | 
21 |     override func tearDownWithError() throws {
22 |         // Put teardown code here. This method is called after the invocation of each test method in the class.
23 |     }
24 | 
25 |     @MainActor
26 |     func testExample() throws {
27 |         // UI tests must launch the application that they test.
28 |         let app = XCUIApplication()
29 |         app.launch()
30 | 
31 |         // Use XCTAssert and related functions to verify your tests produce the correct results.
32 |     }
33 | 
34 |     @MainActor
35 |     func testLaunchPerformance() throws {
36 |         // This measures how long it takes to launch your application.
37 |         measure(metrics: [XCTApplicationLaunchMetric()]) {
38 |             XCUIApplication().launch()
39 |         }
40 |     }
41 | }
42 | 
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectWatchOS/TestProjectWatchOSUITests/TestProjectWatchOSUITests.swift:
--------------------------------------------------------------------------------

```swift
 1 | //
 2 | //  TestProjectWatchOSUITests.swift
 3 | //  TestProjectWatchOSUITests
 4 | //
 5 | //  Created by Stefan Dragos Nitu on 22/08/2025.
 6 | //
 7 | 
 8 | import XCTest
 9 | 
10 | final class TestProjectWatchOSUITests: XCTestCase {
11 | 
12 |     override func setUpWithError() throws {
13 |         // Put setup code here. This method is called before the invocation of each test method in the class.
14 | 
15 |         // In UI tests it is usually best to stop immediately when a failure occurs.
16 |         continueAfterFailure = false
17 | 
18 |         // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
19 |     }
20 | 
21 |     override func tearDownWithError() throws {
22 |         // Put teardown code here. This method is called after the invocation of each test method in the class.
23 |     }
24 | 
25 |     @MainActor
26 |     func testExample() throws {
27 |         // UI tests must launch the application that they test.
28 |         let app = XCUIApplication()
29 |         app.launch()
30 | 
31 |         // Use XCTAssert and related functions to verify your tests produce the correct results.
32 |     }
33 | 
34 |     @MainActor
35 |     func testLaunchPerformance() throws {
36 |         // This measures how long it takes to launch your application.
37 |         measure(metrics: [XCTApplicationLaunchMetric()]) {
38 |             XCUIApplication().launch()
39 |         }
40 |     }
41 | }
42 | 
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectSwiftTesting/TestProjectSwiftTestingUITests/TestProjectSwiftTestingUITests.swift:
--------------------------------------------------------------------------------

```swift
 1 | //
 2 | //  TestProjectSwiftTestingUITests.swift
 3 | //  TestProjectSwiftTestingUITests
 4 | //
 5 | //  Created by Stefan Dragos Nitu on 17/08/2025.
 6 | //
 7 | 
 8 | import XCTest
 9 | 
10 | final class TestProjectSwiftTestingUITests: XCTestCase {
11 | 
12 |     override func setUpWithError() throws {
13 |         // Put setup code here. This method is called before the invocation of each test method in the class.
14 | 
15 |         // In UI tests it is usually best to stop immediately when a failure occurs.
16 |         continueAfterFailure = false
17 | 
18 |         // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
19 |     }
20 | 
21 |     override func tearDownWithError() throws {
22 |         // Put teardown code here. This method is called after the invocation of each test method in the class.
23 |     }
24 | 
25 |     @MainActor
26 |     func testExample() throws {
27 |         // UI tests must launch the application that they test.
28 |         let app = XCUIApplication()
29 |         app.launch()
30 | 
31 |         // Use XCTAssert and related functions to verify your tests produce the correct results.
32 |     }
33 | 
34 |     @MainActor
35 |     func testLaunchPerformance() throws {
36 |         // This measures how long it takes to launch your application.
37 |         measure(metrics: [XCTApplicationLaunchMetric()]) {
38 |             XCUIApplication().launch()
39 |         }
40 |     }
41 | }
42 | 
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectWatchOS/TestProjectWatchOS Watch AppUITests/TestProjectWatchOS_Watch_AppUITests.swift:
--------------------------------------------------------------------------------

```swift
 1 | //
 2 | //  TestProjectWatchOS_Watch_AppUITests.swift
 3 | //  TestProjectWatchOS Watch AppUITests
 4 | //
 5 | //  Created by Stefan Dragos Nitu on 22/08/2025.
 6 | //
 7 | 
 8 | import XCTest
 9 | 
10 | final class TestProjectWatchOS_Watch_AppUITests: XCTestCase {
11 | 
12 |     override func setUpWithError() throws {
13 |         // Put setup code here. This method is called before the invocation of each test method in the class.
14 | 
15 |         // In UI tests it is usually best to stop immediately when a failure occurs.
16 |         continueAfterFailure = false
17 | 
18 |         // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
19 |     }
20 | 
21 |     override func tearDownWithError() throws {
22 |         // Put teardown code here. This method is called after the invocation of each test method in the class.
23 |     }
24 | 
25 |     @MainActor
26 |     func testExample() throws {
27 |         // UI tests must launch the application that they test.
28 |         let app = XCUIApplication()
29 |         app.launch()
30 | 
31 |         // Use XCTAssert and related functions to verify your tests produce the correct results.
32 |     }
33 | 
34 |     @MainActor
35 |     func testLaunchPerformance() throws {
36 |         // This measures how long it takes to launch your application.
37 |         measure(metrics: [XCTApplicationLaunchMetric()]) {
38 |             XCUIApplication().launch()
39 |         }
40 |     }
41 | }
42 | 
```

--------------------------------------------------------------------------------
/src/shared/infrastructure/ShellCommandExecutorAdapter.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { ExecOptions } from 'child_process';
 2 | import { createModuleLogger } from '../../logger.js';
 3 | import { ICommandExecutor, ExecutionResult, ExecutionOptions } from '../../application/ports/CommandPorts.js';
 4 | 
 5 | const logger = createModuleLogger('ShellCommandExecutor');
 6 | 
 7 | /**
 8 |  * Executes shell commands via child process
 9 |  * Single Responsibility: Execute shell commands and return results
10 |  */
11 | export class ShellCommandExecutorAdapter implements ICommandExecutor {
12 |   constructor(
13 |     private readonly execAsync: (
14 |       command: string, 
15 |       options: ExecOptions
16 |     ) => Promise<{ stdout: string; stderr: string }>
17 |   ) {}
18 |   
19 |   /**
20 |    * Execute a command and return the result
21 |    */
22 |   async execute(command: string, options: ExecutionOptions = {}): Promise<ExecutionResult> {
23 |     const {
24 |       maxBuffer = 50 * 1024 * 1024, // 50MB default
25 |       timeout = 300000, // 5 minute default
26 |       shell = '/bin/bash'
27 |     } = options;
28 |     
29 |     logger.debug({ command }, 'Executing command');
30 |     
31 |     try {
32 |       const { stdout, stderr } = await this.execAsync(command, {
33 |         maxBuffer,
34 |         timeout,
35 |         shell
36 |       });
37 |       
38 |       return {
39 |         stdout,
40 |         stderr,
41 |         exitCode: 0
42 |       };
43 |     } catch (error: any) {
44 |       // Even on failure, return the output
45 |       return {
46 |         stdout: error.stdout || '',
47 |         stderr: error.stderr || '',
48 |         exitCode: error.code || 1
49 |       };
50 |     }
51 |   }
52 | }
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectSwiftTesting/TestProjectSwiftTesting/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "images" : [
 3 |     {
 4 |       "idiom" : "universal",
 5 |       "platform" : "ios",
 6 |       "size" : "1024x1024"
 7 |     },
 8 |     {
 9 |       "appearances" : [
10 |         {
11 |           "appearance" : "luminosity",
12 |           "value" : "dark"
13 |         }
14 |       ],
15 |       "idiom" : "universal",
16 |       "platform" : "ios",
17 |       "size" : "1024x1024"
18 |     },
19 |     {
20 |       "appearances" : [
21 |         {
22 |           "appearance" : "luminosity",
23 |           "value" : "tinted"
24 |         }
25 |       ],
26 |       "idiom" : "universal",
27 |       "platform" : "ios",
28 |       "size" : "1024x1024"
29 |     },
30 |     {
31 |       "idiom" : "mac",
32 |       "scale" : "1x",
33 |       "size" : "16x16"
34 |     },
35 |     {
36 |       "idiom" : "mac",
37 |       "scale" : "2x",
38 |       "size" : "16x16"
39 |     },
40 |     {
41 |       "idiom" : "mac",
42 |       "scale" : "1x",
43 |       "size" : "32x32"
44 |     },
45 |     {
46 |       "idiom" : "mac",
47 |       "scale" : "2x",
48 |       "size" : "32x32"
49 |     },
50 |     {
51 |       "idiom" : "mac",
52 |       "scale" : "1x",
53 |       "size" : "128x128"
54 |     },
55 |     {
56 |       "idiom" : "mac",
57 |       "scale" : "2x",
58 |       "size" : "128x128"
59 |     },
60 |     {
61 |       "idiom" : "mac",
62 |       "scale" : "1x",
63 |       "size" : "256x256"
64 |     },
65 |     {
66 |       "idiom" : "mac",
67 |       "scale" : "2x",
68 |       "size" : "256x256"
69 |     },
70 |     {
71 |       "idiom" : "mac",
72 |       "scale" : "1x",
73 |       "size" : "512x512"
74 |     },
75 |     {
76 |       "idiom" : "mac",
77 |       "scale" : "2x",
78 |       "size" : "512x512"
79 |     }
80 |   ],
81 |   "info" : {
82 |     "author" : "xcode",
83 |     "version" : 1
84 |   }
85 | }
86 | 
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectXCTest/TestProjectXCTest/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "images" : [
 3 |     {
 4 |       "idiom" : "universal",
 5 |       "platform" : "ios",
 6 |       "size" : "1024x1024"
 7 |     },
 8 |     {
 9 |       "appearances" : [
10 |         {
11 |           "appearance" : "luminosity",
12 |           "value" : "dark"
13 |         }
14 |       ],
15 |       "idiom" : "universal",
16 |       "platform" : "ios",
17 |       "size" : "1024x1024"
18 |     },
19 |     {
20 |       "appearances" : [
21 |         {
22 |           "appearance" : "luminosity",
23 |           "value" : "tinted"
24 |         }
25 |       ],
26 |       "idiom" : "universal",
27 |       "platform" : "ios",
28 |       "size" : "1024x1024"
29 |     },
30 |     {
31 |       "idiom" : "mac",
32 |       "scale" : "1x",
33 |       "size" : "16x16"
34 |     },
35 |     {
36 |       "idiom" : "mac",
37 |       "scale" : "2x",
38 |       "size" : "16x16"
39 |     },
40 |     {
41 |       "idiom" : "mac",
42 |       "scale" : "1x",
43 |       "size" : "32x32"
44 |     },
45 |     {
46 |       "idiom" : "mac",
47 |       "scale" : "2x",
48 |       "size" : "32x32"
49 |     },
50 |     {
51 |       "idiom" : "mac",
52 |       "scale" : "1x",
53 |       "size" : "128x128"
54 |     },
55 |     {
56 |       "idiom" : "mac",
57 |       "scale" : "2x",
58 |       "size" : "128x128"
59 |     },
60 |     {
61 |       "idiom" : "mac",
62 |       "scale" : "1x",
63 |       "size" : "256x256"
64 |     },
65 |     {
66 |       "idiom" : "mac",
67 |       "scale" : "2x",
68 |       "size" : "256x256"
69 |     },
70 |     {
71 |       "idiom" : "mac",
72 |       "scale" : "1x",
73 |       "size" : "512x512"
74 |     },
75 |     {
76 |       "idiom" : "mac",
77 |       "scale" : "2x",
78 |       "size" : "512x512"
79 |     }
80 |   ],
81 |   "info" : {
82 |     "author" : "xcode",
83 |     "version" : 1
84 |   }
85 | }
86 | 
```

--------------------------------------------------------------------------------
/src/presentation/formatters/strategies/BuildIssuesStrategy.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { BuildIssue } from '../../../features/build/domain/BuildIssue.js';
 2 | import { ErrorFormattingStrategy } from './ErrorFormattingStrategy.js';
 3 | 
 4 | /**
 5 |  * Formats build issues (errors and warnings)
 6 |  */
 7 | export class BuildIssuesStrategy implements ErrorFormattingStrategy {
 8 |   canFormat(error: any): boolean {
 9 |     return !!(error && error.issues && Array.isArray(error.issues) && 
10 |              error.issues.some((i: any) => i instanceof BuildIssue));
11 |   }
12 |   
13 |   format(error: any): string {
14 |     // Filter to only actual BuildIssue instances
15 |     const issues = (error.issues as any[]).filter(i => i instanceof BuildIssue);
16 |     const errors = issues.filter(i => i.type === 'error');
17 |     const warnings = issues.filter(i => i.type === 'warning');
18 |     
19 |     let message = '';
20 |     if (errors.length > 0) {
21 |       message += `❌ Errors (${errors.length}):\n`;
22 |       message += errors.slice(0, 5).map(e => `  • ${e.toString()}`).join('\n');
23 |       if (errors.length > 5) {
24 |         message += `\n  ... and ${errors.length - 5} more errors`;
25 |       }
26 |     }
27 |     
28 |     if (warnings.length > 0) {
29 |       if (message) message += '\n\n';
30 |       message += `⚠️ Warnings (${warnings.length}):\n`;
31 |       message += warnings.slice(0, 3).map(w => `  • ${w.toString()}`).join('\n');
32 |       if (warnings.length > 3) {
33 |         message += `\n  ... and ${warnings.length - 3} more warnings`;
34 |       }
35 |     }
36 |     
37 |     return message || error.message || 'Build failed';
38 |   }
39 | }
```

--------------------------------------------------------------------------------
/src/features/simulator/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // Controllers
 2 | export { BootSimulatorController } from './controllers/BootSimulatorController.js';
 3 | export { ShutdownSimulatorController } from './controllers/ShutdownSimulatorController.js';
 4 | export { ListSimulatorsController } from './controllers/ListSimulatorsController.js';
 5 | 
 6 | // Use Cases
 7 | export { BootSimulatorUseCase } from './use-cases/BootSimulatorUseCase.js';
 8 | export { ShutdownSimulatorUseCase } from './use-cases/ShutdownSimulatorUseCase.js';
 9 | export { ListSimulatorsUseCase } from './use-cases/ListSimulatorsUseCase.js';
10 | 
11 | // Factories
12 | export { BootSimulatorControllerFactory } from './factories/BootSimulatorControllerFactory.js';
13 | export { ShutdownSimulatorControllerFactory } from './factories/ShutdownSimulatorControllerFactory.js';
14 | export { ListSimulatorsControllerFactory } from './factories/ListSimulatorsControllerFactory.js';
15 | 
16 | // Domain
17 | export { BootRequest } from './domain/BootRequest.js';
18 | export { ShutdownRequest } from './domain/ShutdownRequest.js';
19 | export { ListSimulatorsRequest } from './domain/ListSimulatorsRequest.js';
20 | export { SimulatorState } from './domain/SimulatorState.js';
21 | export {
22 |   BootResult,
23 |   BootOutcome,
24 |   SimulatorNotFoundError,
25 |   BootCommandFailedError,
26 |   SimulatorBusyError
27 | } from './domain/BootResult.js';
28 | 
29 | // Infrastructure
30 | export { SimulatorControlAdapter } from './infrastructure/SimulatorControlAdapter.js';
31 | export { SimulatorLocatorAdapter } from './infrastructure/SimulatorLocatorAdapter.js';
```

--------------------------------------------------------------------------------
/src/presentation/decorators/DependencyCheckingDecorator.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { MCPController } from '../interfaces/MCPController.js';
 2 | import { MCPResponse } from '../interfaces/MCPResponse.js';
 3 | import { IDependencyChecker } from '../interfaces/IDependencyChecker.js';
 4 | 
 5 | /**
 6 |  * Decorator that checks dependencies before executing the controller
 7 |  *
 8 |  * Follows the Decorator pattern to add dependency checking behavior
 9 |  * without modifying the original controller
10 |  */
11 | export class DependencyCheckingDecorator implements MCPController {
12 |   // Delegate properties to decoratee
13 |   get name(): string { return this.decoratee.name; }
14 |   get description(): string { return this.decoratee.description; }
15 |   get inputSchema(): object { return this.decoratee.inputSchema; }
16 | 
17 |   constructor(
18 |     private readonly decoratee: MCPController,
19 |     private readonly requiredDependencies: string[],
20 |     private readonly dependencyChecker: IDependencyChecker
21 |   ) {}
22 | 
23 |   async execute(args: unknown): Promise<MCPResponse> {
24 |     // Check dependencies first
25 |     const missing = await this.dependencyChecker.check(this.requiredDependencies);
26 | 
27 |     if (missing.length > 0) {
28 |       // Dependencies missing - return error without executing
29 |       let text = '❌ Missing required dependencies:\n';
30 |       for (const dep of missing) {
31 |         text += `\n  • ${dep.name}`;
32 |         if (dep.installCommand) {
33 |           text += `: ${dep.installCommand}`;
34 |         }
35 |       }
36 | 
37 |       return {
38 |         content: [{ type: 'text', text }]
39 |       };
40 |     }
41 | 
42 |     // All dependencies available - delegate to actual controller
43 |     return this.decoratee.execute(args);
44 |   }
45 | 
46 |   getToolDefinition() {
47 |     // Delegate to decoratee
48 |     return this.decoratee.getToolDefinition();
49 |   }
50 | }
```

--------------------------------------------------------------------------------
/src/features/simulator/domain/ShutdownResult.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Domain entity representing the result of a shutdown operation
 3 |  */
 4 | 
 5 | // Error types specific to shutdown operations
 6 | export abstract class ShutdownError extends Error {}
 7 | 
 8 | export class SimulatorNotFoundError extends ShutdownError {
 9 |   constructor(public readonly deviceId: string) {
10 |     super(deviceId);
11 |     this.name = 'SimulatorNotFoundError';
12 |   }
13 | }
14 | 
15 | export class ShutdownCommandFailedError extends ShutdownError {
16 |   constructor(public readonly stderr: string) {
17 |     super(stderr);
18 |     this.name = 'ShutdownCommandFailedError';
19 |   }
20 | }
21 | 
22 | // Possible outcomes of a shutdown operation
23 | export enum ShutdownOutcome {
24 |   Shutdown = 'shutdown',
25 |   AlreadyShutdown = 'already_shutdown',
26 |   Failed = 'failed'
27 | }
28 | 
29 | // Diagnostics information about the shutdown operation
30 | interface ShutdownDiagnostics {
31 |   simulatorId?: string;
32 |   simulatorName?: string;
33 |   error?: Error;
34 | }
35 | 
36 | export class ShutdownResult {
37 |   private constructor(
38 |     public readonly outcome: ShutdownOutcome,
39 |     public readonly diagnostics: ShutdownDiagnostics
40 |   ) {}
41 |   
42 |   static shutdown(simulatorId: string, simulatorName: string): ShutdownResult {
43 |     return new ShutdownResult(
44 |       ShutdownOutcome.Shutdown,
45 |       { simulatorId, simulatorName }
46 |     );
47 |   }
48 |   
49 |   static alreadyShutdown(simulatorId: string, simulatorName: string): ShutdownResult {
50 |     return new ShutdownResult(
51 |       ShutdownOutcome.AlreadyShutdown,
52 |       { simulatorId, simulatorName }
53 |     );
54 |   }
55 |   
56 |   static failed(simulatorId: string | undefined, simulatorName: string | undefined, error: Error): ShutdownResult {
57 |     return new ShutdownResult(
58 |       ShutdownOutcome.Failed,
59 |       { simulatorId, simulatorName, error }
60 |     );
61 |   }
62 | }
```

--------------------------------------------------------------------------------
/src/infrastructure/services/DependencyChecker.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { IDependencyChecker, MissingDependency } from '../../presentation/interfaces/IDependencyChecker.js';
 2 | import { ICommandExecutor } from '../../application/ports/CommandPorts.js';
 3 | 
 4 | /**
 5 |  * Checks for system dependencies using shell commands
 6 |  */
 7 | export class DependencyChecker implements IDependencyChecker {
 8 |   private readonly dependencyMap: Record<string, { checkCommand: string; installCommand?: string }> = {
 9 |     'xcodebuild': {
10 |       checkCommand: 'which xcodebuild',
11 |       installCommand: 'Install Xcode from the App Store'
12 |     },
13 |     'xcrun': {
14 |       checkCommand: 'which xcrun',
15 |       installCommand: 'Install Xcode Command Line Tools: xcode-select --install'
16 |     },
17 |     'xcbeautify': {
18 |       checkCommand: 'which xcbeautify',
19 |       installCommand: 'brew install xcbeautify'
20 |     }
21 |   };
22 | 
23 |   constructor(
24 |     private readonly executor: ICommandExecutor
25 |   ) {}
26 | 
27 |   async check(dependencies: string[]): Promise<MissingDependency[]> {
28 |     const missing: MissingDependency[] = [];
29 | 
30 |     for (const dep of dependencies) {
31 |       const config = this.dependencyMap[dep];
32 |       if (!config) {
33 |         // Unknown dependency - just check with 'which'
34 |         const result = await this.executor.execute(`which ${dep}`, {
35 |           shell: '/bin/bash'
36 |         });
37 | 
38 |         if (result.exitCode !== 0) {
39 |           missing.push({ name: dep });
40 |         }
41 |         continue;
42 |       }
43 | 
44 |       // Check using configured command
45 |       const result = await this.executor.execute(config.checkCommand, {
46 |         shell: '/bin/bash'
47 |       });
48 | 
49 |       if (result.exitCode !== 0) {
50 |         missing.push({
51 |           name: dep,
52 |           installCommand: config.installCommand
53 |         });
54 |       }
55 |     }
56 | 
57 |     return missing;
58 |   }
59 | }
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectXCTest/TestProjectXCTest/ContentView.swift:
--------------------------------------------------------------------------------

```swift
 1 | //
 2 | //  ContentView.swift
 3 | //  TestProjectXCTest
 4 | //
 5 | //  Created by Stefan Dragos Nitu on 17/08/2025.
 6 | //
 7 | 
 8 | import SwiftUI
 9 | import SwiftData
10 | 
11 | struct ContentView: View {
12 |     @Environment(\.modelContext) private var modelContext
13 |     @Query private var items: [Item]
14 | 
15 |     var body: some View {
16 |         NavigationSplitView {
17 |             List {
18 |                 ForEach(items) { item in
19 |                     NavigationLink {
20 |                         Text("Item at \(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))")
21 |                     } label: {
22 |                         Text(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))
23 |                     }
24 |                 }
25 |                 .onDelete(perform: deleteItems)
26 |             }
27 | #if os(macOS)
28 |             .navigationSplitViewColumnWidth(min: 180, ideal: 200)
29 | #endif
30 |             .toolbar {
31 | #if os(iOS)
32 |                 ToolbarItem(placement: .navigationBarTrailing) {
33 |                     EditButton()
34 |                 }
35 | #endif
36 |                 ToolbarItem {
37 |                     Button(action: addItem) {
38 |                         Label("Add Item", systemImage: "plus")
39 |                     }
40 |                 }
41 |             }
42 |         } detail: {
43 |             Text("Select an item")
44 |         }
45 |     }
46 | 
47 |     private func addItem() {
48 |         withAnimation {
49 |             let newItem = Item(timestamp: Date())
50 |             modelContext.insert(newItem)
51 |         }
52 |     }
53 | 
54 |     private func deleteItems(offsets: IndexSet) {
55 |         withAnimation {
56 |             for index in offsets {
57 |                 modelContext.delete(items[index])
58 |             }
59 |         }
60 |     }
61 | }
62 | 
63 | #Preview {
64 |     ContentView()
65 |         .modelContainer(for: Item.self, inMemory: true)
66 | }
67 | 
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectSwiftTesting/TestProjectSwiftTesting/ContentView.swift:
--------------------------------------------------------------------------------

```swift
 1 | //
 2 | //  ContentView.swift
 3 | //  TestProjectSwiftTesting
 4 | //
 5 | //  Created by Stefan Dragos Nitu on 17/08/2025.
 6 | //
 7 | 
 8 | import SwiftUI
 9 | import SwiftData
10 | 
11 | struct ContentView: View {
12 |     @Environment(\.modelContext) private var modelContext
13 |     @Query private var items: [Item]
14 | 
15 |     var body: some View {
16 |         NavigationSplitView {
17 |             List {
18 |                 ForEach(items) { item in
19 |                     NavigationLink {
20 |                         Text("Item at \(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))")
21 |                     } label: {
22 |                         Text(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))
23 |                     }
24 |                 }
25 |                 .onDelete(perform: deleteItems)
26 |             }
27 | #if os(macOS)
28 |             .navigationSplitViewColumnWidth(min: 180, ideal: 200)
29 | #endif
30 |             .toolbar {
31 | #if os(iOS)
32 |                 ToolbarItem(placement: .navigationBarTrailing) {
33 |                     EditButton()
34 |                 }
35 | #endif
36 |                 ToolbarItem {
37 |                     Button(action: addItem) {
38 |                         Label("Add Item", systemImage: "plus")
39 |                     }
40 |                 }
41 |             }
42 |         } detail: {
43 |             Text("Select an item")
44 |         }
45 |     }
46 | 
47 |     private func addItem() {
48 |         withAnimation {
49 |             let newItem = Item(timestamp: Date())
50 |             modelContext.insert(newItem)
51 |         }
52 |     }
53 | 
54 |     private func deleteItems(offsets: IndexSet) {
55 |         withAnimation {
56 |             for index in offsets {
57 |                 modelContext.delete(items[index])
58 |             }
59 |         }
60 |     }
61 | }
62 | 
63 | #Preview {
64 |     ContentView()
65 |         .modelContainer(for: Item.self, inMemory: true)
66 | }
67 | 
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "mcp-xcode-server",
 3 |   "version": "0.6.0",
 4 |   "description": "MCP server for Xcode - build, test, run, and manage Apple platform projects (iOS, macOS, tvOS, watchOS, visionOS)",
 5 |   "main": "dist/index.js",
 6 |   "type": "module",
 7 |   "bin": {
 8 |     "mcp-xcode-server": "dist/cli.js"
 9 |   },
10 |   "scripts": {
11 |     "build": "tsc",
12 |     "start": "node dist/index.js",
13 |     "dev": "tsc && node dist/index.js",
14 |     "test": "jest && jest --config jest.e2e.config.cjs --runInBand",
15 |     "test:coverage": "jest --coverage",
16 |     "test:unit": "jest '*.unit.test.ts'",
17 |     "test:integration": "jest '*.integration.test.ts'",
18 |     "test:e2e": "jest --config jest.e2e.config.cjs --runInBand",
19 |     "postinstall": "cd XcodeProjectModifier && swift build -c release",
20 |     "build:modifier": "cd XcodeProjectModifier && swift build -c release"
21 |   },
22 |   "keywords": [
23 |     "mcp",
24 |     "apple",
25 |     "ios",
26 |     "macos",
27 |     "tvos",
28 |     "watchos",
29 |     "visionos",
30 |     "simulator",
31 |     "xcode",
32 |     "spm",
33 |     "swift"
34 |   ],
35 |   "author": "Stefan",
36 |   "license": "MIT",
37 |   "repository": {
38 |     "type": "git",
39 |     "url": "https://github.com/yourusername/mcp-xcode-server.git"
40 |   },
41 |   "bugs": {
42 |     "url": "https://github.com/yourusername/mcp-xcode-server/issues"
43 |   },
44 |   "homepage": "https://github.com/yourusername/mcp-xcode-server#readme",
45 |   "files": [
46 |     "dist/**/*",
47 |     "XcodeProjectModifier/**/*",
48 |     "README.md",
49 |     "LICENSE"
50 |   ],
51 |   "engines": {
52 |     "node": ">=18.0.0"
53 |   },
54 |   "dependencies": {
55 |     "@modelcontextprotocol/sdk": "^1.17.3",
56 |     "commander": "^12.0.0",
57 |     "fast-xml-parser": "^5.2.5",
58 |     "pino": "^9.9.0",
59 |     "pino-pretty": "^13.1.1"
60 |   },
61 |   "devDependencies": {
62 |     "@types/jest": "^30.0.0",
63 |     "@types/node": "^20.11.0",
64 |     "jest": "^30.1.1",
65 |     "regexp-tree": "^0.1.27",
66 |     "ts-jest": "^29.4.1",
67 |     "typescript": "^5.3.3"
68 |   }
69 | }
70 | 
```

--------------------------------------------------------------------------------
/src/shared/domain/Platform.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Domain Value Object: Platform enum
 3 |  * Represents the supported Apple platforms
 4 |  */
 5 | export enum Platform {
 6 |   iOS = 'iOS',
 7 |   macOS = 'macOS',
 8 |   tvOS = 'tvOS',
 9 |   watchOS = 'watchOS',
10 |   visionOS = 'visionOS'
11 | }
12 | 
13 | /**
14 |  * Platform validation and parsing utilities
15 |  */
16 | export namespace Platform {
17 |   /**
18 |    * Parse a string into a Platform enum value
19 |    * @throws Error if the string is not a valid platform
20 |    */
21 |   export function parse(value: unknown): Platform {
22 |     // Type check
23 |     if (typeof value !== 'string') {
24 |       throw new InvalidTypeError(value);
25 |     }
26 | 
27 |     // Check if valid platform - filter out namespace functions
28 |     const validPlatforms = Object.values(Platform).filter(v => typeof v === 'string') as string[];
29 |     if (!validPlatforms.includes(value)) {
30 |       throw new InvalidPlatformError(value, validPlatforms);
31 |     }
32 | 
33 |     return value as Platform;
34 |   }
35 | 
36 |   /**
37 |    * Parse a string into a Platform enum value or return undefined
38 |    */
39 |   export function parseOptional(value: unknown): Platform | undefined {
40 |     if (value === undefined || value === null) {
41 |       return undefined;
42 |     }
43 |     return parse(value);
44 |   }
45 | 
46 |   // Error classes
47 |   export class InvalidTypeError extends Error {
48 |     constructor(public readonly providedValue: unknown) {
49 |       const validValues = Object.values(Platform).filter(v => typeof v === 'string') as string[];
50 |       super(`Platform must be a string (one of: ${validValues.join(', ')}), got ${typeof providedValue}`);
51 |       this.name = 'Platform.InvalidTypeError';
52 |     }
53 |   }
54 | 
55 |   export class InvalidPlatformError extends Error {
56 |     constructor(
57 |       public readonly providedValue: unknown,
58 |       public readonly validValues: string[]
59 |     ) {
60 |       super(`Invalid platform: ${providedValue}. Valid values are: ${validValues.join(', ')}`);
61 |       this.name = 'Platform.InvalidPlatformError';
62 |     }
63 |   }
64 | }
```

--------------------------------------------------------------------------------
/src/shared/tests/mocks/selectiveExecMock.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import type { ExecOptions, ChildProcess } from 'child_process';
 2 | import type { NodeExecError } from '../types/execTypes.js';
 3 | 
 4 | type ExecCallback = (error: NodeExecError | null, stdout: string, stderr: string) => void;
 5 | type ExecFunction = {
 6 |   (command: string, callback: ExecCallback): ChildProcess;
 7 |   (command: string, options: ExecOptions, callback: ExecCallback): ChildProcess;
 8 | };
 9 | 
10 | export interface MockResponse {
11 |   error?: NodeExecError;
12 |   stdout?: string;
13 |   stderr?: string;
14 | }
15 | 
16 | /**
17 |  * Creates a selective exec mock that only mocks specific commands
18 |  * and delegates others to the real exec implementation
19 |  */
20 | export function createSelectiveExecMock(
21 |   commandFilter: (cmd: string) => boolean,
22 |   getMockResponse: () => MockResponse | undefined,
23 |   actualExec: ExecFunction
24 | ) {
25 |   return (cmd: string, ...args: unknown[]) => {
26 |     // Handle both (cmd, callback) and (cmd, options, callback) signatures
27 |     const callback = typeof args[0] === 'function' ? args[0] as ExecCallback : args[1] as ExecCallback;
28 |     const options = typeof args[0] === 'function' ? {} : args[0] as ExecOptions;
29 |     
30 |     if (commandFilter(cmd)) {
31 |       const response = getMockResponse();
32 |       if (response) {
33 |         process.nextTick(() => {
34 |           if (response.error) {
35 |             callback(response.error, response.stdout || '', response.stderr || '');
36 |           } else {
37 |             callback(null, response.stdout || '', response.stderr || '');
38 |           }
39 |         });
40 |       } else {
41 |         process.nextTick(() => {
42 |           const error = new Error(`No mock response configured for: ${cmd}`) as NodeExecError;
43 |           error.code = 1;
44 |           error.stdout = '';
45 |           error.stderr = '';
46 |           callback(error, '', '');
47 |         });
48 |       }
49 |       return;
50 |     }
51 |     
52 |     // Delegate to real exec for other commands
53 |     return actualExec(cmd, options, callback);
54 |   };
55 | }
```

--------------------------------------------------------------------------------
/src/features/simulator/domain/SimulatorState.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Simulator state enum
 3 |  * Values match xcrun simctl output exactly for direct comparison
 4 |  */
 5 | export enum SimulatorState {
 6 |   Booted = 'Booted',
 7 |   Booting = 'Booting',
 8 |   Shutdown = 'Shutdown',
 9 |   ShuttingDown = 'Shutting Down'
10 | }
11 | 
12 | /**
13 |  * SimulatorState validation and parsing utilities
14 |  */
15 | export namespace SimulatorState {
16 |   /**
17 |    * Parse a string into a SimulatorState enum value
18 |    * @throws Error if the string is not a valid state
19 |    */
20 |   export function parse(value: unknown): SimulatorState {
21 |     // Type check
22 |     if (typeof value !== 'string') {
23 |       throw new InvalidTypeError(value);
24 |     }
25 | 
26 |     // Check if valid state - filter out namespace functions
27 |     const validStates = Object.values(SimulatorState).filter(v => typeof v === 'string') as string[];
28 |     if (!validStates.includes(value)) {
29 |       throw new InvalidStateError(value, validStates);
30 |     }
31 | 
32 |     return value as SimulatorState;
33 |   }
34 | 
35 |   /**
36 |    * Parse a string into a SimulatorState enum value or return undefined
37 |    */
38 |   export function parseOptional(value: unknown): SimulatorState | undefined {
39 |     if (value === undefined || value === null) {
40 |       return undefined;
41 |     }
42 |     return parse(value);
43 |   }
44 | 
45 |   // Error classes
46 |   export class InvalidTypeError extends Error {
47 |     constructor(public readonly providedValue: unknown) {
48 |       const validValues = Object.values(SimulatorState).filter(v => typeof v === 'string') as string[];
49 |       super(`Simulator state must be a string (one of: ${validValues.join(', ')}), got ${typeof providedValue}`);
50 |       this.name = 'SimulatorState.InvalidTypeError';
51 |     }
52 |   }
53 | 
54 |   export class InvalidStateError extends Error {
55 |     constructor(
56 |       public readonly providedValue: unknown,
57 |       public readonly validValues: string[]
58 |     ) {
59 |       super(`Invalid simulator state: ${providedValue}. Valid values are: ${validValues.join(', ')}`);
60 |       this.name = 'SimulatorState.InvalidStateError';
61 |     }
62 |   }
63 | }
```

--------------------------------------------------------------------------------
/src/features/simulator/factories/BootSimulatorControllerFactory.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { exec } from 'child_process';
 2 | import { promisify } from 'util';
 3 | import { BootSimulatorUseCase } from '../use-cases/BootSimulatorUseCase.js';
 4 | import { BootSimulatorController } from '../controllers/BootSimulatorController.js';
 5 | import { MCPController } from '../../../presentation/interfaces/MCPController.js';
 6 | import { SimulatorLocatorAdapter } from '../infrastructure/SimulatorLocatorAdapter.js';
 7 | import { SimulatorControlAdapter } from '../infrastructure/SimulatorControlAdapter.js';
 8 | import { ShellCommandExecutorAdapter } from '../../../shared/infrastructure/ShellCommandExecutorAdapter.js';
 9 | import { DependencyCheckingDecorator } from '../../../presentation/decorators/DependencyCheckingDecorator.js';
10 | import { DependencyChecker } from '../../../infrastructure/services/DependencyChecker.js';
11 | 
12 | /**
13 |  * Factory class for creating BootSimulatorController with all dependencies
14 |  * This is the composition root for the boot simulator functionality
15 |  */
16 | export class BootSimulatorControllerFactory {
17 |   static create(): MCPController {
18 |     // Create the shell executor that all adapters will use
19 |     const execAsync = promisify(exec);
20 |     const executor = new ShellCommandExecutorAdapter(execAsync);
21 | 
22 |     // Create infrastructure adapters
23 |     const simulatorLocator = new SimulatorLocatorAdapter(executor);
24 |     const simulatorControl = new SimulatorControlAdapter(executor);
25 | 
26 |     // Create the use case with all dependencies
27 |     const useCase = new BootSimulatorUseCase(
28 |       simulatorLocator,
29 |       simulatorControl
30 |     );
31 | 
32 |     // Create the controller
33 |     const controller = new BootSimulatorController(useCase);
34 | 
35 |     // Create dependency checker
36 |     const dependencyChecker = new DependencyChecker(executor);
37 | 
38 |     // Wrap with dependency checking decorator
39 |     const decoratedController = new DependencyCheckingDecorator(
40 |       controller,
41 |       ['xcrun'],  // simctl is part of xcrun
42 |       dependencyChecker
43 |     );
44 | 
45 |     return decoratedController;
46 |   }
47 | }
```

--------------------------------------------------------------------------------
/src/features/simulator/tests/unit/BootRequest.unit.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { describe, it, expect } from '@jest/globals';
 2 | import { BootRequest } from '../../domain/BootRequest.js';
 3 | import { DeviceId } from '../../../../shared/domain/DeviceId.js';
 4 | 
 5 | describe('BootRequest', () => {
 6 |   describe('create', () => {
 7 |     it('should create a valid boot request with simulator UUID', () => {
 8 |       // Arrange
 9 |       const deviceIdString = 'ABC123-DEF456-789';
10 |       const deviceId = DeviceId.create(deviceIdString);
11 | 
12 |       // Act
13 |       const request = BootRequest.create(deviceId);
14 | 
15 |       // Assert
16 |       expect(request.deviceId).toBe(deviceIdString);
17 |     });
18 | 
19 |     it('should create a valid boot request with simulator name', () => {
20 |       // Arrange
21 |       const deviceName = 'iPhone 15 Pro';
22 |       const deviceId = DeviceId.create(deviceName);
23 | 
24 |       // Act
25 |       const request = BootRequest.create(deviceId);
26 | 
27 |       // Assert
28 |       expect(request.deviceId).toBe(deviceName);
29 |     });
30 | 
31 |     it('should throw error for empty device ID', () => {
32 |       // Arrange
33 |       const emptyId = '';
34 | 
35 |       // Act & Assert
36 |       expect(() => DeviceId.create(emptyId)).toThrow('Device ID cannot be empty');
37 |     });
38 | 
39 |     it('should throw error for whitespace-only device ID', () => {
40 |       // Arrange
41 |       const whitespaceId = '   ';
42 | 
43 |       // Act & Assert
44 |       expect(() => DeviceId.create(whitespaceId)).toThrow('Device ID cannot be whitespace only');
45 |     });
46 | 
47 |     it('should be immutable', () => {
48 |       // Arrange
49 |       const deviceId = DeviceId.create('ABC123');
50 |       const request = BootRequest.create(deviceId);
51 | 
52 |       // Act & Assert
53 |       expect(() => {
54 |         (request as any).deviceId = 'changed';
55 |       }).toThrow();
56 |     });
57 | 
58 |     it('should trim whitespace from device ID', () => {
59 |       // Arrange
60 |       const idWithSpaces = '  iPhone 15  ';
61 |       const deviceId = DeviceId.create(idWithSpaces);
62 | 
63 |       // Act
64 |       const request = BootRequest.create(deviceId);
65 | 
66 |       // Assert
67 |       expect(request.deviceId).toBe('iPhone 15');
68 |     });
69 |   });
70 | });
```

--------------------------------------------------------------------------------
/src/shared/tests/utils/gitResetTestArtifacts.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Utility to reset test artifacts using git
 3 |  * This ensures consistent cleanup across all test utilities
 4 |  */
 5 | 
 6 | import { execSync } from 'child_process';
 7 | import { resolve } from 'path';
 8 | import { createModuleLogger } from '../../../logger.js';
 9 | 
10 | const logger = createModuleLogger('GitResetTestArtifacts');
11 | 
12 | /**
13 |  * Reset test_artifacts directory to pristine git state
14 |  * @param path - Optional specific path within test_artifacts to reset
15 |  */
16 | export function gitResetTestArtifacts(path?: string): void {
17 |   const targetPath = path || 'test_artifacts/';
18 |   
19 |   try {
20 |     // Remove untracked files and directories (build artifacts)
21 |     execSync(`git clean -fdx ${targetPath}`, { 
22 |       cwd: resolve(process.cwd()),
23 |       stdio: 'pipe'
24 |     });
25 |     
26 |     // First unstage any staged changes
27 |     execSync(`git reset HEAD ${targetPath}`, { 
28 |       cwd: resolve(process.cwd()),
29 |       stdio: 'pipe'
30 |     });
31 |     
32 |     // Then reset any modified tracked files
33 |     execSync(`git checkout -- ${targetPath}`, { 
34 |       cwd: resolve(process.cwd()),
35 |       stdio: 'pipe'
36 |     });
37 |     
38 |     logger.debug({ path: targetPath }, 'Reset test artifacts using git');
39 |   } catch (error) {
40 |     logger.error({ error, path: targetPath }, 'Failed to reset test artifacts');
41 |     // Don't throw - cleanup should be best effort
42 |   }
43 | }
44 | 
45 | /**
46 |  * Reset a specific file within test_artifacts
47 |  * @param filePath - Path to the file relative to project root
48 |  */
49 | export function gitResetFile(filePath: string): void {
50 |   try {
51 |     // Only reset if the file is within test_artifacts
52 |     if (!filePath.includes('test_artifacts')) {
53 |       logger.warn({ filePath }, 'Attempting to reset file outside test_artifacts - skipping');
54 |       return;
55 |     }
56 |     
57 |     execSync(`git checkout -- ${filePath}`, { 
58 |       cwd: resolve(process.cwd()),
59 |       stdio: 'pipe'
60 |     });
61 |     
62 |     logger.debug({ filePath }, 'Reset file using git');
63 |   } catch (error) {
64 |     logger.warn({ error, filePath }, 'Failed to reset file - may be untracked');
65 |   }
66 | }
```

--------------------------------------------------------------------------------
/src/features/simulator/factories/ShutdownSimulatorControllerFactory.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { exec } from 'child_process';
 2 | import { promisify } from 'util';
 3 | import { ShutdownSimulatorUseCase } from '../use-cases/ShutdownSimulatorUseCase.js';
 4 | import { ShutdownSimulatorController } from '../controllers/ShutdownSimulatorController.js';
 5 | import { MCPController } from '../../../presentation/interfaces/MCPController.js';
 6 | import { SimulatorLocatorAdapter } from '../infrastructure/SimulatorLocatorAdapter.js';
 7 | import { SimulatorControlAdapter } from '../infrastructure/SimulatorControlAdapter.js';
 8 | import { ShellCommandExecutorAdapter } from '../../../shared/infrastructure/ShellCommandExecutorAdapter.js';
 9 | import { DependencyCheckingDecorator } from '../../../presentation/decorators/DependencyCheckingDecorator.js';
10 | import { DependencyChecker } from '../../../infrastructure/services/DependencyChecker.js';
11 | 
12 | /**
13 |  * Factory class for creating ShutdownSimulatorController with all dependencies
14 |  * This is the composition root for the shutdown simulator functionality
15 |  */
16 | export class ShutdownSimulatorControllerFactory {
17 |   static create(): MCPController {
18 |     // Create the shell executor that all adapters will use
19 |     const execAsync = promisify(exec);
20 |     const executor = new ShellCommandExecutorAdapter(execAsync);
21 | 
22 |     // Create infrastructure adapters
23 |     const simulatorLocator = new SimulatorLocatorAdapter(executor);
24 |     const simulatorControl = new SimulatorControlAdapter(executor);
25 | 
26 |     // Create the use case with all dependencies
27 |     const useCase = new ShutdownSimulatorUseCase(
28 |       simulatorLocator,
29 |       simulatorControl
30 |     );
31 | 
32 |     // Create the controller
33 |     const controller = new ShutdownSimulatorController(useCase);
34 | 
35 |     // Create dependency checker
36 |     const dependencyChecker = new DependencyChecker(executor);
37 | 
38 |     // Wrap with dependency checking decorator
39 |     const decoratedController = new DependencyCheckingDecorator(
40 |       controller,
41 |       ['xcrun'],  // simctl is part of xcrun
42 |       dependencyChecker
43 |     );
44 | 
45 |     return decoratedController;
46 |   }
47 | }
```

--------------------------------------------------------------------------------
/src/shared/domain/DeviceId.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { DomainEmptyError, DomainInvalidTypeError } from '../../domain/errors/DomainError.js';
 2 | 
 3 | /**
 4 |  * Value object for a device identifier
 5 |  * Can be either a device UDID or a device name
 6 |  * Works for both simulators and physical devices
 7 |  */
 8 | export class DeviceId {
 9 |   private constructor(private readonly value: string) {}
10 | 
11 |   static create(id: unknown): DeviceId {
12 |     // Required check
13 |     if (id === undefined || id === null) {
14 |       throw new DeviceId.RequiredError();
15 |     }
16 | 
17 |     // Type checking
18 |     if (typeof id !== 'string') {
19 |       throw new DeviceId.InvalidTypeError(id);
20 |     }
21 | 
22 |     // Empty check
23 |     if (id === '') {
24 |       throw new DeviceId.EmptyError(id);
25 |     }
26 | 
27 |     // Whitespace-only check
28 |     if (id.trim() === '') {
29 |       throw new DeviceId.WhitespaceOnlyError(id);
30 |     }
31 | 
32 |     return new DeviceId(id.trim());
33 |   }
34 | 
35 |   static createOptional(id: unknown): DeviceId | undefined {
36 |     if (id === undefined || id === null) {
37 |       return undefined;
38 |     }
39 |     return DeviceId.create(id);
40 |   }
41 | 
42 |   toString(): string {
43 |     return this.value;
44 |   }
45 | 
46 |   equals(other: DeviceId): boolean {
47 |     return this.value === other.value;
48 |   }
49 | }
50 | 
51 | // Nested error classes under DeviceId namespace
52 | export namespace DeviceId {
53 |   export class RequiredError extends Error {
54 |     constructor() {
55 |       super('Device ID is required');
56 |       this.name = 'DeviceId.RequiredError';
57 |     }
58 |   }
59 | 
60 |   export class InvalidTypeError extends DomainInvalidTypeError {
61 |     constructor(public readonly providedValue: unknown) {
62 |       super('Device ID', 'string');
63 |       this.name = 'DeviceId.InvalidTypeError';
64 |     }
65 |   }
66 | 
67 |   export class EmptyError extends DomainEmptyError {
68 |     constructor(public readonly providedValue: unknown) {
69 |       super('Device ID');
70 |       this.name = 'DeviceId.EmptyError';
71 |     }
72 |   }
73 | 
74 |   export class WhitespaceOnlyError extends Error {
75 |     constructor(public readonly providedValue: unknown) {
76 |       super('Device ID cannot be whitespace only');
77 |       this.name = 'DeviceId.WhitespaceOnlyError';
78 |     }
79 |   }
80 | }
```

--------------------------------------------------------------------------------
/src/shared/infrastructure/ConfigProviderAdapter.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { IConfigProvider } from '../../application/ports/ConfigPorts.js';
 2 | import { homedir } from 'os';
 3 | import path from 'path';
 4 | 
 5 | /**
 6 |  * Infrastructure adapter for configuration access
 7 |  * Implements the IConfigProvider port
 8 |  * 
 9 |  * Self-contained with no external dependencies.
10 |  * Configuration values come from:
11 |  * 1. Environment variables (when available)
12 |  * 2. System defaults (e.g., user home directory)
13 |  * 3. Hardcoded defaults as fallback
14 |  */
15 | export class ConfigProviderAdapter implements IConfigProvider {
16 |   private readonly derivedDataBasePath: string;
17 |   
18 |   constructor() {
19 |     // Read from environment or use default
20 |     // This is an infrastructure concern - reading from the environment
21 |     this.derivedDataBasePath = process.env.MCP_XCODE_DERIVED_DATA_PATH ||
22 |       path.join(homedir(), 'Library', 'Developer', 'Xcode', 'DerivedData', 'MCP-Xcode');
23 |   }
24 |   
25 |   getDerivedDataPath(projectPath?: string): string {
26 |     // If we have a project path, use it for the derived data path
27 |     if (projectPath) {
28 |       const projectName = path.basename(projectPath, path.extname(projectPath));
29 |       return path.join(this.derivedDataBasePath, projectName);
30 |     }
31 |     // Otherwise return the base path
32 |     return this.derivedDataBasePath;
33 |   }
34 |   
35 |   getBuildTimeout(): number {
36 |     // Read from environment or use default (10 minutes)
37 |     const timeout = process.env.MCP_XCODE_BUILD_TIMEOUT;
38 |     return timeout ? parseInt(timeout, 10) : 600000;
39 |   }
40 |   
41 |   isXcbeautifyEnabled(): boolean {
42 |     // Read from environment or default to true
43 |     const enabled = process.env.MCP_XCODE_XCBEAUTIFY_ENABLED;
44 |     return enabled ? enabled.toLowerCase() === 'true' : true;
45 |   }
46 |   
47 |   getCustomBuildSettings(): Record<string, string> {
48 |     // Could read from environment as JSON or return empty
49 |     const settings = process.env.MCP_XCODE_CUSTOM_BUILD_SETTINGS;
50 |     if (settings) {
51 |       try {
52 |         return JSON.parse(settings);
53 |       } catch {
54 |         // Invalid JSON, return empty
55 |         return {};
56 |       }
57 |     }
58 |     return {};
59 |   }
60 | }
```

--------------------------------------------------------------------------------
/src/features/simulator/use-cases/ShutdownSimulatorUseCase.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { ShutdownRequest } from '../domain/ShutdownRequest.js';
 2 | import { ShutdownResult, SimulatorNotFoundError, ShutdownCommandFailedError } from '../domain/ShutdownResult.js';
 3 | import { SimulatorState } from '../domain/SimulatorState.js';
 4 | import { ISimulatorLocator, ISimulatorControl } from '../../../application/ports/SimulatorPorts.js';
 5 | 
 6 | export interface IShutdownSimulatorUseCase {
 7 |   execute(request: ShutdownRequest): Promise<ShutdownResult>;
 8 | }
 9 | 
10 | /**
11 |  * Use Case: Shutdown a simulator
12 |  * 
13 |  * Orchestrates finding the target simulator and shutting it down if needed
14 |  */
15 | export class ShutdownSimulatorUseCase implements IShutdownSimulatorUseCase {
16 |   constructor(
17 |     private simulatorLocator: ISimulatorLocator,
18 |     private simulatorControl: ISimulatorControl
19 |   ) {}
20 | 
21 |   async execute(request: ShutdownRequest): Promise<ShutdownResult> {
22 |     // Find the simulator
23 |     const simulator = await this.simulatorLocator.findSimulator(request.deviceId);
24 |     
25 |     if (!simulator) {
26 |       return ShutdownResult.failed(
27 |         request.deviceId,
28 |         '',  // No name available since simulator wasn't found
29 |         new SimulatorNotFoundError(request.deviceId)
30 |       );
31 |     }
32 |     
33 |     // Check simulator state
34 |     if (simulator.state === SimulatorState.Shutdown) {
35 |       return ShutdownResult.alreadyShutdown(
36 |         simulator.id,
37 |         simulator.name
38 |       );
39 |     }
40 |     
41 |     // Handle ShuttingDown state - simulator is already shutting down
42 |     if (simulator.state === SimulatorState.ShuttingDown) {
43 |       return ShutdownResult.alreadyShutdown(
44 |         simulator.id,
45 |         simulator.name
46 |       );
47 |     }
48 |     
49 |     // Shutdown the simulator (handles Booted and Booting states)
50 |     try {
51 |       await this.simulatorControl.shutdown(simulator.id);
52 |       
53 |       return ShutdownResult.shutdown(
54 |         simulator.id,
55 |         simulator.name
56 |       );
57 |     } catch (error: any) {
58 |       return ShutdownResult.failed(
59 |         simulator.id,
60 |         simulator.name,
61 |         new ShutdownCommandFailedError(error.stderr || error.message || '')
62 |       );
63 |     }
64 |   }
65 | }
```

--------------------------------------------------------------------------------
/src/presentation/tests/unit/ErrorFormatter.unit.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { ErrorFormatter } from '../../formatters/ErrorFormatter.js';
 2 | import { BuildIssue } from '../../../features/build/domain/BuildIssue.js';
 3 | 
 4 | describe('ErrorFormatter', () => {
 5 |   describe('strategy delegation', () => {
 6 |     it('should delegate error with BuildIssues to BuildIssuesStrategy', () => {
 7 |       const error = {
 8 |         issues: [
 9 |           BuildIssue.error('Test error')
10 |         ]
11 |       };
12 |       
13 |       const result = ErrorFormatter.format(error);
14 |       
15 |       // Should return formatted result
16 |       expect(result).toBeDefined();
17 |       expect(typeof result).toBe('string');
18 |       expect(result.length).toBeGreaterThan(0);
19 |     });
20 | 
21 |     it('should delegate plain Error to DefaultErrorStrategy', () => {
22 |       const error = new Error('Plain error message');
23 |       
24 |       const result = ErrorFormatter.format(error);
25 |       
26 |       // Should return formatted result
27 |       expect(result).toBeDefined();
28 |       expect(typeof result).toBe('string');
29 |       expect(result.length).toBeGreaterThan(0);
30 |     });
31 | 
32 |     it('should delegate unknown objects to DefaultErrorStrategy', () => {
33 |       const error = { someField: 'value' };
34 |       
35 |       const result = ErrorFormatter.format(error);
36 |       
37 |       // Should return formatted result (DefaultErrorStrategy handles everything)
38 |       expect(result).toBeDefined();
39 |       expect(typeof result).toBe('string');
40 |       expect(result.length).toBeGreaterThan(0);
41 |     });
42 | 
43 |     it('should handle null by delegating to DefaultErrorStrategy', () => {
44 |       const result = ErrorFormatter.format(null);
45 |       
46 |       // DefaultErrorStrategy should handle null
47 |       expect(result).toBeDefined();
48 |       expect(typeof result).toBe('string');
49 |       expect(result.length).toBeGreaterThan(0);
50 |     });
51 | 
52 |     it('should handle undefined by delegating to DefaultErrorStrategy', () => {
53 |       const result = ErrorFormatter.format(undefined);
54 |       
55 |       // DefaultErrorStrategy should handle undefined
56 |       expect(result).toBeDefined();
57 |       expect(typeof result).toBe('string');
58 |       expect(result.length).toBeGreaterThan(0);
59 |     });
60 |   });
61 | });
```

--------------------------------------------------------------------------------
/test_artifacts/TestProjectXCTest/TestProjectXCTestTests/TestProjectXCTestTests.swift:
--------------------------------------------------------------------------------

```swift
 1 | //
 2 | //  TestProjectXCTestTests.swift
 3 | //  TestProjectXCTestTests
 4 | //
 5 | //  Created by Stefan Dragos Nitu on 17/08/2025.
 6 | //
 7 | 
 8 | import XCTest
 9 | 
10 | final class TestProjectXCTestTests: XCTestCase {
11 | 
12 |     override func setUpWithError() throws {
13 |         // Put setup code here. This method is called before the invocation of each test method in the class.
14 |     }
15 | 
16 |     override func tearDownWithError() throws {
17 |         // Put teardown code here. This method is called after the invocation of each test method in the class.
18 |     }
19 | 
20 |     func testExample() throws {
21 |         // This is an example of a functional test case.
22 |         // Use XCTAssert and related functions to verify your tests produce the correct results.
23 |         // Any test you write for XCTest can be annotated as throws and async.
24 |         // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
25 |         // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
26 |     }
27 | 
28 |     func testPerformanceExample() throws {
29 |         // This is an example of a performance test case.
30 |         measure {
31 |             // Put the code you want to measure the time of here.
32 |         }
33 |     }
34 |     
35 |     func testTargetForFilter() throws {
36 |         // This is an example of a functional test case.
37 |         // Use XCTAssert and related functions to verify your tests produce the correct results.
38 |         // Any test you write for XCTest can be annotated as throws and async.
39 |         // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
40 |         // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
41 |     }
42 |     
43 |     func testFailingTest() throws {
44 |         XCTFail("Test MCP failing test reporting")
45 |     }
46 |     
47 |     func testAnotherFailure() throws {
48 |         // Another failing test to verify multiple failures are handled
49 |         let result = 42
50 |         XCTAssertEqual(result, 100, "Expected result to be 100 but got \(result)")
51 |     }
52 | 
53 | }
54 | 
```

--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------

```yaml
  1 | name: CI
  2 | 
  3 | on:
  4 |   push:
  5 |     branches: [ main ]
  6 |   pull_request:
  7 |     branches: [ main ]
  8 | 
  9 | jobs:
 10 |   test:
 11 |     runs-on: macos-latest
 12 |     
 13 |     strategy:
 14 |       matrix:
 15 |         node-version: [18.x, 20.x, 22.x]
 16 |     
 17 |     steps:
 18 |     - uses: actions/checkout@v4
 19 |     
 20 |     - name: Use Node.js ${{ matrix.node-version }}
 21 |       uses: actions/setup-node@v4
 22 |       with:
 23 |         node-version: ${{ matrix.node-version }}
 24 |     
 25 |     - name: Install dependencies
 26 |       run: npm ci
 27 |     
 28 |     - name: Build TypeScript
 29 |       run: npm run build
 30 |     
 31 |     - name: Run unit tests with coverage
 32 |       run: npm run test:unit -- --coverage
 33 | 
 34 |     - name: Run integration tests with coverage
 35 |       run: npm run test:integration -- --coverage
 36 | 
 37 |     - name: Upload coverage reports
 38 |       uses: codecov/codecov-action@v4
 39 |       if: matrix.node-version == '20.x'
 40 |       with:
 41 |         files: ./coverage/lcov.info
 42 |         flags: unittests
 43 |         name: codecov-umbrella
 44 |         fail_ci_if_error: false
 45 | 
 46 |   e2e-test:
 47 |     runs-on: macos-15
 48 |     needs: test
 49 | 
 50 |     steps:
 51 |     - uses: actions/checkout@v4
 52 |     
 53 |     - name: Use Node.js 20.x
 54 |       uses: actions/setup-node@v4
 55 |       with:
 56 |         node-version: '20.x'
 57 | 
 58 |     - name: Install dependencies
 59 |       run: |
 60 |         echo "Installing system dependencies..."
 61 |         which xcbeautify || brew install xcbeautify
 62 |         which xcbeautify
 63 |         echo "Installing Node.js dependencies..."
 64 |         npm ci
 65 |     
 66 |     - name: Build TypeScript
 67 |       run: npm run build
 68 |     
 69 |     - name: Run E2E tests
 70 |       run: npm run test:e2e
 71 |       timeout-minutes: 90
 72 |     
 73 |     - name: Upload test logs
 74 |       if: always()
 75 |       uses: actions/upload-artifact@v4
 76 |       with:
 77 |         name: e2e-test-logs
 78 |         path: |
 79 |           ~/.mcp-xcode-server/logs/
 80 |         retention-days: 7
 81 |         if-no-files-found: warn
 82 | 
 83 |   lint:
 84 |     runs-on: macos-latest
 85 | 
 86 |     steps:
 87 |     - uses: actions/checkout@v4
 88 | 
 89 |     - name: Use Node.js
 90 |       uses: actions/setup-node@v4
 91 |       with:
 92 |         node-version: '20.x'
 93 | 
 94 |     - name: Install dependencies
 95 |       run: npm ci
 96 | 
 97 |     - name: Check TypeScript
 98 |       run: npx tsc --noEmit
 99 | 
100 |     - name: Build
101 |       run: npm run build
```

--------------------------------------------------------------------------------
/src/features/app-management/factories/InstallAppControllerFactory.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { exec } from 'child_process';
 2 | import { promisify } from 'util';
 3 | import { InstallAppUseCase } from '../use-cases/InstallAppUseCase.js';
 4 | import { InstallAppController } from '../controllers/InstallAppController.js';
 5 | import { MCPController } from '../../../presentation/interfaces/MCPController.js';
 6 | import { SimulatorLocatorAdapter } from '../../simulator/infrastructure/SimulatorLocatorAdapter.js';
 7 | import { SimulatorControlAdapter } from '../../simulator/infrastructure/SimulatorControlAdapter.js';
 8 | import { AppInstallerAdapter } from '../infrastructure/AppInstallerAdapter.js';
 9 | import { ShellCommandExecutorAdapter } from '../../../shared/infrastructure/ShellCommandExecutorAdapter.js';
10 | import { LogManagerInstance } from '../../../utils/LogManagerInstance.js';
11 | import { DependencyCheckingDecorator } from '../../../presentation/decorators/DependencyCheckingDecorator.js';
12 | import { DependencyChecker } from '../../../infrastructure/services/DependencyChecker.js';
13 | 
14 | /**
15 |  * Factory class for creating InstallAppController with all dependencies
16 |  * This is the composition root for the install app functionality
17 |  */
18 | export class InstallAppControllerFactory {
19 |   static create(): MCPController {
20 |     // Create the shell executor that all adapters will use
21 |     const execAsync = promisify(exec);
22 |     const executor = new ShellCommandExecutorAdapter(execAsync);
23 | 
24 |     // Create infrastructure adapters
25 |     const simulatorLocator = new SimulatorLocatorAdapter(executor);
26 |     const simulatorControl = new SimulatorControlAdapter(executor);
27 |     const appInstaller = new AppInstallerAdapter(executor);
28 |     const logManager = new LogManagerInstance();
29 | 
30 |     // Create the use case with all dependencies
31 |     const useCase = new InstallAppUseCase(
32 |       simulatorLocator,
33 |       simulatorControl,
34 |       appInstaller,
35 |       logManager
36 |     );
37 | 
38 |     // Create the controller
39 |     const controller = new InstallAppController(useCase);
40 | 
41 |     // Create dependency checker
42 |     const dependencyChecker = new DependencyChecker(executor);
43 | 
44 |     // Wrap with dependency checking decorator
45 |     const decoratedController = new DependencyCheckingDecorator(
46 |       controller,
47 |       ['xcrun'],  // simctl is part of xcrun
48 |       dependencyChecker
49 |     );
50 | 
51 |     return decoratedController;
52 |   }
53 | }
```

--------------------------------------------------------------------------------
/src/shared/domain/ProjectPath.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { existsSync } from 'fs';
 2 | import path from 'path';
 3 | import { DomainEmptyError, DomainInvalidTypeError, DomainInvalidFormatError, DomainRequiredError } from '../../domain/errors/DomainError.js';
 4 | 
 5 | /**
 6 |  * Value Object: Represents a validated project path
 7 |  * Ensures the path exists and is a valid Xcode project or workspace
 8 |  */
 9 | export class ProjectPath {
10 |   private constructor(private readonly value: string) {}
11 | 
12 |   static create(pathString: unknown): ProjectPath {
13 |     // Required check (for undefined/null)
14 |     if (pathString === undefined || pathString === null) {
15 |       throw new ProjectPath.RequiredError();
16 |     }
17 | 
18 |     // Type checking
19 |     if (typeof pathString !== 'string') {
20 |       throw new ProjectPath.InvalidTypeError(pathString);
21 |     }
22 | 
23 |     // Empty check
24 |     if (pathString.trim() === '') {
25 |       throw new ProjectPath.EmptyError(pathString);
26 |     }
27 | 
28 |     const trimmed = pathString.trim();
29 | 
30 |     // Format validation
31 |     const ext = path.extname(trimmed);
32 |     if (ext !== '.xcodeproj' && ext !== '.xcworkspace') {
33 |       throw new ProjectPath.InvalidFormatError(trimmed);
34 |     }
35 | 
36 |     // Runtime check - this stays as a regular Error since it's not validation
37 |     if (!existsSync(trimmed)) {
38 |       throw new Error(`Project path does not exist: ${trimmed}`);
39 |     }
40 | 
41 |     return new ProjectPath(trimmed);
42 |   }
43 |   
44 |   toString(): string {
45 |     return this.value;
46 |   }
47 |   
48 |   get name(): string {
49 |     return path.basename(this.value, path.extname(this.value));
50 |   }
51 |   
52 |   get isWorkspace(): boolean {
53 |     return path.extname(this.value) === '.xcworkspace';
54 |   }
55 | }
56 | 
57 | // Nested error classes under ProjectPath namespace
58 | export namespace ProjectPath {
59 |   export class RequiredError extends DomainRequiredError {
60 |     constructor() {
61 |       super('Project path');
62 |       this.name = 'ProjectPath.RequiredError';
63 |     }
64 |   }
65 | 
66 |   export class InvalidTypeError extends DomainInvalidTypeError {
67 |     constructor(public readonly providedValue: unknown) {
68 |       super('Project path', 'string');
69 |       this.name = 'ProjectPath.InvalidTypeError';
70 |     }
71 |   }
72 | 
73 |   export class EmptyError extends DomainEmptyError {
74 |     constructor(public readonly providedValue: unknown) {
75 |       super('Project path');
76 |       this.name = 'ProjectPath.EmptyError';
77 |     }
78 |   }
79 | 
80 |   export class InvalidFormatError extends DomainInvalidFormatError {
81 |     constructor(public readonly path: string) {
82 |       super('Project path must be an .xcodeproj or .xcworkspace file');
83 |       this.name = 'ProjectPath.InvalidFormatError';
84 |     }
85 |   }
86 | }
```

--------------------------------------------------------------------------------
/src/features/simulator/use-cases/BootSimulatorUseCase.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { BootRequest } from '../domain/BootRequest.js';
 2 | import { BootResult, SimulatorNotFoundError, BootCommandFailedError, SimulatorBusyError } from '../domain/BootResult.js';
 3 | import { SimulatorState } from '../domain/SimulatorState.js';
 4 | import { ISimulatorLocator, ISimulatorControl } from '../../../application/ports/SimulatorPorts.js';
 5 | 
 6 | export interface IBootSimulatorUseCase {
 7 |   execute(request: BootRequest): Promise<BootResult>;
 8 | }
 9 | 
10 | /**
11 |  * Use Case: Boot a simulator
12 |  * 
13 |  * Orchestrates finding the target simulator and booting it if needed
14 |  */
15 | export class BootSimulatorUseCase implements IBootSimulatorUseCase {
16 |   constructor(
17 |     private simulatorLocator: ISimulatorLocator,
18 |     private simulatorControl: ISimulatorControl
19 |   ) {}
20 | 
21 |   async execute(request: BootRequest): Promise<BootResult> {
22 |     // Find the simulator
23 |     const simulator = await this.simulatorLocator.findSimulator(request.deviceId);
24 |     
25 |     if (!simulator) {
26 |       return BootResult.failed(
27 |         request.deviceId,
28 |         '',  // No name available since simulator wasn't found
29 |         new SimulatorNotFoundError(request.deviceId)
30 |       );
31 |     }
32 |     
33 |     // Check simulator state
34 |     if (simulator.state === SimulatorState.Booted) {
35 |       return BootResult.alreadyBooted(
36 |         simulator.id,
37 |         simulator.name,
38 |         {
39 |           platform: simulator.platform,
40 |           runtime: simulator.runtime
41 |         }
42 |       );
43 |     }
44 |     
45 |     // Handle Booting state - simulator is already in the process of booting
46 |     if (simulator.state === SimulatorState.Booting) {
47 |       return BootResult.alreadyBooted(
48 |         simulator.id,
49 |         simulator.name,
50 |         {
51 |           platform: simulator.platform,
52 |           runtime: simulator.runtime
53 |         }
54 |       );
55 |     }
56 |     
57 |     // Handle ShuttingDown state - can't boot while shutting down
58 |     if (simulator.state === SimulatorState.ShuttingDown) {
59 |       return BootResult.failed(
60 |         simulator.id,
61 |         simulator.name,
62 |         new SimulatorBusyError(SimulatorState.ShuttingDown)
63 |       );
64 |     }
65 |     
66 |     // Boot the simulator (handles Shutdown state)
67 |     try {
68 |       await this.simulatorControl.boot(simulator.id);
69 |       
70 |       return BootResult.booted(
71 |         simulator.id,
72 |         simulator.name,
73 |         {
74 |           platform: simulator.platform,
75 |           runtime: simulator.runtime
76 |         }
77 |       );
78 |     } catch (error: any) {
79 |       return BootResult.failed(
80 |         simulator.id,
81 |         simulator.name,
82 |         new BootCommandFailedError(error.stderr || error.message || '')
83 |       );
84 |     }
85 |   }
86 | }
```

--------------------------------------------------------------------------------
/src/utils/errors/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Export types and functions from xcbeautify parser
 3 |  */
 4 | 
 5 | export {
 6 |   Issue,
 7 |   Test,
 8 |   XcbeautifyOutput,
 9 |   parseXcbeautifyOutput,
10 |   formatParsedOutput
11 | } from './xcbeautify-parser.js';
12 | 
13 | import { Issue, parseXcbeautifyOutput as parseOutput } from './xcbeautify-parser.js';
14 | 
15 | // Error handlers for tools
16 | // These return MCP format for tools
17 | export function handleSwiftPackageError(error: unknown, context?: any): { content: { type: string; text: string }[] } {
18 |   const message = error instanceof Error ? error.message : String(error);
19 |   
20 |   // Add ❌ prefix if message doesn't already have xcbeautify formatting
21 |   const formattedMessage = message.includes('❌') || message.includes('⚠️') || message.includes('✅') 
22 |     ? message 
23 |     : `❌ ${message}`;
24 |   
25 |   const contextInfo = context 
26 |     ? Object.entries(context)
27 |         .filter(([_, v]) => v !== undefined)
28 |         .map(([k, v]) => `${k}: ${v}`)
29 |         .join(', ')
30 |     : '';
31 |   
32 |   // Check if error has a logPath property
33 |   const logPath = (error as any)?.logPath;
34 |   const logInfo = logPath ? `\n\n📁 Full logs saved to: ${logPath}` : '';
35 |   
36 |   return {
37 |     content: [{
38 |       type: 'text',
39 |       text: contextInfo ? `${formattedMessage}\n\nContext: ${contextInfo}${logInfo}` : `${formattedMessage}${logInfo}`
40 |     }]
41 |   };
42 | }
43 | 
44 | export function handleXcodeError(error: unknown, context?: any): { content: { type: string; text: string }[] } {
45 |   const message = error instanceof Error ? error.message : String(error);
46 |   
47 |   // Add ❌ prefix if message doesn't already have xcbeautify formatting
48 |   const formattedMessage = message.includes('❌') || message.includes('⚠️') || message.includes('✅') 
49 |     ? message 
50 |     : `❌ ${message}`;
51 |   
52 |   const contextInfo = context 
53 |     ? Object.entries(context)
54 |         .filter(([_, v]) => v !== undefined)
55 |         .map(([k, v]) => `${k}: ${v}`)
56 |         .join(', ')
57 |     : '';
58 |   
59 |   // Check if error has a logPath property
60 |   const logPath = (error as any)?.logPath;
61 |   const logInfo = logPath ? `\n\n📁 Full logs saved to: ${logPath}` : '';
62 |   
63 |   return {
64 |     content: [{
65 |       type: 'text',
66 |       text: contextInfo ? `${formattedMessage}\n\nContext: ${contextInfo}${logInfo}` : `${formattedMessage}${logInfo}`
67 |     }]
68 |   };
69 | }
70 | 
71 | // Backward compatibility for tests
72 | export function parseBuildErrors(output: string): Issue[] {
73 |   const { errors, warnings } = parseOutput(output);
74 |   return [...errors, ...warnings];
75 | }
76 | 
77 | export function formatBuildErrors(errors: Issue[]): string {
78 |   return errors.map(e => `${e.file ? `${e.file}:${e.line}:${e.column} - ` : ''}${e.message}`).join('\n');
79 | }
```

--------------------------------------------------------------------------------
/examples/screenshot-demo.js:
--------------------------------------------------------------------------------

```javascript
 1 | #!/usr/bin/env node
 2 | 
 3 | /**
 4 |  * Demo script showing how to use the view_simulator_screen tool
 5 |  * to capture and view the current simulator screen
 6 |  */
 7 | 
 8 | import { Client } from '@modelcontextprotocol/sdk/client/index.js';
 9 | import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
10 | import { CallToolResultSchema } from '@modelcontextprotocol/sdk/types.js';
11 | import { writeFileSync } from 'fs';
12 | 
13 | async function main() {
14 |   // Create MCP client
15 |   const transport = new StdioClientTransport({
16 |     command: 'node',
17 |     args: ['../dist/index.js'],
18 |     cwd: process.cwd(),
19 |   });
20 |   
21 |   const client = new Client({
22 |     name: 'screenshot-demo',
23 |     version: '1.0.0',
24 |   }, {
25 |     capabilities: {}
26 |   });
27 |   
28 |   console.log('Connecting to MCP server...');
29 |   await client.connect(transport);
30 |   
31 |   try {
32 |     // List available simulators
33 |     console.log('Listing simulators...');
34 |     const listResponse = await client.request({
35 |       method: 'tools/call',
36 |       params: {
37 |         name: 'list_simulators',
38 |         arguments: {
39 |           platform: 'iOS'
40 |         }
41 |       }
42 |     }, CallToolResultSchema);
43 |     
44 |     const devices = JSON.parse(listResponse.content[0].text);
45 |     console.log(`Found ${devices.length} iOS simulators`);
46 |     
47 |     const bootedDevice = devices.find(d => d.state === 'Booted');
48 |     if (!bootedDevice) {
49 |       console.log('No booted simulator found. Please boot a simulator first.');
50 |       return;
51 |     }
52 |     
53 |     console.log(`Using booted simulator: ${bootedDevice.name}`);
54 |     
55 |     // Capture the screen
56 |     console.log('Capturing simulator screen...');
57 |     const screenshotResponse = await client.request({
58 |       method: 'tools/call',
59 |       params: {
60 |         name: 'view_simulator_screen',
61 |         arguments: {
62 |           deviceId: bootedDevice.udid
63 |         }
64 |       }
65 |     }, CallToolResultSchema);
66 |     
67 |     // The response contains the image data
68 |     const imageContent = screenshotResponse.content[0];
69 |     if (imageContent.type === 'image') {
70 |       console.log(`Screenshot captured! Image size: ${imageContent.data.length} bytes (base64)`);
71 |       
72 |       // Save to file for demonstration
73 |       const buffer = Buffer.from(imageContent.data, 'base64');
74 |       const filename = `simulator-screen-${Date.now()}.png`;
75 |       writeFileSync(filename, buffer);
76 |       console.log(`Screenshot saved to: ${filename}`);
77 |       
78 |       // In a real MCP client like Claude Code, the image would be displayed directly
79 |       console.log('In an MCP client, this image would be displayed for viewing and analysis.');
80 |     }
81 |     
82 |   } finally {
83 |     await client.close();
84 |     console.log('Disconnected from MCP server');
85 |   }
86 | }
87 | 
88 | main().catch(console.error);
```

--------------------------------------------------------------------------------
/src/features/app-management/domain/InstallResult.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { AppPath } from '../../../shared/domain/AppPath.js';
  2 | import { DeviceId } from '../../../shared/domain/DeviceId.js';
  3 | 
  4 | /**
  5 |  * Domain Entity: Represents the result of an app installation
  6 |  *
  7 |  * Separates user-facing outcome from internal diagnostics
  8 |  */
  9 | 
 10 | // User-facing outcome (what happened)
 11 | export enum InstallOutcome {
 12 |   Succeeded = 'succeeded',
 13 |   Failed = 'failed'
 14 | }
 15 | 
 16 | // Base class for all install-related errors
 17 | export abstract class InstallError extends Error {}
 18 | 
 19 | // Specific error types
 20 | export class AppNotFoundError extends InstallError {
 21 |   constructor(public readonly appPath: AppPath) {
 22 |     super(appPath.toString());
 23 |     this.name = 'AppNotFoundError';
 24 |   }
 25 | }
 26 | 
 27 | export class SimulatorNotFoundError extends InstallError {
 28 |   constructor(public readonly simulatorId: DeviceId) {
 29 |     super(simulatorId.toString());
 30 |     this.name = 'SimulatorNotFoundError';
 31 |   }
 32 | }
 33 | 
 34 | export class NoBootedSimulatorError extends InstallError {
 35 |   constructor() {
 36 |     super('No booted simulator found');
 37 |     this.name = 'NoBootedSimulatorError';
 38 |   }
 39 | }
 40 | 
 41 | export class InstallCommandFailedError extends InstallError {
 42 |   constructor(public readonly stderr: string) {
 43 |     super(stderr);
 44 |     this.name = 'InstallCommandFailedError';
 45 |   }
 46 | }
 47 | 
 48 | // Internal diagnostics (why/how it happened)
 49 | export interface InstallDiagnostics {
 50 |   readonly appPath: AppPath;
 51 |   readonly simulatorId?: DeviceId;
 52 |   readonly simulatorName?: string;
 53 |   readonly bundleId?: string;
 54 |   readonly error?: InstallError;
 55 |   readonly installedAt: Date;
 56 | }
 57 | 
 58 | // Complete result combining outcome and diagnostics
 59 | export interface InstallResult {
 60 |   readonly outcome: InstallOutcome;
 61 |   readonly diagnostics: InstallDiagnostics;
 62 | }
 63 | 
 64 | export const InstallResult = {
 65 |   /**
 66 |    * Installation succeeded
 67 |    */
 68 |   succeeded(
 69 |     bundleId: string,
 70 |     simulatorId: DeviceId,
 71 |     simulatorName: string,
 72 |     appPath: AppPath,
 73 |     diagnostics?: Partial<InstallDiagnostics>
 74 |   ): InstallResult {
 75 |     return Object.freeze({
 76 |       outcome: InstallOutcome.Succeeded,
 77 |       diagnostics: Object.freeze({
 78 |         bundleId,
 79 |         simulatorId,
 80 |         simulatorName,
 81 |         appPath,
 82 |         installedAt: new Date(),
 83 |         ...diagnostics
 84 |       })
 85 |     });
 86 |   },
 87 | 
 88 |   /**
 89 |    * Installation failed
 90 |    */
 91 |   failed(
 92 |     error: InstallError,
 93 |     appPath: AppPath,
 94 |     simulatorId?: DeviceId,
 95 |     simulatorName?: string,
 96 |     diagnostics?: Partial<InstallDiagnostics>
 97 |   ): InstallResult {
 98 |     return Object.freeze({
 99 |       outcome: InstallOutcome.Failed,
100 |       diagnostics: Object.freeze({
101 |         error,
102 |         appPath,
103 |         simulatorId,
104 |         simulatorName,
105 |         installedAt: new Date(),
106 |         ...diagnostics
107 |       })
108 |     });
109 |   }
110 | };
```

--------------------------------------------------------------------------------
/src/features/simulator/domain/BootResult.ts:
--------------------------------------------------------------------------------

```typescript
 1 | /**
 2 |  * Domain entity representing the result of a boot simulator operation
 3 |  * 
 4 |  * Separates user-facing outcome from internal diagnostics
 5 |  */
 6 | 
 7 | // User-facing outcome (what happened)
 8 | export enum BootOutcome {
 9 |   Booted = 'booted',              // Successfully booted the simulator
10 |   AlreadyBooted = 'alreadyBooted', // Simulator was already running  
11 |   Failed = 'failed'                // Boot failed
12 | }
13 | 
14 | // Base class for all boot-related errors
15 | export abstract class BootError extends Error {}
16 | 
17 | // Specific error types
18 | export class SimulatorNotFoundError extends BootError {
19 |   constructor(public readonly deviceId: string) {
20 |     super(deviceId); // Just store the data
21 |     this.name = 'SimulatorNotFoundError';
22 |   }
23 | }
24 | 
25 | export class BootCommandFailedError extends BootError {
26 |   constructor(public readonly stderr: string) {
27 |     super(stderr); // Just store the stderr output
28 |     this.name = 'BootCommandFailedError';
29 |   }
30 | }
31 | 
32 | export class SimulatorBusyError extends BootError {
33 |   constructor(public readonly currentState: string) {
34 |     super(currentState); // Just store the state
35 |     this.name = 'SimulatorBusyError';
36 |   }
37 | }
38 | 
39 | // Internal diagnostics (why/how it happened)
40 | export interface BootDiagnostics {
41 |   readonly simulatorId: string;
42 |   readonly simulatorName: string;
43 |   readonly error?: BootError;           // Any boot-specific error
44 |   readonly runtime?: string;            // Which iOS version
45 |   readonly platform?: string;           // iOS, tvOS, etc
46 | }
47 | 
48 | // Complete result combining outcome and diagnostics
49 | export interface BootResult {
50 |   readonly outcome: BootOutcome;
51 |   readonly diagnostics: BootDiagnostics;
52 | }
53 | 
54 | export const BootResult = {
55 |   /**
56 |    * Simulator was successfully booted
57 |    */
58 |   booted(simulatorId: string, simulatorName: string, diagnostics?: Partial<BootDiagnostics>): BootResult {
59 |     return Object.freeze({
60 |       outcome: BootOutcome.Booted,
61 |       diagnostics: Object.freeze({
62 |         simulatorId,
63 |         simulatorName,
64 |         ...diagnostics
65 |       })
66 |     });
67 |   },
68 | 
69 |   /**
70 |    * Simulator was already running
71 |    */
72 |   alreadyBooted(simulatorId: string, simulatorName: string, diagnostics?: Partial<BootDiagnostics>): BootResult {
73 |     return Object.freeze({
74 |       outcome: BootOutcome.AlreadyBooted,
75 |       diagnostics: Object.freeze({
76 |         simulatorId,
77 |         simulatorName,
78 |         ...diagnostics
79 |       })
80 |     });
81 |   },
82 | 
83 |   /**
84 |    * Boot operation failed
85 |    */
86 |   failed(simulatorId: string, simulatorName: string, error: BootError, diagnostics?: Partial<BootDiagnostics>): BootResult {
87 |     return Object.freeze({
88 |       outcome: BootOutcome.Failed,
89 |       diagnostics: Object.freeze({
90 |         simulatorId,
91 |         simulatorName,
92 |         error,
93 |         ...diagnostics
94 |       })
95 |     });
96 |   }
97 | };
```

--------------------------------------------------------------------------------
/src/domain/tests/unit/PlatformDetector.unit.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { describe, it, expect } from '@jest/globals';
 2 | import { PlatformDetector } from '../../services/PlatformDetector.js';
 3 | import { BuildDestination } from '../../../features/build/domain/BuildDestination.js';
 4 | import { Platform } from '../../../shared/domain/Platform.js';
 5 | 
 6 | /**
 7 |  * Unit tests for PlatformDetector domain service
 8 |  * 
 9 |  * Testing pure domain logic for platform detection from build destinations
10 |  */
11 | describe('PlatformDetector', () => {
12 |   describe('fromDestination', () => {
13 |     it('should detect iOS platform from iOS destinations', () => {
14 |       // Arrange & Act & Assert
15 |       expect(PlatformDetector.fromDestination(BuildDestination.iOSSimulator)).toBe(Platform.iOS);
16 |       expect(PlatformDetector.fromDestination(BuildDestination.iOSDevice)).toBe(Platform.iOS);
17 |       expect(PlatformDetector.fromDestination(BuildDestination.iOSSimulatorUniversal)).toBe(Platform.iOS);
18 |     });
19 | 
20 |     it('should detect macOS platform from macOS destinations', () => {
21 |       // Arrange & Act & Assert
22 |       expect(PlatformDetector.fromDestination(BuildDestination.macOS)).toBe(Platform.macOS);
23 |       expect(PlatformDetector.fromDestination(BuildDestination.macOSUniversal)).toBe(Platform.macOS);
24 |     });
25 | 
26 |     it('should detect tvOS platform from tvOS destinations', () => {
27 |       // Arrange & Act & Assert
28 |       expect(PlatformDetector.fromDestination(BuildDestination.tvOSSimulator)).toBe(Platform.tvOS);
29 |       expect(PlatformDetector.fromDestination(BuildDestination.tvOSDevice)).toBe(Platform.tvOS);
30 |       expect(PlatformDetector.fromDestination(BuildDestination.tvOSSimulatorUniversal)).toBe(Platform.tvOS);
31 |     });
32 | 
33 |     it('should detect watchOS platform from watchOS destinations', () => {
34 |       // Arrange & Act & Assert
35 |       expect(PlatformDetector.fromDestination(BuildDestination.watchOSSimulator)).toBe(Platform.watchOS);
36 |       expect(PlatformDetector.fromDestination(BuildDestination.watchOSDevice)).toBe(Platform.watchOS);
37 |       expect(PlatformDetector.fromDestination(BuildDestination.watchOSSimulatorUniversal)).toBe(Platform.watchOS);
38 |     });
39 | 
40 |     it('should detect visionOS platform from visionOS destinations', () => {
41 |       // Arrange & Act & Assert
42 |       expect(PlatformDetector.fromDestination(BuildDestination.visionOSSimulator)).toBe(Platform.visionOS);
43 |       expect(PlatformDetector.fromDestination(BuildDestination.visionOSDevice)).toBe(Platform.visionOS);
44 |       expect(PlatformDetector.fromDestination(BuildDestination.visionOSSimulatorUniversal)).toBe(Platform.visionOS);
45 |     });
46 | 
47 |     it('should default to iOS for unknown destination patterns', () => {
48 |       // Arrange
49 |       const unknownDestination = 'unknownPlatform' as BuildDestination;
50 |       
51 |       // Act
52 |       const result = PlatformDetector.fromDestination(unknownDestination);
53 |       
54 |       // Assert
55 |       expect(result).toBe(Platform.iOS);
56 |     });
57 |   });
58 | });
```

--------------------------------------------------------------------------------
/src/features/app-management/tests/unit/InstallRequest.unit.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { describe, it, expect } from '@jest/globals';
 2 | import { InstallRequest } from '../../domain/InstallRequest.js';
 3 | 
 4 | describe('InstallRequest', () => {
 5 |   describe('create', () => {
 6 |     it('should create valid install request with simulator ID', () => {
 7 |       // Arrange & Act
 8 |       const request = InstallRequest.create(
 9 |         '/path/to/app.app',
10 |         'iPhone-15-Simulator'
11 |       );
12 |       
13 |       // Assert
14 |       expect(request.appPath.toString()).toBe('/path/to/app.app');
15 |       expect(request.simulatorId?.toString()).toBe('iPhone-15-Simulator');
16 |     });
17 | 
18 |     it('should create valid install request without simulator ID', () => {
19 |       // Arrange & Act
20 |       const request = InstallRequest.create(
21 |         '/path/to/app.app'
22 |       );
23 |       
24 |       // Assert
25 |       expect(request.appPath.toString()).toBe('/path/to/app.app');
26 |       expect(request.simulatorId).toBeUndefined();
27 |     });
28 | 
29 |     it('should reject empty app path', () => {
30 |       // Arrange & Act & Assert
31 |       expect(() => InstallRequest.create('', 'test-sim'))
32 |         .toThrow('App path cannot be empty');
33 |     });
34 | 
35 |     it('should reject whitespace-only app path', () => {
36 |       // Arrange & Act & Assert
37 |       expect(() => InstallRequest.create('   ', 'test-sim'))
38 |         .toThrow('App path cannot be empty');
39 |     });
40 | 
41 |     it('should reject invalid app extension', () => {
42 |       // Arrange & Act & Assert
43 |       expect(() => InstallRequest.create('/path/to/file.txt', 'test-sim'))
44 |         .toThrow('App path must end with .app');
45 |     });
46 | 
47 |     it('should accept .app bundle path', () => {
48 |       // Arrange & Act
49 |       const request = InstallRequest.create(
50 |         '/path/to/MyApp.app',
51 |         'test-sim'
52 |       );
53 |       
54 |       // Assert
55 |       expect(request.appPath.toString()).toBe('/path/to/MyApp.app');
56 |     });
57 | 
58 |     it('should trim whitespace from simulator ID', () => {
59 |       // Arrange & Act
60 |       const request = InstallRequest.create(
61 |         '/path/to/app.app',
62 |         '  test-sim  '
63 |       );
64 |       
65 |       // Assert
66 |       expect(request.simulatorId?.toString()).toBe('test-sim');
67 |     });
68 |   });
69 | 
70 |   describe('validation', () => {
71 |     it('should reject path traversal attempts', () => {
72 |       // Arrange & Act & Assert
73 |       expect(() => InstallRequest.create('../../../etc/passwd.app'))
74 |         .toThrow();
75 |     });
76 | 
77 |     it('should accept absolute paths', () => {
78 |       // Arrange & Act
79 |       const request = InstallRequest.create(
80 |         '/Users/developer/MyApp.app'
81 |       );
82 |       
83 |       // Assert
84 |       expect(request.appPath.toString()).toBe('/Users/developer/MyApp.app');
85 |     });
86 | 
87 |     it('should accept relative paths within project', () => {
88 |       // Arrange & Act
89 |       const request = InstallRequest.create(
90 |         './build/Debug/MyApp.app'
91 |       );
92 |       
93 |       // Assert
94 |       expect(request.appPath.toString()).toBe('./build/Debug/MyApp.app');
95 |     });
96 |   });
97 | });
```

--------------------------------------------------------------------------------
/src/features/simulator/tests/unit/ShutdownRequest.unit.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { describe, it, expect } from '@jest/globals';
 2 | import { ShutdownRequest } from '../../domain/ShutdownRequest.js';
 3 | import { DeviceId } from '../../../../shared/domain/DeviceId.js';
 4 | 
 5 | describe('ShutdownRequest', () => {
 6 |   describe('create', () => {
 7 |     it('should create a valid shutdown request with device ID', () => {
 8 |       // Arrange
 9 |       const deviceIdString = 'iPhone-15';
10 |       const deviceId = DeviceId.create(deviceIdString);
11 | 
12 |       // Act
13 |       const request = ShutdownRequest.create(deviceId);
14 | 
15 |       // Assert
16 |       expect(request.deviceId).toBe('iPhone-15');
17 |     });
18 | 
19 |     it('should trim whitespace from device ID', () => {
20 |       // Arrange
21 |       const deviceIdString = '  iPhone-15  ';
22 |       const deviceId = DeviceId.create(deviceIdString);
23 | 
24 |       // Act
25 |       const request = ShutdownRequest.create(deviceId);
26 | 
27 |       // Assert
28 |       expect(request.deviceId).toBe('iPhone-15');
29 |     });
30 | 
31 |     it('should accept UUID format device ID', () => {
32 |       // Arrange
33 |       const uuid = '550e8400-e29b-41d4-a716-446655440000';
34 |       const deviceId = DeviceId.create(uuid);
35 | 
36 |       // Act
37 |       const request = ShutdownRequest.create(deviceId);
38 | 
39 |       // Assert
40 |       expect(request.deviceId).toBe(uuid);
41 |     });
42 | 
43 |     it('should throw error for empty device ID', () => {
44 |       // Arrange & Act & Assert
45 |       expect(() => DeviceId.create('')).toThrow('Device ID cannot be empty');
46 |     });
47 | 
48 |     it('should throw error for null device ID', () => {
49 |       // Arrange & Act & Assert
50 |       expect(() => DeviceId.create(null as any)).toThrow('Device ID is required');
51 |     });
52 | 
53 |     it('should throw error for undefined device ID', () => {
54 |       // Arrange & Act & Assert
55 |       expect(() => DeviceId.create(undefined as any)).toThrow('Device ID is required');
56 |     });
57 | 
58 |     it('should throw error for whitespace-only device ID', () => {
59 |       // Arrange & Act & Assert
60 |       expect(() => DeviceId.create('   ')).toThrow('Device ID cannot be whitespace only');
61 |     });
62 | 
63 |     it('should be immutable', () => {
64 |       // Arrange
65 |       const deviceId = DeviceId.create('iPhone-15');
66 |       const request = ShutdownRequest.create(deviceId);
67 | 
68 |       // Act & Assert
69 |       expect(() => {
70 |         (request as any).deviceId = 'Changed';
71 |       }).toThrow();
72 |     });
73 | 
74 |     it('should handle device names with spaces', () => {
75 |       // Arrange
76 |       const deviceName = 'iPhone 15 Pro Max';
77 |       const deviceId = DeviceId.create(deviceName);
78 | 
79 |       // Act
80 |       const request = ShutdownRequest.create(deviceId);
81 | 
82 |       // Assert
83 |       expect(request.deviceId).toBe('iPhone 15 Pro Max');
84 |     });
85 | 
86 |     it('should handle device names with special characters', () => {
87 |       // Arrange
88 |       const deviceName = "John's iPhone (Work)";
89 |       const deviceId = DeviceId.create(deviceName);
90 | 
91 |       // Act
92 |       const request = ShutdownRequest.create(deviceId);
93 | 
94 |       // Assert
95 |       expect(request.deviceId).toBe("John's iPhone (Work)");
96 |     });
97 |   });
98 | });
```

--------------------------------------------------------------------------------
/src/features/simulator/controllers/ListSimulatorsController.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { ListSimulatorsUseCase } from '../use-cases/ListSimulatorsUseCase.js';
  2 | import { ListSimulatorsRequest } from '../domain/ListSimulatorsRequest.js';
  3 | import { SimulatorState } from '../domain/SimulatorState.js';
  4 | import { Platform } from '../../../shared/domain/Platform.js';
  5 | import { ErrorFormatter } from '../../../presentation/formatters/ErrorFormatter.js';
  6 | import { MCPController } from '../../../presentation/interfaces/MCPController.js';
  7 | 
  8 | /**
  9 |  * Controller for the list_simulators MCP tool
 10 |  *
 11 |  * Lists available simulators with optional filtering
 12 |  */
 13 | export class ListSimulatorsController implements MCPController {
 14 |   readonly name = 'list_simulators';
 15 |   readonly description = 'List available iOS simulators';
 16 | 
 17 |   constructor(
 18 |     private useCase: ListSimulatorsUseCase
 19 |   ) {}
 20 | 
 21 |   get inputSchema() {
 22 |     return {
 23 |       type: 'object' as const,
 24 |       properties: {
 25 |         platform: {
 26 |           type: 'string' as const,
 27 |           description: 'Filter by platform',
 28 |           enum: ['iOS', 'tvOS', 'watchOS', 'visionOS'] as const
 29 |         },
 30 |         state: {
 31 |           type: 'string' as const,
 32 |           description: 'Filter by simulator state',
 33 |           enum: ['Booted', 'Shutdown'] as const
 34 |         },
 35 |         name: {
 36 |           type: 'string' as const,
 37 |           description: 'Filter by device name (partial match, case-insensitive)'
 38 |         }
 39 |       },
 40 |       required: [] as const
 41 |     };
 42 |   }
 43 | 
 44 |   getToolDefinition() {
 45 |     return {
 46 |       name: this.name,
 47 |       description: this.description,
 48 |       inputSchema: this.inputSchema
 49 |     };
 50 |   }
 51 | 
 52 |   async execute(args: unknown): Promise<{ content: Array<{ type: string; text: string }> }> {
 53 |     try {
 54 |       // Cast to expected shape
 55 |       const input = args as { platform?: string; state?: string; name?: string };
 56 | 
 57 |       // Use the new validation functions
 58 |       const platform = Platform.parseOptional(input.platform);
 59 |       const state = SimulatorState.parseOptional(input.state);
 60 | 
 61 |       const request = ListSimulatorsRequest.create(platform, state, input.name);
 62 |       const result = await this.useCase.execute(request);
 63 | 
 64 |     if (!result.isSuccess) {
 65 |       return {
 66 |         content: [{
 67 |           type: 'text',
 68 |           text: `❌ ${ErrorFormatter.format(result.error!)}`
 69 |         }]
 70 |       };
 71 |     }
 72 | 
 73 |     if (result.count === 0) {
 74 |       return {
 75 |         content: [{
 76 |           type: 'text',
 77 |           text: '🔍 No simulators found'
 78 |         }]
 79 |       };
 80 |     }
 81 | 
 82 |     const lines: string[] = [
 83 |       `✅ Found ${result.count} simulator${result.count === 1 ? '' : 's'}`,
 84 |       ''
 85 |     ];
 86 | 
 87 |     for (const simulator of result.simulators) {
 88 |       lines.push(`• ${simulator.name} (${simulator.udid}) - ${simulator.state} - ${simulator.runtime}`);
 89 |     }
 90 | 
 91 |     return {
 92 |       content: [{
 93 |         type: 'text',
 94 |         text: lines.join('\n')
 95 |       }]
 96 |     };
 97 |     } catch (error) {
 98 |       return {
 99 |         content: [{
100 |           type: 'text',
101 |           text: `❌ ${ErrorFormatter.format(error as Error)}`
102 |         }]
103 |       };
104 |     }
105 |   }
106 | 
107 | }
```
Page 1/5FirstPrevNextLast