From e81096f8be6756ffc2376b92c74897ec27c90a5d Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Sat, 28 Feb 2026 22:23:59 +0000 Subject: [PATCH 1/3] feat(guidance): add prescriptive agent workflow instructions Replace generic server instructions with a structured simulator run flow protocol that prevents common waste patterns (speculative discovery, redundant boot/open calls, skipped session_show_defaults). Rewrite both skill files from exhaustive tool catalogs to concise workflow-oriented guidance that teaches decision-making over listing facts. Update tool manifest descriptions to embed workflow context at point of tool selection (boot_sim/open_sim not required before build_run_sim, session_show_defaults required before first build/run/test). --- AGENTS.md | 2 + docs/SKILLS.md | 2 + docs/TOOLS-CLI.md | 10 +- docs/TOOLS.md | 12 +- manifests/tools/boot_sim.yaml | 2 +- manifests/tools/build_run_sim.yaml | 2 +- manifests/tools/discover_projs.yaml | 9 +- manifests/tools/open_sim.yaml | 4 +- manifests/tools/session_show_defaults.yaml | 2 +- skills/xcodebuildmcp-cli/SKILL.md | 197 +++---------------- skills/xcodebuildmcp/SKILL.md | 214 +++------------------ src/server/server.ts | 7 +- 12 files changed, 92 insertions(+), 371 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 8bc172d4..11957ae7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -17,6 +17,8 @@ When reading issues: - ## Tools - GitHub CLI for issues/PRs +- When working on skill sources in `skills/`, use the `skill-creator` skill workflow. +- After modifying any skill source, run `npx skill-check ` and address all errors/warnings before handoff. - ## Style - Keep answers short and concise diff --git a/docs/SKILLS.md b/docs/SKILLS.md index 1fcc7556..5e635131 100644 --- a/docs/SKILLS.md +++ b/docs/SKILLS.md @@ -26,6 +26,8 @@ xcodebuildmcp init --remove-conflict # Auto-remove conflicting variant xcodebuildmcp init --uninstall # Remove installed skill ``` +When installing `--skill mcp` in auto-detect mode, Claude Code targets are skipped because Claude already receives MCP guidance through server instructions. If you explicitly set `--client claude`, MCP skill installation is still allowed. + ## Unsupported Clients For clients without a skills directory, print the skill content and pipe it to a file or paste it into your client's instructions area: diff --git a/docs/TOOLS-CLI.md b/docs/TOOLS-CLI.md index a806e763..54e36441 100644 --- a/docs/TOOLS-CLI.md +++ b/docs/TOOLS-CLI.md @@ -18,7 +18,7 @@ XcodeBuildMCP provides 73 canonical tools organized into 13 workflow groups. - `build` - Build for device. - `clean` - Clean build products. -- `discover-projects` - Scans a directory (defaults to workspace root) to find Xcode project (.xcodeproj) and workspace (.xcworkspace) files. +- `discover-projects` - Scans a directory (defaults to workspace root) to find Xcode project (.xcodeproj) and workspace (.xcworkspace) files. Use when project/workspace path is unknown. - `get-app-bundle-id` - Extract bundle id from .app. - `get-app-path` - Get device built app path. - `install` - Install app on device. @@ -38,7 +38,7 @@ XcodeBuildMCP provides 73 canonical tools organized into 13 workflow groups. - `boot` - Defined in Simulator Management workflow. - `build` - Build for iOS sim (compile-only, no launch). -- `build-and-run` - Build and run iOS sim (preferred for run/launch intent). +- `build-and-run` - Build, install, and launch on iOS Simulator; boots simulator and attempts to open Simulator.app as needed. Preferred single-step run tool when defaults are set. - `clean` - Defined in iOS Device Development workflow. - `discover-projects` - Defined in iOS Device Development workflow. - `get-app-bundle-id` - Defined in iOS Device Development workflow. @@ -130,10 +130,10 @@ XcodeBuildMCP provides 73 canonical tools organized into 13 workflow groups. ### Simulator Management (`simulator-management`) **Purpose**: Tools for managing simulators from booting, opening simulators, listing simulators, stopping simulators, erasing simulator content and settings, and setting simulator environment options like location, network, statusbar and appearance. (8 tools) -- `boot` - Boot iOS simulator. +- `boot` - Boot iOS simulator for manual/non-build flows. Not required before simulator build-and-run (build_run_sim). - `erase` - Erase simulator. - `list` - List iOS simulators. -- `open` - Open Simulator app. +- `open` - Open Simulator.app for visibility/manual workflows. Not required before simulator build-and-run (build_run_sim). - `reset-location` - Reset sim location. - `set-appearance` - Set sim appearance. - `set-location` - Set sim location. @@ -189,4 +189,4 @@ XcodeBuildMCP provides 73 canonical tools organized into 13 workflow groups. --- -*This documentation is automatically generated by `scripts/update-tools-docs.ts` from the tools manifest. Last updated: 2026-02-22T18:16:55.247Z UTC* +*This documentation is automatically generated by `scripts/update-tools-docs.ts` from the tools manifest. Last updated: 2026-02-28T20:48:25.650Z UTC* diff --git a/docs/TOOLS.md b/docs/TOOLS.md index c2e13a0e..6f7adaa5 100644 --- a/docs/TOOLS.md +++ b/docs/TOOLS.md @@ -16,7 +16,7 @@ This document lists MCP tool names as exposed to MCP clients. XcodeBuildMCP prov - `build_device` - Build for device. - `clean` - Clean build products. -- `discover_projs` - Scans a directory (defaults to workspace root) to find Xcode project (.xcodeproj) and workspace (.xcworkspace) files. +- `discover_projs` - Scans a directory (defaults to workspace root) to find Xcode project (.xcodeproj) and workspace (.xcworkspace) files. Use when project/workspace path is unknown. - `get_app_bundle_id` - Extract bundle id from .app. - `get_device_app_path` - Get device built app path. - `install_app_device` - Install app on device. @@ -35,7 +35,7 @@ This document lists MCP tool names as exposed to MCP clients. XcodeBuildMCP prov **Purpose**: Complete iOS development workflow for both .xcodeproj and .xcworkspace files targeting simulators. (21 tools) - `boot_sim` - Defined in Simulator Management workflow. -- `build_run_sim` - Build and run iOS sim (preferred for run/launch intent). +- `build_run_sim` - Build, install, and launch on iOS Simulator; boots simulator and attempts to open Simulator.app as needed. Preferred single-step run tool when defaults are set. - `build_sim` - Build for iOS sim (compile-only, no launch). - `clean` - Defined in iOS Device Development workflow. - `discover_projs` - Defined in iOS Device Development workflow. @@ -130,7 +130,7 @@ This document lists MCP tool names as exposed to MCP clients. XcodeBuildMCP prov - `session_clear_defaults` - Clear session defaults for the active profile or a specified profile. - `session_set_defaults` - Set session defaults for the active profile, or for a specified profile and make it active. -- `session_show_defaults` - Show the current active defaults. +- `session_show_defaults` - Show current active defaults. Required before your first build/run/test call in a session — do not assume defaults are configured. - `session_use_defaults_profile` - Switch the active session defaults profile. - `sync_xcode_defaults` - Sync session defaults (scheme, simulator) from Xcode's current IDE selection. @@ -139,10 +139,10 @@ This document lists MCP tool names as exposed to MCP clients. XcodeBuildMCP prov ### Simulator Management (`simulator-management`) **Purpose**: Tools for managing simulators from booting, opening simulators, listing simulators, stopping simulators, erasing simulator content and settings, and setting simulator environment options like location, network, statusbar and appearance. (8 tools) -- `boot_sim` - Boot iOS simulator. +- `boot_sim` - Boot iOS simulator for manual/non-build flows. Not required before simulator build-and-run (build_run_sim). - `erase_sims` - Erase simulator. - `list_sims` - List iOS simulators. -- `open_sim` - Open Simulator app. +- `open_sim` - Open Simulator.app for visibility/manual workflows. Not required before simulator build-and-run (build_run_sim). - `reset_sim_location` - Reset sim location. - `set_sim_appearance` - Set sim appearance. - `set_sim_location` - Set sim location. @@ -205,4 +205,4 @@ This document lists MCP tool names as exposed to MCP clients. XcodeBuildMCP prov --- -*This documentation is automatically generated by `scripts/update-tools-docs.ts` from the tools manifest. Last updated: 2026-02-22T18:16:55.247Z UTC* +*This documentation is automatically generated by `scripts/update-tools-docs.ts` from the tools manifest. Last updated: 2026-02-28T20:48:25.650Z UTC* diff --git a/manifests/tools/boot_sim.yaml b/manifests/tools/boot_sim.yaml index 22c2913f..78abbd28 100644 --- a/manifests/tools/boot_sim.yaml +++ b/manifests/tools/boot_sim.yaml @@ -3,7 +3,7 @@ module: mcp/tools/simulator/boot_sim names: mcp: boot_sim cli: boot -description: Boot iOS simulator. +description: Boot iOS simulator for manual/non-build flows. Not required before simulator build-and-run (build_run_sim). annotations: title: Boot Simulator destructiveHint: true diff --git a/manifests/tools/build_run_sim.yaml b/manifests/tools/build_run_sim.yaml index 153b2059..dd7fcbfa 100644 --- a/manifests/tools/build_run_sim.yaml +++ b/manifests/tools/build_run_sim.yaml @@ -3,7 +3,7 @@ module: mcp/tools/simulator/build_run_sim names: mcp: build_run_sim cli: build-and-run -description: Build and run iOS sim (preferred for run/launch intent). +description: Build, install, and launch on iOS Simulator; boots simulator and attempts to open Simulator.app as needed. Preferred single-step run tool when defaults are set. predicates: - hideWhenXcodeAgentMode annotations: diff --git a/manifests/tools/discover_projs.yaml b/manifests/tools/discover_projs.yaml index 4c2cf4c1..d8dbe9ec 100644 --- a/manifests/tools/discover_projs.yaml +++ b/manifests/tools/discover_projs.yaml @@ -3,7 +3,14 @@ module: mcp/tools/project-discovery/discover_projs names: mcp: discover_projs cli: discover-projects -description: Scans a directory (defaults to workspace root) to find Xcode project (.xcodeproj) and workspace (.xcworkspace) files. +description: Scans a directory (defaults to workspace root) to find Xcode project (.xcodeproj) and workspace (.xcworkspace) files. Use when project/workspace path is unknown. annotations: title: Discover Projects readOnlyHint: true +nextSteps: + - label: Save discovered project/workspace as session defaults + toolId: session_set_defaults + priority: 1 + - label: Build and run once defaults are set + toolId: build_run_sim + priority: 2 diff --git a/manifests/tools/open_sim.yaml b/manifests/tools/open_sim.yaml index ab388239..7d87ac75 100644 --- a/manifests/tools/open_sim.yaml +++ b/manifests/tools/open_sim.yaml @@ -3,12 +3,12 @@ module: mcp/tools/simulator/open_sim names: mcp: open_sim cli: open -description: Open Simulator app. +description: Open Simulator.app for visibility/manual workflows. Not required before simulator build-and-run (build_run_sim). annotations: title: Open Simulator destructiveHint: true nextSteps: - - label: Boot a simulator if needed + - label: Boot a simulator for manual workflows toolId: boot_sim params: simulatorId: UUID_FROM_LIST_SIMS diff --git a/manifests/tools/session_show_defaults.yaml b/manifests/tools/session_show_defaults.yaml index 60ab807d..ff304da9 100644 --- a/manifests/tools/session_show_defaults.yaml +++ b/manifests/tools/session_show_defaults.yaml @@ -3,7 +3,7 @@ module: mcp/tools/session-management/session_show_defaults names: mcp: session_show_defaults cli: show-defaults -description: Show the current active defaults. +description: Show current active defaults. Required before your first build/run/test call in a session — do not assume defaults are configured. annotations: title: Show Session Defaults readOnlyHint: true diff --git a/skills/xcodebuildmcp-cli/SKILL.md b/skills/xcodebuildmcp-cli/SKILL.md index e76fb452..f7ca6af4 100644 --- a/skills/xcodebuildmcp-cli/SKILL.md +++ b/skills/xcodebuildmcp-cli/SKILL.md @@ -5,193 +5,60 @@ description: Official skill for the XcodeBuildMCP CLI. Use when doing iOS/macOS/ # XcodeBuildMCP CLI -This skill is for AI agents. It positions the XcodeBuildMCP CLI as a low‑overhead alternative to MCP tool calls: agents can already run shell commands, and the CLI exposes the same tool surface without the schema‑exchange cost. Prefer the CLI over raw `xcodebuild`, `xcrun`, or `simctl`. +Use XcodeBuildMCP tools via the `xcodebuildmcp` executable instead of raw `xcodebuild`, `xcrun`, or `simctl`. -## When To Use This CLI (Capabilities And Workflows) - -- When you need build/test/run/debugging/logging/UI automation capabilities. -- When you want simulator/device management capabilities. -- When you want AI optimized tools and tool responses. -- When you need project discovery capabilities (schemes, bundle IDs, app paths). - -## Command Discovery - -Use `--help` to discover workflows, tools, and arguments. +## Step 1: Ensure the CLI Exists +Check availability: ```bash xcodebuildmcp --help -xcodebuildmcp tools --help -xcodebuildmcp tools --json -xcodebuildmcp --help -xcodebuildmcp --help -``` - -Notes: -- Use `--json '{...}'` for complex arguments and `--output json` if you need machine-readable results (not recommended). - -## Common Workflows - -### Build And Run On Simulator - -If your intent is to run the app in Simulator, use `build-and-run` directly. It already performs the build step. -Do not run `build` first unless the user explicitly requests both commands. - -1. List simulators and pick a device name or UDID. -2. Build and run. - -If app and project details are not known: -```bash -xcodebuildmcp simulator discover-projects --workspace-root . -xcodebuildmcp simulator list-schemes --project-path ./MyApp.xcodeproj -xcodebuildmcp simulator list ``` -To build, install and launch the app in one command: +If missing, install with one of: ```bash -xcodebuildmcp simulator build-and-run --scheme MyApp --project-path ./MyApp.xcodeproj --simulator-name "iPhone 17 Pro" +brew tap getsentry/xcodebuildmcp +brew install xcodebuildmcp ``` -### Build only - -Use this only when you want compile feedback and do not want to launch the app. -For run/launch intent, use `build-and-run` instead of chaining `build` and `build-and-run`. - ```bash -xcodebuildmcp simulator build --scheme MyApp --project-path ./MyApp.xcodeproj --simulator-name "iPhone 17 Pro" +npm install -g xcodebuildmcp@latest ``` -### Run Tests - -When you need to run tests, you can do so with the `test` tool. - +Re-check after install: ```bash -xcodebuildmcp simulator test --scheme MyAppTests --project-path ./MyApp.xcodeproj --simulator-name "iPhone 17 Pro" -``` - -### Install And Launch On Physical Device - -```bash -xcodebuildmcp device list -xcodebuildmcp device build --scheme MyApp --project-path ./MyApp.xcodeproj -xcodebuildmcp device get-app-path --scheme MyApp --project-path ./MyApp.xcodeproj -xcodebuildmcp device get-app-bundle-id --app-path /path/to/MyApp.app -xcodebuildmcp device install --device-id DEVICE_UDID --app-path /path/to/MyApp.app -xcodebuildmcp device launch --device-id DEVICE_UDID --bundle-id io.sentry.MyApp -``` - -### Capture Logs On Simulator - -```bash -xcodebuildmcp logging start-simulator-log-capture --simulator-id SIMULATOR_UDID --bundle-id io.sentry.MyApp -xcodebuildmcp logging stop-simulator-log-capture --log-session-id LOG_SESSION_ID -``` - -### Debug A Running App (Simulator) - -1. Launch the app. -2. Attach the debugger after the app is fully launched. - -Launch if not already running: -```bash -xcodebuildmcp simulator launch-app --bundle-id io.sentry.MyApp --simulator-id SIMULATOR_UDID -``` - -Attach the debugger: - -It's generally a good idea to wait for 1-2s for the app to fully launch before attaching the debugger. - -```bash -xcodebuildmcp debugging attach --bundle-id io.sentry.MyApp --simulator-id SIMULATOR_UDID -``` - -To add/remove breakpoints, inspect stack/variables, and issue arbitrary LLDB commands, view debugging help: -```bash -xcodebuildmcp debugging --help -``` - - -### Inspect UI And Automate Input - -Snapshot UI accessibility tree, tap/swipe/type, and capture screenshots: - -```bash -xcodebuildmcp ui-automation snapshot-ui --simulator-id SIMULATOR_UDID -xcodebuildmcp ui-automation tap --simulator-id SIMULATOR_UDID --label "Submit" -xcodebuildmcp ui-automation tap --simulator-id SIMULATOR_UDID --id "SubmitButton" -# Coordinate fallback when label/id is unavailable -xcodebuildmcp ui-automation tap --simulator-id SIMULATOR_UDID --x 200 --y 400 -xcodebuildmcp ui-automation type-text --simulator-id SIMULATOR_UDID --text "hello" -xcodebuildmcp ui-automation screenshot --simulator-id SIMULATOR_UDID --return-format path -``` - -To see all UI automation tools, view UI automation help: -```bash -xcodebuildmcp ui-automation --help -``` - -### macOS App Build/Run - -```bash -xcodebuildmcp macos build --scheme MyMacApp --project-path ./MyMacApp.xcodeproj -xcodebuildmcp macos build-and-run --scheme MyMacApp --project-path ./MyMacApp.xcodeproj -``` - -To see all macOS tools, view macOS help: -```bash -xcodebuildmcp macos --help -``` - -### SwiftPM Package Workflows - -```bash -xcodebuildmcp swift-package list -xcodebuildmcp swift-package build --package-path ./MyPackage -xcodebuildmcp swift-package test --package-path ./MyPackage -``` - -To see all SwiftPM tools, view SwiftPM help: -```bash -xcodebuildmcp swift-package --help +xcodebuildmcp --help ``` -### Project Discovery +## Step 2: Use Help-First Discovery +Discover workflows and arguments from the CLI itself: ```bash -xcodebuildmcp project-discovery discover-projects --workspace-root . -xcodebuildmcp project-discovery list-schemes --project-path ./MyApp.xcodeproj -xcodebuildmcp project-discovery get-app-bundle-id --app-path ./Build/MyApp.app +xcodebuildmcp --help +xcodebuildmcp tools +xcodebuildmcp --help +xcodebuildmcp --help ``` -To see all project discovery tools, view project discovery help: -```bash -xcodebuildmcp project-discovery --help -``` +Use this discovery path instead of memorizing static tool lists. -### Scaffolding new projects +## Step 3: Keep Execution Minimal -It's worth viewing the --help for the scaffolding tools to see the available options. -Here are some minimal examples: +- Choose the smallest command sequence that satisfies the request. +- Prefer direct workflow commands over manual multi-step chains unless explicitly requested. +- For simulator run intent, prefer the combined `build-and-run` command. +- Do not chain `build` then `build-and-run` unless explicitly requested. -```bash -xcodebuildmcp project-scaffolding scaffold-ios --project-name MyApp --output-path ./Projects -xcodebuildmcp project-scaffolding scaffold-macos --project-name MyMacApp --output-path ./Projects -``` +## Capability Overview -To see all project scaffolding tools, view project scaffolding help: -```bash -xcodebuildmcp project-scaffolding --help -``` +`xcodebuildmcp` supports: +- simulator and device build/test/run +- debugging and log capture +- UI automation +- project discovery and scaffolding +- session defaults and workflow configuration -## Daemon Notes (Stateful Tools) +## Exit Criteria -Stateful tools (logs, debug, video recording, background run) go through a per-workspace daemon that auto-starts, if you find you are getting errors with the stateful tools, you can manage the daemon process manually. - -```bash -xcodebuildmcp daemon status -xcodebuildmcp daemon restart -``` - -To see all daemon commands, view daemon help: -```bash -xcodebuildmcp daemon --help -``` +- CLI presence is verified or installation steps are provided. +- Commands are discovered via `--help` / `tools`. +- Session defaults are checked before first build/run/test action. diff --git a/skills/xcodebuildmcp/SKILL.md b/skills/xcodebuildmcp/SKILL.md index f5ff3f1b..86764097 100644 --- a/skills/xcodebuildmcp/SKILL.md +++ b/skills/xcodebuildmcp/SKILL.md @@ -5,199 +5,37 @@ description: Official skill for XcodeBuildMCP. Use when doing iOS/macOS/watchOS/ # XcodeBuildMCP -Prefer XcodeBuildMCP over raw `xcodebuild`, `xcrun`, or `simctl`. +Use XcodeBuildMCP tools instead of raw `xcodebuild`, `xcrun`, or `simctl`. -If a capability is missing, assume your tool list may be hiding tools (search/progressive disclosure) or not loading tool schemas yet. Use your tool-search or “load tools” mechanism. If you still can’t find the tools, ask the user to enable them in the MCP client's configuration. +Capabilities: +- Session defaults: Configure project, scheme, simulator, and device defaults to avoid repetitive parameters +- Project discovery: Find Xcode projects/workspaces, list schemes, inspect build settings +- Simulator workflows: Build, run, test, install, and launch apps on iOS simulators; manage simulator state (boot, erase, location, appearance) +- Device workflows: Build, test, install, and launch apps on physical devices with code signing +- macOS workflows: Build, run, and test macOS applications +- Log capture: Stream and capture logs from simulators and devices +- LLDB debugging: Attach debugger, set breakpoints, inspect stack traces and variables, execute LLDB commands +- UI automation: Capture screenshots, inspect view hierarchy with coordinates, perform taps/swipes/gestures, type text, press hardware buttons +- SwiftPM: Build, run, test, and manage Swift Package Manager projects +- Project scaffolding: Generate new iOS/macOS project templates -## Default Tool Choice (Simulator) +Only simulator workflow tools are enabled by default. If capabilities like device, macOS, debugging, or UI automation are not available, the user must configure XcodeBuildMCP to enable them. See https://github.com/getsentry/XcodeBuildMCP/blob/main/docs/CONFIGURATION.md for workflow configuration. -- If intent includes run/launch/open in Simulator, use `build_run_sim` as the default. -- If intent is compile-only feedback (no launch), use `build_sim`. -- Do not call `build_sim` and then `build_run_sim` in sequence unless the user explicitly asks for both. -- If the app is already built and you need launch only without rebuilding, use `install_app_sim` + `launch_app_sim` (or `launch_app_logs_sim`). +## Step 1: Establish Session Context -## Tools (exact names + official descriptions) +- Call `session_show_defaults` before the first build/run/test action in a session. +- Use `discover_projs` only when defaults show missing or incorrect project/workspace context. +- Do not run discovery speculatively or in parallel with `session_show_defaults`. +- For simulator run intent, prefer the combined build-and-run tool instead of separate build then run calls. +- Do not chain build-only then build-and-run unless the user explicitly requests both. -### Session defaults +## Step 2: Understand Workflow-Scoped Tool Availability -Before you call any other tools, you **must** call `session_show_defaults` to show the current defaults, then fill in any missing defaults. You may need discovery/list tools first to obtain valid values. +- Not all tools are enabled by default; tool availability depends on enabled workflows. +- If a tool is expected but missing, check enabled workflows first. +- Update enabled workflows in `.xcodebuildmcp/config.yaml`, then ask user to reload/restart the session to surface refreshes. -- `session_show_defaults` - - Show the current active defaults (including the active profile name). -- `session_set_defaults` - - Set defaults for the current active profile, or set defaults for a specific profile via `profile`. -- `session_use_defaults_profile` - - Switch the active defaults profile. -- `session_clear_defaults` - - Clear defaults (current active profile by default, or a specific profile when provided). +## Step 3: Report Context Clearly -### Project discovery - -- `discover_projs` - - Scans a directory (defaults to workspace root) to find Xcode project (.xcodeproj) and workspace (.xcworkspace) files. -- `list_schemes` - - List Xcode schemes. -- `show_build_settings` - - Show build settings. -- `get_app_bundle_id` - - Extract bundle id from .app. -- `get_mac_bundle_id` - - Extract bundle id from macOS .app. - -### Simulator - -- `boot_sim` - - Boot iOS simulator. -- `list_sims` - - List iOS simulators. -- `open_sim` - - Open Simulator app. -- `build_sim` - - Build for iOS sim. -- `build_run_sim` - - Build and run iOS sim. -- `test_sim` - - Test on iOS sim. -- `get_sim_app_path` - - Get sim built app path. -- `install_app_sim` - - Install app on sim. -- `launch_app_sim` - - Launch app on simulator. -- `launch_app_logs_sim` - - Launch sim app with logs. -- `stop_app_sim` - - Stop sim app. -- `record_sim_video` - - Record sim video. - -### Simulator management - -- `erase_sims` - - Erase simulator. -- `set_sim_location` - - Set sim location. -- `reset_sim_location` - - Reset sim location. -- `set_sim_appearance` - - Set sim appearance. -- `sim_statusbar` - - Set sim status bar network. - -### Device - -- `list_devices` - - List connected devices. -- `build_device` - - Build for device. -- `test_device` - - Test on device. -- `get_device_app_path` - - Get device built app path. -- `install_app_device` - - Install app on device. -- `launch_app_device` - - Launch app on device. -- `stop_app_device` - - Stop device app. - -### macOS - -- `build_macos` - - Build macOS app. -- `build_run_macos` - - Build and run macOS app. -- `test_macos` - - Test macOS target. -- `get_mac_app_path` - - Get macOS built app path. -- `launch_mac_app` - - Launch macOS app. -- `stop_mac_app` - - Stop macOS app. - -### Logging - -- `start_device_log_cap` - - Start device log capture. -- `start_sim_log_cap` - - Start sim log capture. -- `stop_device_log_cap` - - Stop device log capture. -- `stop_sim_log_cap` - - Stop sim log capture. - -### Debugging - -- `debug_attach_sim` - - Attach LLDB to sim app. -- `debug_breakpoint_add` - - Add breakpoint. -- `debug_breakpoint_remove` - - Remove breakpoint. -- `debug_continue` - - Continue debug session. -- `debug_detach` - - Detach debugger. -- `debug_lldb_command` - - Run LLDB command. -- `debug_stack` - - Get backtrace. -- `debug_variables` - - Get frame variables. - -### UI automation - -- `button` - - Press simulator hardware button. -- `gesture` - - Simulator gesture preset. -- `key_press` - - Press key by keycode. -- `key_sequence` - - Press a sequence of keys by their keycodes. -- `long_press` - - Long press at coords. -- `screenshot` - - Capture screenshot. -- `snapshot_ui` - - Print view hierarchy with element ids/labels and precise coordinates (x, y, width, height) for visible elements. -- `swipe` - - Swipe between points. -- `tap` - - Tap UI element by accessibility id/label (recommended) or coordinates as fallback. -- `touch` - - Touch down/up at coords. -- `type_text` - - Type text. - -### SwiftPM - -- `swift_package_build` - - swift package target build. -- `swift_package_clean` - - swift package clean. -- `swift_package_list` - - List SwiftPM processes. -- `swift_package_run` - - swift package target run. -- `swift_package_stop` - - Stop SwiftPM run. -- `swift_package_test` - - Run swift package target tests. - -### Scaffolding / utilities - -- `scaffold_ios_project` - - Scaffold iOS project. -- `scaffold_macos_project` - - Scaffold macOS project. -- `clean` - - Clean build products. - -### Diagnostics - -- `doctor` - - MCP environment info. -- `manage_workflows` - - Workflows are groups of tools exposed by XcodeBuildMCP. By default, not all workflows (and therefore tools) are enabled; only simulator tools are enabled by default. Some workflows are mandatory and can't be disabled. +- Return the active defaults context used for execution (project/workspace, scheme, simulator/device). +- For failures, include the exact failing step and the next actionable command/tool call. diff --git a/src/server/server.ts b/src/server/server.ts index a880da63..b132b728 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -44,7 +44,12 @@ Capabilities: Only simulator workflow tools are enabled by default. If capabilities like device, macOS, debugging, or UI automation are not available, the user must configure XcodeBuildMCP to enable them. See https://github.com/getsentry/XcodeBuildMCP/blob/main/docs/CONFIGURATION.md for workflow configuration. -Always start by calling session_show_defaults to see current configuration, then use discovery tools to find projects and set appropriate defaults.`, +Simulator run flow: +- Before your first build, run, or test call in a session, you MUST call session_show_defaults to verify the active project/workspace, scheme, and simulator. Do not assume defaults are configured. Only skip this if you have already called session_show_defaults earlier in the current session. +- If session_show_defaults confirms project/workspace + scheme + simulator are set, call build_run_sim immediately (often with empty arguments). +- Use discover_projs only when session_show_defaults shows project/workspace is missing or wrong. +- Never call discover_projs speculatively or in parallel with session_show_defaults. +- Do not call boot_sim or open_sim as prerequisites for build_run_sim; build_run_sim boots and opens Simulator as needed.`, capabilities: { tools: { listChanged: true, From eea275ab12f92a75717e2905303af47af0253aa5 Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Sat, 28 Feb 2026 22:24:27 +0000 Subject: [PATCH 2/3] feat(cli): add AGENTS.md guidance and Claude MCP install policy to init The init command now creates or updates a project-level AGENTS.md with XcodeBuildMCP skill usage guidance after installing a skill. Handles legacy guidance line replacement, shows a diff preview, and prompts for confirmation (or requires --force in non-interactive mode). Skip Claude Code when installing MCP skills in auto-detect mode since Claude already receives server instructions via the MCP protocol. An explicit --client claude flag overrides the policy. --- example_projects/iOS_Calculator/AGENTS.md | 3 + src/cli.ts | 2 +- src/cli/commands/__tests__/init.test.ts | 177 ++++++++++++++++++++++ src/cli/commands/init.ts | 137 +++++++++++++++-- src/cli/yargs-app.ts | 2 +- 5 files changed, 307 insertions(+), 14 deletions(-) create mode 100644 example_projects/iOS_Calculator/AGENTS.md diff --git a/example_projects/iOS_Calculator/AGENTS.md b/example_projects/iOS_Calculator/AGENTS.md new file mode 100644 index 00000000..1a9b266e --- /dev/null +++ b/example_projects/iOS_Calculator/AGENTS.md @@ -0,0 +1,3 @@ +# AGENTS.md + +- If using XcodeBuildMCP, use the installed XcodeBuildMCP skill before calling XcodeBuildMCP tools. diff --git a/src/cli.ts b/src/cli.ts index ea594bbe..d630f016 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -63,7 +63,7 @@ async function runInitCommand(): Promise { setLogLevel(level); } }); - registerInitCommand(app); + registerInitCommand(app, { workspaceRoot: process.cwd() }); await app.parseAsync(); } diff --git a/src/cli/commands/__tests__/init.test.ts b/src/cli/commands/__tests__/init.test.ts index 8c95203b..b1da49ea 100644 --- a/src/cli/commands/__tests__/init.test.ts +++ b/src/cli/commands/__tests__/init.test.ts @@ -20,6 +20,10 @@ import { homedir } from 'node:os'; const mockedGetResourceRoot = vi.mocked(getResourceRoot); const mockedHomedir = vi.mocked(homedir); +const agentsGuidanceLine = + '- If using XcodeBuildMCP, use the installed XcodeBuildMCP skill before calling XcodeBuildMCP tools.'; +const legacyAgentsGuidanceLine = + '- If using XcodeBuildMCP, first find and read the installed XcodeBuildMCP skill before calling XcodeBuildMCP tools.'; function loadInitModule() { return import('../init.ts'); @@ -126,6 +130,58 @@ describe('init command', () => { stdoutSpy.mockRestore(); }); + + it('skips Claude for MCP skill in auto-detect mode', async () => { + const fakeHome = join(tempDir, 'home-auto-skip-claude'); + mkdirSync(join(fakeHome, '.claude'), { recursive: true }); + mkdirSync(join(fakeHome, '.cursor'), { recursive: true }); + mockedHomedir.mockReturnValue(fakeHome); + + const yargs = (await import('yargs')).default; + const mod = await loadInitModule(); + + const app = yargs(['init', '--skill', 'mcp']).scriptName(''); + mod.registerInitCommand(app); + + const stdoutSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true); + await app.parseAsync(); + + expect(existsSync(join(fakeHome, '.claude', 'skills', 'xcodebuildmcp', 'SKILL.md'))).toBe( + false, + ); + expect(existsSync(join(fakeHome, '.cursor', 'skills', 'xcodebuildmcp', 'SKILL.md'))).toBe( + true, + ); + + const output = stdoutSpy.mock.calls.map((c) => String(c[0])).join(''); + expect(output).toContain('Skipped Claude Code'); + + stdoutSpy.mockRestore(); + }); + + it('allows explicit Claude MCP install with --client claude', async () => { + const fakeHome = join(tempDir, 'home-explicit-claude'); + mkdirSync(join(fakeHome, '.claude'), { recursive: true }); + mockedHomedir.mockReturnValue(fakeHome); + + const yargs = (await import('yargs')).default; + const mod = await loadInitModule(); + + const app = yargs(['init', '--client', 'claude', '--skill', 'mcp']).scriptName(''); + mod.registerInitCommand(app); + + const stdoutSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true); + await app.parseAsync(); + + expect(existsSync(join(fakeHome, '.claude', 'skills', 'xcodebuildmcp', 'SKILL.md'))).toBe( + true, + ); + + const output = stdoutSpy.mock.calls.map((c) => String(c[0])).join(''); + expect(output).not.toContain('Skipped Claude Code'); + + stdoutSpy.mockRestore(); + }); }); describe('conflict handling', () => { @@ -320,6 +376,127 @@ describe('init command', () => { }); }); + describe('AGENTS.md guidance on skill install', () => { + it('creates project-level AGENTS.md when missing', async () => { + const dest = join(tempDir, 'skills'); + const projectRoot = join(tempDir, 'project-create'); + mkdirSync(dest, { recursive: true }); + mkdirSync(projectRoot, { recursive: true }); + + const yargs = (await import('yargs')).default; + const mod = await loadInitModule(); + + const app = yargs(['init', '--dest', dest, '--skill', 'cli']).scriptName(''); + mod.registerInitCommand(app, { workspaceRoot: projectRoot }); + + const stdoutSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true); + await app.parseAsync(); + + const agentsPath = join(projectRoot, 'AGENTS.md'); + expect(existsSync(agentsPath)).toBe(true); + expect(readFileSync(agentsPath, 'utf8')).toContain(agentsGuidanceLine); + + const output = stdoutSpy.mock.calls.map((c) => String(c[0])).join(''); + expect(output).toContain('Created AGENTS.md with XcodeBuildMCP guidance'); + + stdoutSpy.mockRestore(); + }); + + it('shows diff and errors in non-interactive mode when AGENTS.md exists and --force is not set', async () => { + const dest = join(tempDir, 'skills'); + const projectRoot = join(tempDir, 'project-non-interactive'); + mkdirSync(dest, { recursive: true }); + mkdirSync(projectRoot, { recursive: true }); + writeFileSync(join(projectRoot, 'AGENTS.md'), '# Existing\n', 'utf8'); + + const originalIsTTY = process.stdin.isTTY; + Object.defineProperty(process.stdin, 'isTTY', { value: false, configurable: true }); + + const yargs = (await import('yargs')).default; + const mod = await loadInitModule(); + + const app = yargs(['init', '--dest', dest, '--skill', 'cli']).scriptName('').fail(false); + mod.registerInitCommand(app, { workspaceRoot: projectRoot }); + + const stdoutSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true); + await expect(app.parseAsync()).rejects.toThrow( + 'AGENTS.md exists and requires confirmation to update', + ); + + const output = stdoutSpy.mock.calls.map((c) => String(c[0])).join(''); + expect(output).toContain('Proposed update for'); + expect(output).toContain('--- AGENTS.md'); + expect(output).toContain('+++ AGENTS.md'); + expect(output).toContain(agentsGuidanceLine); + + stdoutSpy.mockRestore(); + Object.defineProperty(process.stdin, 'isTTY', { value: originalIsTTY, configurable: true }); + }); + + it('updates existing AGENTS.md with --force without prompting', async () => { + const dest = join(tempDir, 'skills'); + const projectRoot = join(tempDir, 'project-force'); + mkdirSync(dest, { recursive: true }); + mkdirSync(projectRoot, { recursive: true }); + writeFileSync(join(projectRoot, 'AGENTS.md'), '# Existing\n', 'utf8'); + + const originalIsTTY = process.stdin.isTTY; + Object.defineProperty(process.stdin, 'isTTY', { value: false, configurable: true }); + + const yargs = (await import('yargs')).default; + const mod = await loadInitModule(); + + const app = yargs(['init', '--dest', dest, '--skill', 'cli', '--force']).scriptName(''); + mod.registerInitCommand(app, { workspaceRoot: projectRoot }); + + const stdoutSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true); + await app.parseAsync(); + + const agentsContent = readFileSync(join(projectRoot, 'AGENTS.md'), 'utf8'); + expect(agentsContent).toContain('# Existing'); + expect(agentsContent).toContain(agentsGuidanceLine); + + const output = stdoutSpy.mock.calls.map((c) => String(c[0])).join(''); + expect(output).toContain('Proposed update for'); + expect(output).toContain('Updated AGENTS.md at'); + + stdoutSpy.mockRestore(); + Object.defineProperty(process.stdin, 'isTTY', { value: originalIsTTY, configurable: true }); + }); + + it('replaces legacy XcodeBuildMCP guidance line without appending duplicate', async () => { + const dest = join(tempDir, 'skills'); + const projectRoot = join(tempDir, 'project-legacy-guidance'); + mkdirSync(dest, { recursive: true }); + mkdirSync(projectRoot, { recursive: true }); + writeFileSync( + join(projectRoot, 'AGENTS.md'), + `# Existing\n\n${legacyAgentsGuidanceLine}\n`, + 'utf8', + ); + + const yargs = (await import('yargs')).default; + const mod = await loadInitModule(); + + const app = yargs(['init', '--dest', dest, '--skill', 'cli']).scriptName(''); + mod.registerInitCommand(app, { workspaceRoot: projectRoot }); + + const stdoutSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true); + await app.parseAsync(); + + const agentsContent = readFileSync(join(projectRoot, 'AGENTS.md'), 'utf8'); + expect(agentsContent).toContain(agentsGuidanceLine); + expect(agentsContent).not.toContain(legacyAgentsGuidanceLine); + expect( + agentsContent.match( + new RegExp(agentsGuidanceLine.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), + )?.length, + ).toBe(1); + + stdoutSpy.mockRestore(); + }); + }); + describe('error cases', () => { it('errors when --dest points to filesystem root', async () => { const rootDest = '/'; diff --git a/src/cli/commands/init.ts b/src/cli/commands/init.ts index dcd3a27f..96357730 100644 --- a/src/cli/commands/init.ts +++ b/src/cli/commands/init.ts @@ -19,6 +19,12 @@ const CLIENT_DEFINITIONS: { id: string; name: string; skillsSubdir: string }[] = { id: 'codex', name: 'Codex', skillsSubdir: '.codex/skills/public' }, ]; +const AGENTS_FILE_NAME = 'AGENTS.md'; +const AGENTS_LEGACY_GUIDANCE_LINE = + '- If using XcodeBuildMCP, first find and read the installed XcodeBuildMCP skill before calling XcodeBuildMCP tools.'; +const AGENTS_GUIDANCE_LINE = + '- If using XcodeBuildMCP, use the installed XcodeBuildMCP skill before calling XcodeBuildMCP tools.'; + function writeLine(text: string): void { process.stdout.write(`${text}\n`); } @@ -89,6 +95,11 @@ interface InstallResult { location: string; } +interface InstallPolicyResult { + allowedTargets: ClientInfo[]; + skippedClients: Array<{ client: string; reason: string }>; +} + async function installSkill( skillsDir: string, clientName: string, @@ -197,7 +208,66 @@ function resolveTargets( return detected; } -export function registerInitCommand(app: Argv): void { +function renderAgentsAppendDiff(fileName: string): string { + return `--- ${fileName}\n+++ ${fileName}\n@@\n+${AGENTS_GUIDANCE_LINE}`; +} + +async function ensureAgentsGuidance( + projectRoot: string, + force: boolean, +): Promise<'created' | 'updated' | 'no_change' | 'skipped'> { + const agentsPath = path.join(projectRoot, AGENTS_FILE_NAME); + if (!fs.existsSync(agentsPath)) { + const newContent = `# ${AGENTS_FILE_NAME}\n\n${AGENTS_GUIDANCE_LINE}\n`; + fs.writeFileSync(agentsPath, newContent, 'utf8'); + writeLine(`Created ${AGENTS_FILE_NAME} with XcodeBuildMCP guidance at ${agentsPath}`); + return 'created'; + } + + const currentContent = fs.readFileSync(agentsPath, 'utf8'); + if (currentContent.includes(AGENTS_GUIDANCE_LINE)) { + writeLine(`${AGENTS_FILE_NAME} already includes XcodeBuildMCP guidance.`); + return 'no_change'; + } + + if (currentContent.includes(AGENTS_LEGACY_GUIDANCE_LINE)) { + const updatedFromLegacy = currentContent.replace( + AGENTS_LEGACY_GUIDANCE_LINE, + AGENTS_GUIDANCE_LINE, + ); + fs.writeFileSync(agentsPath, updatedFromLegacy, 'utf8'); + writeLine(`Updated ${AGENTS_FILE_NAME} at ${agentsPath}`); + return 'updated'; + } + + const diff = renderAgentsAppendDiff(AGENTS_FILE_NAME); + writeLine(`Proposed update for ${agentsPath}:`); + writeLine(diff); + + if (!force) { + if (!process.stdin.isTTY) { + throw new Error( + `${AGENTS_FILE_NAME} exists and requires confirmation to update. Re-run with --force to apply the change in non-interactive mode.`, + ); + } + + const confirmed = await promptYesNo(`Update ${AGENTS_FILE_NAME} with the guidance above?`); + if (!confirmed) { + writeLine(`Skipped updating ${AGENTS_FILE_NAME}.`); + return 'skipped'; + } + } + + const updatedContent = currentContent.endsWith('\n') + ? `${currentContent}${AGENTS_GUIDANCE_LINE}\n` + : `${currentContent}\n${AGENTS_GUIDANCE_LINE}\n`; + + fs.writeFileSync(agentsPath, updatedContent, 'utf8'); + writeLine(`Updated ${AGENTS_FILE_NAME} at ${agentsPath}`); + return 'updated'; +} + +export function registerInitCommand(app: Argv, ctx?: { workspaceRoot: string }): void { app.command( 'init', 'Install XcodeBuildMCP agent skill', @@ -242,6 +312,8 @@ export function registerInitCommand(app: Argv): void { }, async (argv) => { const skillType = argv.skill as SkillType; + const clientFlag = argv.client as string | undefined; + const destFlag = argv.dest as string | undefined; if (argv.print) { const content = readSkillContent(skillType); @@ -250,11 +322,7 @@ export function registerInitCommand(app: Argv): void { } if (argv.uninstall) { - const targets = resolveTargets( - argv.client as string | undefined, - argv.dest as string | undefined, - 'uninstall', - ); + const targets = resolveTargets(clientFlag, destFlag, 'uninstall'); let anyRemoved = false; for (const target of targets) { @@ -277,14 +345,15 @@ export function registerInitCommand(app: Argv): void { return; } - const targets = resolveTargets( - argv.client as string | undefined, - argv.dest as string | undefined, - 'install', - ); + const targets = resolveTargets(clientFlag, destFlag, 'install'); + + const policy = enforceInstallPolicy(targets, skillType, clientFlag, destFlag); + if (policy.allowedTargets.length === 0) { + throw new Error('No eligible install targets after applying skill policy.'); + } const results: InstallResult[] = []; - for (const target of targets) { + for (const target of policy.allowedTargets) { const result = await installSkill(target.skillsDir, target.name, skillType, { force: argv.force as boolean, removeConflict: argv['remove-conflict'] as boolean, @@ -292,11 +361,55 @@ export function registerInitCommand(app: Argv): void { results.push(result); } + for (const skipped of policy.skippedClients) { + writeLine(`Skipped ${skipped.client}: ${skipped.reason}`); + } + writeLine(`Installed ${skillDisplayName(skillType)} skill`); for (const result of results) { writeLine(` Client: ${result.client}`); writeLine(` Location: ${result.location}`); } + + if (ctx?.workspaceRoot) { + const projectRoot = path.resolve(ctx.workspaceRoot); + await ensureAgentsGuidance(projectRoot, argv.force as boolean); + } }, ); } + +function enforceInstallPolicy( + targets: ClientInfo[], + skillType: SkillType, + clientFlag: string | undefined, + destFlag: string | undefined, +): InstallPolicyResult { + if (skillType !== 'mcp') { + return { allowedTargets: targets, skippedClients: [] }; + } + + if (destFlag) { + return { allowedTargets: targets, skippedClients: [] }; + } + + const allowedTargets: ClientInfo[] = []; + const skippedClients: Array<{ client: string; reason: string }> = []; + + for (const target of targets) { + if (target.id === 'claude') { + skippedClients.push({ + client: target.name, + reason: 'MCP skill is unnecessary because Claude Code already uses server instructions.', + }); + continue; + } + allowedTargets.push(target); + } + + if (clientFlag === 'claude') { + return { allowedTargets: targets, skippedClients: [] }; + } + + return { allowedTargets, skippedClients }; +} diff --git a/src/cli/yargs-app.ts b/src/cli/yargs-app.ts index 5420b3ad..0d87012e 100644 --- a/src/cli/yargs-app.ts +++ b/src/cli/yargs-app.ts @@ -71,7 +71,7 @@ export function buildYargsApp(opts: YargsAppOptions): ReturnType { // Register command groups with workspace context registerMcpCommand(app); - registerInitCommand(app); + registerInitCommand(app, { workspaceRoot: opts.workspaceRoot }); registerToolsCommand(app); registerToolCommands(app, opts.catalog, { workspaceRoot: opts.workspaceRoot, From f9f798990e7a053a20520ea466bfb4e5ac7c12ba Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Sat, 28 Feb 2026 22:24:51 +0000 Subject: [PATCH 3/3] chore: update default simulator reference from iPhone 16 to iPhone 17 Update all tool parameter descriptions, next-step suggestions, config examples, documentation, and test fixtures to reference iPhone 17 as the default simulator name, matching the current Xcode release. --- CHANGELOG.md | 2 +- config.example.yaml | 2 +- docs/CONFIGURATION.md | 6 +-- docs/SESSION_DEFAULTS.md | 2 +- src/mcp/tools/debugging/debug_attach_sim.ts | 2 +- .../__tests__/list_schemes.test.ts | 8 ++-- .../__tests__/show_build_settings.test.ts | 4 +- .../tools/project-discovery/list_schemes.ts | 4 +- .../project-discovery/show_build_settings.ts | 2 +- .../__tests__/scaffold_ios_project.test.ts | 12 ++--- .../scaffold_ios_project.ts | 4 +- .../__tests__/session_clear_defaults.test.ts | 4 +- .../__tests__/session_set_defaults.test.ts | 22 ++++----- .../simulator/__tests__/build_run_sim.test.ts | 44 ++++++++--------- .../simulator/__tests__/build_sim.test.ts | 48 +++++++++---------- .../__tests__/get_sim_app_path.test.ts | 6 +-- .../__tests__/launch_app_sim.test.ts | 8 ++-- .../simulator/__tests__/stop_app_sim.test.ts | 8 ++-- .../simulator/__tests__/test_sim.test.ts | 2 +- src/mcp/tools/simulator/boot_sim.ts | 2 +- src/mcp/tools/simulator/build_run_sim.ts | 2 +- src/mcp/tools/simulator/build_sim.ts | 2 +- src/mcp/tools/simulator/get_sim_app_path.ts | 2 +- src/mcp/tools/simulator/install_app_sim.ts | 2 +- .../tools/simulator/launch_app_logs_sim.ts | 2 +- src/mcp/tools/simulator/launch_app_sim.ts | 2 +- src/mcp/tools/simulator/stop_app_sim.ts | 2 +- src/mcp/tools/simulator/test_sim.ts | 2 +- .../__tests__/bootstrap-runtime.test.ts | 8 ++-- .../__tests__/e2e-mcp-invocation.test.ts | 2 +- .../__tests__/e2e-mcp-sessions.test.ts | 2 +- .../__tests__/e2e-mcp-simulator.test.ts | 4 +- .../__tests__/e2e-mcp-ui-automation.test.ts | 2 +- src/utils/__tests__/config-store.test.ts | 4 +- src/utils/__tests__/infer-platform.test.ts | 4 +- src/utils/__tests__/project-config.test.ts | 6 +-- .../session-aware-tool-factory.test.ts | 2 +- src/utils/__tests__/session-store.test.ts | 8 ++-- src/utils/__tests__/simulator-utils.test.ts | 10 ++-- .../__tests__/xcode-state-reader.test.ts | 4 +- src/utils/simulator-resolver.ts | 2 +- 41 files changed, 133 insertions(+), 133 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33ee66a5..3dff04f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,7 +73,7 @@ enabledWorkflows: sessionDefaults: scheme: MyApp projectPath: ./MyApp.xcodeproj - simulatorName: iPhone 16 + simulatorName: iPhone 17 ``` See [docs/CONFIGURATION.md](docs/CONFIGURATION.md) for the full reference. diff --git a/config.example.yaml b/config.example.yaml index 60df0c0a..ca45339a 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -8,7 +8,7 @@ sessionDefaults: workspacePath: './MyApp.xcworkspace' # xor projectPath scheme: 'MyApp' configuration: 'Debug' - simulatorName: 'iPhone 16' # xor simulatorId + simulatorName: 'iPhone 17' # xor simulatorId simulatorId: '' # xor simulatorName deviceId: '' useLatestOS: true diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index 4adcf620..93922018 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -47,7 +47,7 @@ sessionDefaults: projectPath: "./MyApp.xcodeproj" scheme: "MyApp" configuration: "Debug" - simulatorName: "iPhone 16" + simulatorName: "iPhone 17" platform: "iOS" useLatestOS: true arch: "arm64" @@ -104,7 +104,7 @@ sessionDefaults: projectPath: "./MyApp.xcodeproj" scheme: "MyApp" configuration: "Debug" - simulatorName: "iPhone 16" + simulatorName: "iPhone 17" ``` ### Setting defaults from an agent @@ -119,7 +119,7 @@ Agents can call `session_set_defaults` at runtime. By default these are stored i | `workspacePath` | string | Path to `.xcworkspace` file. Takes precedence if both are set. | | `scheme` | string | Build scheme name. | | `configuration` | string | Build configuration (e.g., `Debug`, `Release`). | -| `simulatorName` | string | Simulator name (e.g., `iPhone 16`). Mutually exclusive with `simulatorId`. | +| `simulatorName` | string | Simulator name (e.g., `iPhone 17`). Mutually exclusive with `simulatorId`. | | `simulatorId` | string | Simulator UUID. Takes precedence if both are set. | | `deviceId` | string | Physical device UUID. | | `platform` | string | Target platform (e.g., `iOS`, `macOS`, `watchOS`, `tvOS`, `visionOS`). | diff --git a/docs/SESSION_DEFAULTS.md b/docs/SESSION_DEFAULTS.md index afe318ef..0331819e 100644 --- a/docs/SESSION_DEFAULTS.md +++ b/docs/SESSION_DEFAULTS.md @@ -45,7 +45,7 @@ Copy/paste this sequence when starting a new session: {"name":"session_set_defaults","arguments":{ "workspacePath":"/repo/MyApp.xcworkspace", "scheme":"MyApp-iOS", - "simulatorName":"iPhone 16 Pro", + "simulatorName":"iPhone 17 Pro", "persist":true }} {"name":"session_show_defaults","arguments":{}} diff --git a/src/mcp/tools/debugging/debug_attach_sim.ts b/src/mcp/tools/debugging/debug_attach_sim.ts index b0adeb16..66292f28 100644 --- a/src/mcp/tools/debugging/debug_attach_sim.ts +++ b/src/mcp/tools/debugging/debug_attach_sim.ts @@ -25,7 +25,7 @@ const baseSchemaObject = z.object({ .string() .optional() .describe( - "Name of the simulator (e.g., 'iPhone 16'). Provide EITHER this OR simulatorId, not both", + "Name of the simulator (e.g., 'iPhone 17'). Provide EITHER this OR simulatorId, not both", ), bundleId: z.string().optional(), pid: z.number().int().positive().optional(), diff --git a/src/mcp/tools/project-discovery/__tests__/list_schemes.test.ts b/src/mcp/tools/project-discovery/__tests__/list_schemes.test.ts index 4d9f1ff3..f849e0da 100644 --- a/src/mcp/tools/project-discovery/__tests__/list_schemes.test.ts +++ b/src/mcp/tools/project-discovery/__tests__/list_schemes.test.ts @@ -76,12 +76,12 @@ describe('list_schemes plugin', () => { build_run_sim: { projectPath: '/path/to/MyProject.xcodeproj', scheme: 'MyProject', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, build_sim: { projectPath: '/path/to/MyProject.xcodeproj', scheme: 'MyProject', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, show_build_settings: { projectPath: '/path/to/MyProject.xcodeproj', scheme: 'MyProject' }, }, @@ -303,12 +303,12 @@ describe('list_schemes plugin', () => { build_run_sim: { workspacePath: '/path/to/MyProject.xcworkspace', scheme: 'MyApp', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, build_sim: { workspacePath: '/path/to/MyProject.xcworkspace', scheme: 'MyApp', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, show_build_settings: { workspacePath: '/path/to/MyProject.xcworkspace', scheme: 'MyApp' }, }, diff --git a/src/mcp/tools/project-discovery/__tests__/show_build_settings.test.ts b/src/mcp/tools/project-discovery/__tests__/show_build_settings.test.ts index eca48309..dd18f402 100644 --- a/src/mcp/tools/project-discovery/__tests__/show_build_settings.test.ts +++ b/src/mcp/tools/project-discovery/__tests__/show_build_settings.test.ts @@ -118,7 +118,7 @@ describe('show_build_settings plugin', () => { build_sim: { projectPath: '/path/to/MyProject.xcodeproj', scheme: 'MyScheme', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, list_schemes: { projectPath: '/path/to/MyProject.xcodeproj' }, }, @@ -311,7 +311,7 @@ describe('show_build_settings plugin', () => { build_sim: { projectPath: '/path/to/MyProject.xcodeproj', scheme: 'MyScheme', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, list_schemes: { projectPath: '/path/to/MyProject.xcodeproj' }, }, diff --git a/src/mcp/tools/project-discovery/list_schemes.ts b/src/mcp/tools/project-discovery/list_schemes.ts index fabd68c8..b7ae4de0 100644 --- a/src/mcp/tools/project-discovery/list_schemes.ts +++ b/src/mcp/tools/project-discovery/list_schemes.ts @@ -91,12 +91,12 @@ export async function listSchemesLogic( build_run_sim: { [`${projectOrWorkspace}Path`]: path!, scheme: firstScheme, - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, build_sim: { [`${projectOrWorkspace}Path`]: path!, scheme: firstScheme, - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, show_build_settings: { [`${projectOrWorkspace}Path`]: path!, scheme: firstScheme }, }; diff --git a/src/mcp/tools/project-discovery/show_build_settings.ts b/src/mcp/tools/project-discovery/show_build_settings.ts index 676d7321..015f30b7 100644 --- a/src/mcp/tools/project-discovery/show_build_settings.ts +++ b/src/mcp/tools/project-discovery/show_build_settings.ts @@ -91,7 +91,7 @@ export async function showBuildSettingsLogic( const pathKey = hasProjectPath ? 'projectPath' : 'workspacePath'; nextStepParams = { build_macos: { [pathKey]: path, scheme: params.scheme }, - build_sim: { [pathKey]: path, scheme: params.scheme, simulatorName: 'iPhone 16' }, + build_sim: { [pathKey]: path, scheme: params.scheme, simulatorName: 'iPhone 17' }, list_schemes: { [pathKey]: path }, }; } diff --git a/src/mcp/tools/project-scaffolding/__tests__/scaffold_ios_project.test.ts b/src/mcp/tools/project-scaffolding/__tests__/scaffold_ios_project.test.ts index 4facdd74..7707c407 100644 --- a/src/mcp/tools/project-scaffolding/__tests__/scaffold_ios_project.test.ts +++ b/src/mcp/tools/project-scaffolding/__tests__/scaffold_ios_project.test.ts @@ -377,12 +377,12 @@ describe('scaffold_ios_project plugin', () => { build_sim: { workspacePath: '/tmp/test-projects/TestIOSApp.xcworkspace', scheme: 'TestIOSApp', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, build_run_sim: { workspacePath: '/tmp/test-projects/TestIOSApp.xcworkspace', scheme: 'TestIOSApp', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, }, }); @@ -427,12 +427,12 @@ describe('scaffold_ios_project plugin', () => { build_sim: { workspacePath: '/tmp/test-projects/TestIOSApp.xcworkspace', scheme: 'TestIOSApp', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, build_run_sim: { workspacePath: '/tmp/test-projects/TestIOSApp.xcworkspace', scheme: 'TestIOSApp', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, }, }); @@ -469,12 +469,12 @@ describe('scaffold_ios_project plugin', () => { build_sim: { workspacePath: '/tmp/test-projects/MyProject.xcworkspace', scheme: 'MyProject', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, build_run_sim: { workspacePath: '/tmp/test-projects/MyProject.xcworkspace', scheme: 'MyProject', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, }, }); diff --git a/src/mcp/tools/project-scaffolding/scaffold_ios_project.ts b/src/mcp/tools/project-scaffolding/scaffold_ios_project.ts index 67935049..396b4925 100644 --- a/src/mcp/tools/project-scaffolding/scaffold_ios_project.ts +++ b/src/mcp/tools/project-scaffolding/scaffold_ios_project.ts @@ -379,12 +379,12 @@ export async function scaffold_ios_projectLogic( build_sim: { workspacePath, scheme: generatedProjectName, - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, build_run_sim: { workspacePath, scheme: generatedProjectName, - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, }, }; diff --git a/src/mcp/tools/session-management/__tests__/session_clear_defaults.test.ts b/src/mcp/tools/session-management/__tests__/session_clear_defaults.test.ts index 40ef1e22..d9b9ebca 100644 --- a/src/mcp/tools/session-management/__tests__/session_clear_defaults.test.ts +++ b/src/mcp/tools/session-management/__tests__/session_clear_defaults.test.ts @@ -8,7 +8,7 @@ describe('session-clear-defaults tool', () => { sessionStore.setDefaults({ scheme: 'MyScheme', projectPath: '/path/to/proj.xcodeproj', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', deviceId: 'DEVICE-123', useLatestOS: true, arch: 'arm64', @@ -44,7 +44,7 @@ describe('session-clear-defaults tool', () => { expect(current.deviceId).toBeUndefined(); expect(current.derivedDataPath).toBeUndefined(); expect(current.projectPath).toBe('/path/to/proj.xcodeproj'); - expect(current.simulatorName).toBe('iPhone 16'); + expect(current.simulatorName).toBe('iPhone 17'); expect(current.useLatestOS).toBe(true); expect(current.arch).toBe('arm64'); }); diff --git a/src/mcp/tools/session-management/__tests__/session_set_defaults.test.ts b/src/mcp/tools/session-management/__tests__/session_set_defaults.test.ts index 6fd4c6c6..774f9861 100644 --- a/src/mcp/tools/session-management/__tests__/session_set_defaults.test.ts +++ b/src/mcp/tools/session-management/__tests__/session_set_defaults.test.ts @@ -25,7 +25,7 @@ describe('session-set-defaults tool', () => { output: JSON.stringify({ devices: { 'com.apple.CoreSimulator.SimRuntime.iOS-18-0': [ - { udid: 'RESOLVED-SIM-UUID', name: 'iPhone 16' }, + { udid: 'RESOLVED-SIM-UUID', name: 'iPhone 17' }, { udid: 'OTHER-SIM-UUID', name: 'iPhone 15' }, ], }, @@ -58,7 +58,7 @@ describe('session-set-defaults tool', () => { const result = await sessionSetDefaultsLogic( { scheme: 'MyScheme', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', useLatestOS: true, arch: 'arm64', }, @@ -70,7 +70,7 @@ describe('session-set-defaults tool', () => { const current = sessionStore.getAll(); expect(current.scheme).toBe('MyScheme'); - expect(current.simulatorName).toBe('iPhone 16'); + expect(current.simulatorName).toBe('iPhone 17'); // simulatorId resolution happens in background; immediate update keeps explicit inputs only expect(current.simulatorId).toBeUndefined(); expect(current.useLatestOS).toBe(true); @@ -153,10 +153,10 @@ describe('session-set-defaults tool', () => { it('should clear stale simulatorId when only simulatorName is set', async () => { sessionStore.setDefaults({ simulatorId: 'OLD-SIM-UUID' }); - const result = await sessionSetDefaultsLogic({ simulatorName: 'iPhone 16' }, createContext()); + const result = await sessionSetDefaultsLogic({ simulatorName: 'iPhone 17' }, createContext()); const current = sessionStore.getAll(); // simulatorId resolution happens in background; stale id is cleared immediately - expect(current.simulatorName).toBe('iPhone 16'); + expect(current.simulatorName).toBe('iPhone 17'); expect(current.simulatorId).toBeUndefined(); expect(result.content[0].text).toContain( 'Cleared simulatorId because simulatorName was set; background resolution will repopulate it.', @@ -220,14 +220,14 @@ describe('session-set-defaults tool', () => { const res = await sessionSetDefaultsLogic( { simulatorId: 'SIM-1', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, createContext(), ); const current = sessionStore.getAll(); // Both are kept, simulatorId takes precedence for tools expect(current.simulatorId).toBe('SIM-1'); - expect(current.simulatorName).toBe('iPhone 16'); + expect(current.simulatorName).toBe('iPhone 17'); expect(res.content[0].text).toContain( 'Both simulatorId and simulatorName were provided; simulatorId will be used by tools.', ); @@ -289,7 +289,7 @@ describe('session-set-defaults tool', () => { { profile: 'ios', scheme: 'NewIOS', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, createContext(), ); @@ -298,7 +298,7 @@ describe('session-set-defaults tool', () => { expect(result.content[0].text).toContain('Activated profile "ios".'); expect(sessionStore.getActiveProfile()).toBe('ios'); expect(sessionStore.getAll().scheme).toBe('NewIOS'); - expect(sessionStore.getAll().simulatorName).toBe('iPhone 16'); + expect(sessionStore.getAll().simulatorName).toBe('iPhone 17'); }); it('returns error when profile does not exist and createIfNotExists is false', async () => { @@ -364,7 +364,7 @@ describe('session-set-defaults tool', () => { { profile: 'ios', scheme: 'NewIOS', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', persist: true, }, createContext(), @@ -376,7 +376,7 @@ describe('session-set-defaults tool', () => { activeSessionDefaultsProfile?: string; }; expect(parsed.sessionDefaultsProfiles?.ios?.scheme).toBe('NewIOS'); - expect(parsed.sessionDefaultsProfiles?.ios?.simulatorName).toBe('iPhone 16'); + expect(parsed.sessionDefaultsProfiles?.ios?.simulatorName).toBe('iPhone 17'); expect(parsed.activeSessionDefaultsProfile).toBe('ios'); }); diff --git a/src/mcp/tools/simulator/__tests__/build_run_sim.test.ts b/src/mcp/tools/simulator/__tests__/build_run_sim.test.ts index 98901872..03238365 100644 --- a/src/mcp/tools/simulator/__tests__/build_run_sim.test.ts +++ b/src/mcp/tools/simulator/__tests__/build_run_sim.test.ts @@ -61,7 +61,7 @@ describe('build_run_sim tool', () => { output: JSON.stringify({ devices: { 'com.apple.CoreSimulator.SimRuntime.iOS-18-0': [ - { udid: 'SIM-UUID', name: 'iPhone 16', isAvailable: true }, + { udid: 'SIM-UUID', name: 'iPhone 17', isAvailable: true }, ], }, }), @@ -89,7 +89,7 @@ describe('build_run_sim tool', () => { { workspacePath: '/path/to/workspace', scheme: 'MyScheme', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, mockExecutor, ); @@ -115,7 +115,7 @@ describe('build_run_sim tool', () => { { workspacePath: '/path/to/workspace', scheme: 'MyScheme', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, mockExecutor, ); @@ -152,7 +152,7 @@ describe('build_run_sim tool', () => { 'iOS 16.0': [ { udid: 'test-uuid-123', - name: 'iPhone 16', + name: 'iPhone 17', state: 'Booted', isAvailable: true, }, @@ -183,7 +183,7 @@ describe('build_run_sim tool', () => { { workspacePath: '/path/to/workspace', scheme: 'MyScheme', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, mockExecutor, ); @@ -203,7 +203,7 @@ describe('build_run_sim tool', () => { { workspacePath: '/path/to/workspace', scheme: 'MyScheme', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, mockExecutor, ); @@ -223,7 +223,7 @@ describe('build_run_sim tool', () => { { workspacePath: '/path/to/workspace', scheme: 'MyScheme', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, mockExecutor, ); @@ -255,7 +255,7 @@ describe('build_run_sim tool', () => { { workspacePath: '/path/to/MyProject.xcworkspace', scheme: 'MyScheme', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, createTrackingExecutor(callHistory), ); @@ -272,7 +272,7 @@ describe('build_run_sim tool', () => { 'Debug', '-skipMacroValidation', '-destination', - 'platform=iOS Simulator,name=iPhone 16,OS=latest', + 'platform=iOS Simulator,name=iPhone 17,OS=latest', 'build', ]); expect(callHistory[1].logPrefix).toBe('iOS Simulator Build'); @@ -292,7 +292,7 @@ describe('build_run_sim tool', () => { output: JSON.stringify({ devices: { 'com.apple.CoreSimulator.SimRuntime.iOS-18-0': [ - { udid: 'test-uuid-123', name: 'iPhone 16', isAvailable: true }, + { udid: 'test-uuid-123', name: 'iPhone 17', isAvailable: true }, ], }, }), @@ -310,7 +310,7 @@ describe('build_run_sim tool', () => { { workspacePath: '/path/to/MyProject.xcworkspace', scheme: 'MyScheme', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, trackingExecutor, ); @@ -327,7 +327,7 @@ describe('build_run_sim tool', () => { 'Debug', '-skipMacroValidation', '-destination', - 'platform=iOS Simulator,name=iPhone 16,OS=latest', + 'platform=iOS Simulator,name=iPhone 17,OS=latest', 'build', ]); expect(callHistory[1].logPrefix).toBe('iOS Simulator Build'); @@ -347,7 +347,7 @@ describe('build_run_sim tool', () => { output: JSON.stringify({ devices: { 'com.apple.CoreSimulator.SimRuntime.iOS-18-0': [ - { udid: 'test-uuid-123', name: 'iPhone 16', isAvailable: true }, + { udid: 'test-uuid-123', name: 'iPhone 17', isAvailable: true }, ], }, }), @@ -371,7 +371,7 @@ describe('build_run_sim tool', () => { { workspacePath: '/path/to/MyProject.xcworkspace', scheme: 'MyScheme', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', configuration: 'Release', useLatestOS: false, }, @@ -390,7 +390,7 @@ describe('build_run_sim tool', () => { 'Release', '-skipMacroValidation', '-destination', - 'platform=iOS Simulator,name=iPhone 16', + 'platform=iOS Simulator,name=iPhone 17', 'build', ]); expect(callHistory[1].logPrefix).toBe('iOS Simulator Build'); @@ -404,7 +404,7 @@ describe('build_run_sim tool', () => { '-configuration', 'Release', '-destination', - 'platform=iOS Simulator,name=iPhone 16', + 'platform=iOS Simulator,name=iPhone 17', ]); expect(callHistory[2].logPrefix).toBe('Get App Path'); }); @@ -416,7 +416,7 @@ describe('build_run_sim tool', () => { { workspacePath: '/Users/dev/My Project/MyProject.xcworkspace', scheme: 'My Scheme', - simulatorName: 'iPhone 16 Pro', + simulatorName: 'iPhone 17 Pro', }, createTrackingExecutor(callHistory), ); @@ -433,7 +433,7 @@ describe('build_run_sim tool', () => { 'Debug', '-skipMacroValidation', '-destination', - 'platform=iOS Simulator,name=iPhone 16 Pro,OS=latest', + 'platform=iOS Simulator,name=iPhone 17 Pro,OS=latest', 'build', ]); expect(callHistory[1].logPrefix).toBe('iOS Simulator Build'); @@ -474,7 +474,7 @@ describe('build_run_sim tool', () => { it('should error when neither projectPath nor workspacePath provided', async () => { const result = await handler({ scheme: 'MyScheme', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Missing required session defaults'); @@ -486,7 +486,7 @@ describe('build_run_sim tool', () => { projectPath: '/path/project.xcodeproj', workspacePath: '/path/workspace.xcworkspace', scheme: 'MyScheme', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Parameter validation failed'); @@ -506,7 +506,7 @@ describe('build_run_sim tool', () => { { projectPath: '/path/project.xcodeproj', scheme: 'MyScheme', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, mockExecutor, ); @@ -526,7 +526,7 @@ describe('build_run_sim tool', () => { { workspacePath: '/path/workspace.xcworkspace', scheme: 'MyScheme', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, mockExecutor, ); diff --git a/src/mcp/tools/simulator/__tests__/build_sim.test.ts b/src/mcp/tools/simulator/__tests__/build_sim.test.ts index ad9b05d0..5655cdd5 100644 --- a/src/mcp/tools/simulator/__tests__/build_sim.test.ts +++ b/src/mcp/tools/simulator/__tests__/build_sim.test.ts @@ -44,7 +44,7 @@ describe('build_sim tool', () => { it('should handle missing both projectPath and workspacePath', async () => { const result = await handler({ scheme: 'MyScheme', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }); expect(result.isError).toBe(true); @@ -57,7 +57,7 @@ describe('build_sim tool', () => { projectPath: '/path/to/project.xcodeproj', workspacePath: '/path/to/workspace', scheme: 'MyScheme', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }); expect(result.isError).toBe(true); @@ -74,7 +74,7 @@ describe('build_sim tool', () => { { workspacePath: '', scheme: 'MyScheme', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, mockExecutor, ); @@ -95,7 +95,7 @@ describe('build_sim tool', () => { it('should handle missing scheme parameter', async () => { const result = await handler({ workspacePath: '/path/to/workspace', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }); expect(result.isError).toBe(true); @@ -110,7 +110,7 @@ describe('build_sim tool', () => { { workspacePath: '/path/to/workspace', scheme: '', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, mockExecutor, ); @@ -147,7 +147,7 @@ describe('build_sim tool', () => { workspacePath: '/path/to/workspace', scheme: 'MyScheme', simulatorId: 'ABC-123', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }); expect(result.isError).toBe(true); @@ -213,7 +213,7 @@ describe('build_sim tool', () => { { workspacePath: '/path/to/MyProject.xcworkspace', scheme: 'MyScheme', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, createTrackingExecutor(callHistory), ); @@ -230,7 +230,7 @@ describe('build_sim tool', () => { 'Debug', '-skipMacroValidation', '-destination', - 'platform=iOS Simulator,name=iPhone 16,OS=latest', + 'platform=iOS Simulator,name=iPhone 17,OS=latest', 'build', ], 'iOS Simulator Build', @@ -244,7 +244,7 @@ describe('build_sim tool', () => { { projectPath: '/path/to/MyProject.xcodeproj', scheme: 'MyScheme', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, createTrackingExecutor(callHistory), ); @@ -261,7 +261,7 @@ describe('build_sim tool', () => { 'Debug', '-skipMacroValidation', '-destination', - 'platform=iOS Simulator,name=iPhone 16,OS=latest', + 'platform=iOS Simulator,name=iPhone 17,OS=latest', 'build', ], 'iOS Simulator Build', @@ -275,7 +275,7 @@ describe('build_sim tool', () => { { workspacePath: '/path/to/MyProject.xcworkspace', scheme: 'MyScheme', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', configuration: 'Release', derivedDataPath: '/custom/derived/path', extraArgs: ['--verbose'], @@ -296,7 +296,7 @@ describe('build_sim tool', () => { 'Release', '-skipMacroValidation', '-destination', - 'platform=iOS Simulator,name=iPhone 16', + 'platform=iOS Simulator,name=iPhone 17', '-derivedDataPath', '/custom/derived/path', '--verbose', @@ -313,7 +313,7 @@ describe('build_sim tool', () => { { workspacePath: '/Users/dev/My Project/MyProject.xcworkspace', scheme: 'My Scheme', - simulatorName: 'iPhone 16 Pro', + simulatorName: 'iPhone 17 Pro', }, createTrackingExecutor(callHistory), ); @@ -330,7 +330,7 @@ describe('build_sim tool', () => { 'Debug', '-skipMacroValidation', '-destination', - 'platform=iOS Simulator,name=iPhone 16 Pro,OS=latest', + 'platform=iOS Simulator,name=iPhone 17 Pro,OS=latest', 'build', ], 'iOS Simulator Build', @@ -344,7 +344,7 @@ describe('build_sim tool', () => { { workspacePath: '/path/to/MyProject.xcworkspace', scheme: 'MyScheme', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', useLatestOS: true, }, createTrackingExecutor(callHistory), @@ -362,7 +362,7 @@ describe('build_sim tool', () => { 'Debug', '-skipMacroValidation', '-destination', - 'platform=iOS Simulator,name=iPhone 16,OS=latest', + 'platform=iOS Simulator,name=iPhone 17,OS=latest', 'build', ], 'iOS Simulator Build', @@ -409,7 +409,7 @@ describe('build_sim tool', () => { { workspacePath: '/path/to/workspace', scheme: 'MyScheme', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, mockExecutor, ); @@ -433,7 +433,7 @@ describe('build_sim tool', () => { { workspacePath: '/path/to/workspace', scheme: 'MyScheme', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', configuration: 'Release', derivedDataPath: '/path/to/derived', extraArgs: ['--verbose'], @@ -466,7 +466,7 @@ describe('build_sim tool', () => { { workspacePath: '/path/to/workspace', scheme: 'MyScheme', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, mockExecutor, ); @@ -496,7 +496,7 @@ describe('build_sim tool', () => { { workspacePath: '/path/to/workspace', scheme: 'MyScheme', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, mockExecutor, ); @@ -529,7 +529,7 @@ describe('build_sim tool', () => { { workspacePath: '/path/to/workspace', scheme: 'MyScheme', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, mockExecutor, ); @@ -549,7 +549,7 @@ describe('build_sim tool', () => { { workspacePath: '/path/to/workspace', scheme: 'MyScheme', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, mockExecutor, ); @@ -582,7 +582,7 @@ describe('build_sim tool', () => { { workspacePath: '/path/to/workspace', scheme: 'MyScheme', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', // configuration intentionally omitted - should default to Debug }, mockExecutor, @@ -611,7 +611,7 @@ describe('build_sim tool', () => { { workspacePath: '/path/to/workspace', scheme: 'MyScheme', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }, mockExecutor, ); diff --git a/src/mcp/tools/simulator/__tests__/get_sim_app_path.test.ts b/src/mcp/tools/simulator/__tests__/get_sim_app_path.test.ts index 0d650d2c..95bf83df 100644 --- a/src/mcp/tools/simulator/__tests__/get_sim_app_path.test.ts +++ b/src/mcp/tools/simulator/__tests__/get_sim_app_path.test.ts @@ -93,7 +93,7 @@ describe('get_sim_app_path tool', () => { const result = await handler({ platform: XcodePlatform.iOSSimulator, simulatorId: 'SIM-UUID', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }); expect(result.isError).toBe(true); @@ -136,7 +136,7 @@ describe('get_sim_app_path tool', () => { workspacePath: '/path/to/workspace.xcworkspace', scheme: 'MyScheme', platform: XcodePlatform.iOSSimulator, - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', useLatestOS: true, }, trackingExecutor, @@ -155,7 +155,7 @@ describe('get_sim_app_path tool', () => { '-configuration', 'Debug', '-destination', - 'platform=iOS Simulator,name=iPhone 16,OS=latest', + 'platform=iOS Simulator,name=iPhone 17,OS=latest', ]); expect(result.isError).toBe(false); diff --git a/src/mcp/tools/simulator/__tests__/launch_app_sim.test.ts b/src/mcp/tools/simulator/__tests__/launch_app_sim.test.ts index 6c732c79..8ffbd533 100644 --- a/src/mcp/tools/simulator/__tests__/launch_app_sim.test.ts +++ b/src/mcp/tools/simulator/__tests__/launch_app_sim.test.ts @@ -28,7 +28,7 @@ describe('launch_app_sim tool', () => { const withSimDefaults = schemaObj.safeParse({ simulatorId: 'sim-default', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }); expect(withSimDefaults.success).toBe(false); }); @@ -57,7 +57,7 @@ describe('launch_app_sim tool', () => { it('should reject when both simulatorId and simulatorName provided explicitly', async () => { const result = await handler({ simulatorId: 'SIM-UUID', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', bundleId: 'io.sentry.testapp', }); @@ -179,7 +179,7 @@ describe('launch_app_sim tool', () => { const result = await launch_app_simLogic( { simulatorId: 'resolved-uuid', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', bundleId: 'io.sentry.testapp', }, sequencedExecutor, @@ -189,7 +189,7 @@ describe('launch_app_sim tool', () => { content: [ { type: 'text', - text: 'App launched successfully in simulator "iPhone 16" (resolved-uuid).', + text: 'App launched successfully in simulator "iPhone 17" (resolved-uuid).', }, ], nextStepParams: { diff --git a/src/mcp/tools/simulator/__tests__/stop_app_sim.test.ts b/src/mcp/tools/simulator/__tests__/stop_app_sim.test.ts index 2ad91e7b..5ca7e21c 100644 --- a/src/mcp/tools/simulator/__tests__/stop_app_sim.test.ts +++ b/src/mcp/tools/simulator/__tests__/stop_app_sim.test.ts @@ -24,7 +24,7 @@ describe('stop_app_sim tool', () => { const withSessionDefaults = schemaObj.safeParse({ simulatorId: 'SIM-UUID', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }); expect(withSessionDefaults.success).toBe(true); const parsed = withSessionDefaults.data as Record; @@ -56,7 +56,7 @@ describe('stop_app_sim tool', () => { it('should reject mutually exclusive simulator parameters', async () => { const result = await handler({ simulatorId: 'SIM-UUID', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', bundleId: 'io.sentry.app', }); @@ -95,7 +95,7 @@ describe('stop_app_sim tool', () => { const result = await stop_app_simLogic( { simulatorId: 'resolved-uuid', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', bundleId: 'io.sentry.App', }, mockExecutor, @@ -105,7 +105,7 @@ describe('stop_app_sim tool', () => { content: [ { type: 'text', - text: 'App io.sentry.App stopped successfully in simulator "iPhone 16" (resolved-uuid)', + text: 'App io.sentry.App stopped successfully in simulator "iPhone 17" (resolved-uuid)', }, ], }); diff --git a/src/mcp/tools/simulator/__tests__/test_sim.test.ts b/src/mcp/tools/simulator/__tests__/test_sim.test.ts index bcb17b16..4b398fe7 100644 --- a/src/mcp/tools/simulator/__tests__/test_sim.test.ts +++ b/src/mcp/tools/simulator/__tests__/test_sim.test.ts @@ -76,7 +76,7 @@ describe('test_sim tool', () => { const result = await handler({ simulatorId: 'SIM-UUID', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }); expect(result.isError).toBe(true); diff --git a/src/mcp/tools/simulator/boot_sim.ts b/src/mcp/tools/simulator/boot_sim.ts index 997050ca..ba356254 100644 --- a/src/mcp/tools/simulator/boot_sim.ts +++ b/src/mcp/tools/simulator/boot_sim.ts @@ -19,7 +19,7 @@ const baseSchemaObject = z.object({ .string() .optional() .describe( - "Name of the simulator (e.g., 'iPhone 16'). Provide EITHER this OR simulatorId, not both", + "Name of the simulator (e.g., 'iPhone 17'). Provide EITHER this OR simulatorId, not both", ), }); diff --git a/src/mcp/tools/simulator/build_run_sim.ts b/src/mcp/tools/simulator/build_run_sim.ts index b8adab08..850a6260 100644 --- a/src/mcp/tools/simulator/build_run_sim.ts +++ b/src/mcp/tools/simulator/build_run_sim.ts @@ -36,7 +36,7 @@ const baseOptions = { .string() .optional() .describe( - "Name of the simulator (e.g., 'iPhone 16'). Provide EITHER this OR simulatorId, not both", + "Name of the simulator (e.g., 'iPhone 17'). Provide EITHER this OR simulatorId, not both", ), configuration: z.string().optional().describe('Build configuration (Debug, Release, etc.)'), derivedDataPath: z.string().optional(), diff --git a/src/mcp/tools/simulator/build_sim.ts b/src/mcp/tools/simulator/build_sim.ts index 3eef8b42..3af06aff 100644 --- a/src/mcp/tools/simulator/build_sim.ts +++ b/src/mcp/tools/simulator/build_sim.ts @@ -32,7 +32,7 @@ const baseOptions = { .string() .optional() .describe( - "Name of the simulator (e.g., 'iPhone 16'). Provide EITHER this OR simulatorId, not both", + "Name of the simulator (e.g., 'iPhone 17'). Provide EITHER this OR simulatorId, not both", ), configuration: z.string().optional().describe('Build configuration (Debug, Release, etc.)'), derivedDataPath: z.string().optional(), diff --git a/src/mcp/tools/simulator/get_sim_app_path.ts b/src/mcp/tools/simulator/get_sim_app_path.ts index 27d103cb..f6cdecb6 100644 --- a/src/mcp/tools/simulator/get_sim_app_path.ts +++ b/src/mcp/tools/simulator/get_sim_app_path.ts @@ -49,7 +49,7 @@ const baseGetSimulatorAppPathSchema = z.object({ .string() .optional() .describe( - "Name of the simulator (e.g., 'iPhone 16'). Provide EITHER this OR simulatorId, not both", + "Name of the simulator (e.g., 'iPhone 17'). Provide EITHER this OR simulatorId, not both", ), configuration: z.string().optional().describe('Build configuration (Debug, Release, etc.)'), useLatestOS: z diff --git a/src/mcp/tools/simulator/install_app_sim.ts b/src/mcp/tools/simulator/install_app_sim.ts index ba0b297d..e0900368 100644 --- a/src/mcp/tools/simulator/install_app_sim.ts +++ b/src/mcp/tools/simulator/install_app_sim.ts @@ -20,7 +20,7 @@ const baseSchemaObject = z.object({ .string() .optional() .describe( - "Name of the simulator (e.g., 'iPhone 16'). Provide EITHER this OR simulatorId, not both", + "Name of the simulator (e.g., 'iPhone 17'). Provide EITHER this OR simulatorId, not both", ), appPath: z.string().describe('Path to the .app bundle to install'), }); diff --git a/src/mcp/tools/simulator/launch_app_logs_sim.ts b/src/mcp/tools/simulator/launch_app_logs_sim.ts index fb5a5c7a..69aac0f0 100644 --- a/src/mcp/tools/simulator/launch_app_logs_sim.ts +++ b/src/mcp/tools/simulator/launch_app_logs_sim.ts @@ -32,7 +32,7 @@ const baseSchemaObject = z.object({ .string() .optional() .describe( - "Name of the simulator (e.g., 'iPhone 16'). Provide EITHER this OR simulatorId, not both", + "Name of the simulator (e.g., 'iPhone 17'). Provide EITHER this OR simulatorId, not both", ), bundleId: z.string().describe('Bundle identifier of the app to launch'), args: z.array(z.string()).optional().describe('Optional arguments to pass to the app'), diff --git a/src/mcp/tools/simulator/launch_app_sim.ts b/src/mcp/tools/simulator/launch_app_sim.ts index 3f4a1c0f..09a1b959 100644 --- a/src/mcp/tools/simulator/launch_app_sim.ts +++ b/src/mcp/tools/simulator/launch_app_sim.ts @@ -20,7 +20,7 @@ const baseSchemaObject = z.object({ .string() .optional() .describe( - "Name of the simulator (e.g., 'iPhone 16'). Provide EITHER this OR simulatorId, not both", + "Name of the simulator (e.g., 'iPhone 17'). Provide EITHER this OR simulatorId, not both", ), bundleId: z.string().describe('Bundle identifier of the app to launch'), args: z.array(z.string()).optional().describe('Optional arguments to pass to the app'), diff --git a/src/mcp/tools/simulator/stop_app_sim.ts b/src/mcp/tools/simulator/stop_app_sim.ts index 26d4fd1b..1f7997a7 100644 --- a/src/mcp/tools/simulator/stop_app_sim.ts +++ b/src/mcp/tools/simulator/stop_app_sim.ts @@ -19,7 +19,7 @@ const baseSchemaObject = z.object({ .string() .optional() .describe( - "Name of the simulator (e.g., 'iPhone 16'). Provide EITHER this OR simulatorId, not both", + "Name of the simulator (e.g., 'iPhone 17'). Provide EITHER this OR simulatorId, not both", ), bundleId: z.string().describe('Bundle identifier of the app to stop'), }); diff --git a/src/mcp/tools/simulator/test_sim.ts b/src/mcp/tools/simulator/test_sim.ts index 5f4bd2b3..923c0bbd 100644 --- a/src/mcp/tools/simulator/test_sim.ts +++ b/src/mcp/tools/simulator/test_sim.ts @@ -40,7 +40,7 @@ const baseSchemaObject = z.object({ .string() .optional() .describe( - "Name of the simulator (e.g., 'iPhone 16'). Provide EITHER this OR simulatorId, not both", + "Name of the simulator (e.g., 'iPhone 17'). Provide EITHER this OR simulatorId, not both", ), configuration: z.string().optional().describe('Build configuration (Debug, Release, etc.)'), derivedDataPath: z.string().optional(), diff --git a/src/runtime/__tests__/bootstrap-runtime.test.ts b/src/runtime/__tests__/bootstrap-runtime.test.ts index c845506f..c275a9c9 100644 --- a/src/runtime/__tests__/bootstrap-runtime.test.ts +++ b/src/runtime/__tests__/bootstrap-runtime.test.ts @@ -14,7 +14,7 @@ function createFsWithSessionDefaults() { 'sessionDefaults:', ' scheme: "AppScheme"', ' simulatorId: "SIM-UUID"', - ' simulatorName: "iPhone 16"', + ' simulatorName: "iPhone 17"', '', ].join('\n'); @@ -51,7 +51,7 @@ function createFsWithProfiles() { 'sessionDefaultsProfiles:', ' ios:', ' scheme: "IOSScheme"', - ' simulatorName: "iPhone 16"', + ' simulatorName: "iPhone 17"', 'activeSessionDefaultsProfile: "ios"', '', ].join('\n'); @@ -84,7 +84,7 @@ describe('bootstrapRuntime', () => { expect(sessionStore.getAll()).toMatchObject({ scheme: 'AppScheme', simulatorId: 'SIM-UUID', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }); }); @@ -122,6 +122,6 @@ describe('bootstrapRuntime', () => { expect(sessionStore.getActiveProfile()).toBe('ios'); expect(sessionStore.getAll().scheme).toBe('IOSScheme'); - expect(sessionStore.getAll().simulatorName).toBe('iPhone 16'); + expect(sessionStore.getAll().simulatorName).toBe('iPhone 17'); }); }); diff --git a/src/smoke-tests/__tests__/e2e-mcp-invocation.test.ts b/src/smoke-tests/__tests__/e2e-mcp-invocation.test.ts index 2f6f75f8..a5b85777 100644 --- a/src/smoke-tests/__tests__/e2e-mcp-invocation.test.ts +++ b/src/smoke-tests/__tests__/e2e-mcp-invocation.test.ts @@ -13,7 +13,7 @@ beforeAll(async () => { devices: { 'com.apple.CoreSimulator.SimRuntime.iOS-18-0': [ { - name: 'iPhone 16 Pro', + name: 'iPhone 17 Pro', udid: 'AAAAAAAA-1111-2222-3333-444444444444', state: 'Shutdown', isAvailable: true, diff --git a/src/smoke-tests/__tests__/e2e-mcp-sessions.test.ts b/src/smoke-tests/__tests__/e2e-mcp-sessions.test.ts index bb5323af..c8fc2df9 100644 --- a/src/smoke-tests/__tests__/e2e-mcp-sessions.test.ts +++ b/src/smoke-tests/__tests__/e2e-mcp-sessions.test.ts @@ -14,7 +14,7 @@ beforeAll(async () => { devices: { 'com.apple.CoreSimulator.SimRuntime.iOS-18-0': [ { - name: 'iPhone 16 Pro', + name: 'iPhone 17 Pro', udid: 'AAAAAAAA-1111-2222-3333-444444444444', state: 'Shutdown', isAvailable: true, diff --git a/src/smoke-tests/__tests__/e2e-mcp-simulator.test.ts b/src/smoke-tests/__tests__/e2e-mcp-simulator.test.ts index 8d433d6a..5c7a869e 100644 --- a/src/smoke-tests/__tests__/e2e-mcp-simulator.test.ts +++ b/src/smoke-tests/__tests__/e2e-mcp-simulator.test.ts @@ -15,7 +15,7 @@ beforeAll(async () => { devices: { 'com.apple.CoreSimulator.SimRuntime.iOS-18-0': [ { - name: 'iPhone 16 Pro', + name: 'iPhone 17 Pro', udid: SIM_UUID, state: 'Booted', isAvailable: true, @@ -30,7 +30,7 @@ beforeAll(async () => { devices: { 'com.apple.CoreSimulator.SimRuntime.iOS-18-0': [ { - name: 'iPhone 16 Pro', + name: 'iPhone 17 Pro', udid: SIM_UUID, state: 'Booted', isAvailable: true, diff --git a/src/smoke-tests/__tests__/e2e-mcp-ui-automation.test.ts b/src/smoke-tests/__tests__/e2e-mcp-ui-automation.test.ts index cbf4a3c2..c54852db 100644 --- a/src/smoke-tests/__tests__/e2e-mcp-ui-automation.test.ts +++ b/src/smoke-tests/__tests__/e2e-mcp-ui-automation.test.ts @@ -28,7 +28,7 @@ beforeAll(async () => { devices: { 'com.apple.CoreSimulator.SimRuntime.iOS-18-0': [ { - name: 'iPhone 16 Pro', + name: 'iPhone 17 Pro', udid: SIM_ID, state: 'Booted', isAvailable: true, diff --git a/src/utils/__tests__/config-store.test.ts b/src/utils/__tests__/config-store.test.ts index 90ceac4f..0f208cc6 100644 --- a/src/utils/__tests__/config-store.test.ts +++ b/src/utils/__tests__/config-store.test.ts @@ -128,7 +128,7 @@ describe('config-store', () => { fs: createFs(yaml), overrides: { sessionDefaultsProfiles: { - ios: { simulatorName: 'iPhone 16' }, + ios: { simulatorName: 'iPhone 17' }, watch: { scheme: 'WatchScheme' }, }, }, @@ -137,7 +137,7 @@ describe('config-store', () => { const config = getConfig(); expect(config.activeSessionDefaultsProfile).toBe('ios'); expect(config.sessionDefaultsProfiles?.ios?.scheme).toBe('FromFile'); - expect(config.sessionDefaultsProfiles?.ios?.simulatorName).toBe('iPhone 16'); + expect(config.sessionDefaultsProfiles?.ios?.simulatorName).toBe('iPhone 17'); expect(config.sessionDefaultsProfiles?.watch?.scheme).toBe('WatchScheme'); }); diff --git a/src/utils/__tests__/infer-platform.test.ts b/src/utils/__tests__/infer-platform.test.ts index fa8c635f..520b65ac 100644 --- a/src/utils/__tests__/infer-platform.test.ts +++ b/src/utils/__tests__/infer-platform.test.ts @@ -60,7 +60,7 @@ describe('inferPlatform', () => { 'com.apple.CoreSimulator.SimRuntime.iOS-18-0': [ { udid: 'SIM-UUID', - name: 'iPhone 16 Pro', + name: 'iPhone 17 Pro', isAvailable: true, }, ], @@ -68,7 +68,7 @@ describe('inferPlatform', () => { }), }); - const result = await inferPlatform({ simulatorName: 'iPhone 16 Pro' }, mockExecutor); + const result = await inferPlatform({ simulatorName: 'iPhone 17 Pro' }, mockExecutor); expect(result.platform).toBe(XcodePlatform.iOSSimulator); expect(result.source).toBe('simulator-runtime'); diff --git a/src/utils/__tests__/project-config.test.ts b/src/utils/__tests__/project-config.test.ts index 46f9e12e..c5b630f3 100644 --- a/src/utils/__tests__/project-config.test.ts +++ b/src/utils/__tests__/project-config.test.ts @@ -65,7 +65,7 @@ describe('project-config', () => { 'sessionDefaults:', ' projectPath: "./App.xcodeproj"', ' workspacePath: "./App.xcworkspace"', - ' simulatorName: "iPhone 16"', + ' simulatorName: "iPhone 17"', ' simulatorId: "SIM-1"', ' derivedDataPath: "./.derivedData"', '', @@ -83,7 +83,7 @@ describe('project-config', () => { expect(defaults.workspacePath).toBe(path.join(cwd, 'App.xcworkspace')); expect(defaults.projectPath).toBeUndefined(); expect(defaults.simulatorId).toBe('SIM-1'); - expect(defaults.simulatorName).toBe('iPhone 16'); + expect(defaults.simulatorName).toBe('iPhone 17'); expect(defaults.derivedDataPath).toBe(path.join(cwd, '.derivedData')); expect(result.notices.length).toBeGreaterThan(0); }); @@ -136,7 +136,7 @@ describe('project-config', () => { ' ios:', ' projectPath: "./App.xcodeproj"', ' workspacePath: "./App.xcworkspace"', - ' simulatorName: "iPhone 16"', + ' simulatorName: "iPhone 17"', ' watch:', ' workspacePath: "./Watch.xcworkspace"', '', diff --git a/src/utils/__tests__/session-aware-tool-factory.test.ts b/src/utils/__tests__/session-aware-tool-factory.test.ts index e28caada..193c6145 100644 --- a/src/utils/__tests__/session-aware-tool-factory.test.ts +++ b/src/utils/__tests__/session-aware-tool-factory.test.ts @@ -245,7 +245,7 @@ describe('createSessionAwareTool', () => { scheme: 'App', projectPath: '/a.xcodeproj', simulatorId: 'SIM-123', - simulatorName: 'iPhone 16', + simulatorName: 'iPhone 17', }); // Call with no args - both come from session defaults diff --git a/src/utils/__tests__/session-store.test.ts b/src/utils/__tests__/session-store.test.ts index 59ae494e..7d45c6a2 100644 --- a/src/utils/__tests__/session-store.test.ts +++ b/src/utils/__tests__/session-store.test.ts @@ -14,10 +14,10 @@ describe('SessionStore', () => { it('should merge defaults on set', () => { sessionStore.setDefaults({ scheme: 'App' }); - sessionStore.setDefaults({ simulatorName: 'iPhone 16' }); + sessionStore.setDefaults({ simulatorName: 'iPhone 17' }); const all = sessionStore.getAll(); expect(all.scheme).toBe('App'); - expect(all.simulatorName).toBe('iPhone 16'); + expect(all.simulatorName).toBe('iPhone 17'); }); it('should clear specific keys', () => { @@ -47,12 +47,12 @@ describe('SessionStore', () => { it('isolates defaults by active profile', () => { sessionStore.setDefaults({ scheme: 'GlobalApp' }); sessionStore.setActiveProfile('ios'); - sessionStore.setDefaults({ scheme: 'iOSApp', simulatorName: 'iPhone 16' }); + sessionStore.setDefaults({ scheme: 'iOSApp', simulatorName: 'iPhone 17' }); sessionStore.setActiveProfile('watch'); sessionStore.setDefaults({ scheme: 'WatchApp' }); sessionStore.setActiveProfile('ios'); - expect(sessionStore.getAll()).toMatchObject({ scheme: 'iOSApp', simulatorName: 'iPhone 16' }); + expect(sessionStore.getAll()).toMatchObject({ scheme: 'iOSApp', simulatorName: 'iPhone 17' }); sessionStore.setActiveProfile('watch'); expect(sessionStore.getAll()).toMatchObject({ scheme: 'WatchApp' }); diff --git a/src/utils/__tests__/simulator-utils.test.ts b/src/utils/__tests__/simulator-utils.test.ts index bdd3b140..b67f347e 100644 --- a/src/utils/__tests__/simulator-utils.test.ts +++ b/src/utils/__tests__/simulator-utils.test.ts @@ -8,7 +8,7 @@ describe('determineSimulatorUuid', () => { 'com.apple.CoreSimulator.SimRuntime.iOS-17-0': [ { udid: 'ABC-123-UUID', - name: 'iPhone 16', + name: 'iPhone 17', isAvailable: true, }, { @@ -49,7 +49,7 @@ describe('determineSimulatorUuid', () => { ); const result = await determineSimulatorUuid( - { simulatorUuid: 'DIRECT-UUID', simulatorName: 'iPhone 16' }, + { simulatorUuid: 'DIRECT-UUID', simulatorName: 'iPhone 17' }, mockExecutor, ); @@ -91,7 +91,7 @@ describe('determineSimulatorUuid', () => { output: mockSimulatorListOutput, }); - const result = await determineSimulatorUuid({ simulatorName: 'iPhone 16' }, mockExecutor); + const result = await determineSimulatorUuid({ simulatorName: 'iPhone 17' }, mockExecutor); expect(result.uuid).toBe('ABC-123-UUID'); expect(result.warning).toBeUndefined(); @@ -142,7 +142,7 @@ describe('determineSimulatorUuid', () => { error: 'simctl command failed', }); - const result = await determineSimulatorUuid({ simulatorName: 'iPhone 16' }, mockExecutor); + const result = await determineSimulatorUuid({ simulatorName: 'iPhone 17' }, mockExecutor); expect(result.uuid).toBeUndefined(); expect(result.error).toBeDefined(); @@ -155,7 +155,7 @@ describe('determineSimulatorUuid', () => { output: 'invalid json {', }); - const result = await determineSimulatorUuid({ simulatorName: 'iPhone 16' }, mockExecutor); + const result = await determineSimulatorUuid({ simulatorName: 'iPhone 17' }, mockExecutor); expect(result.uuid).toBeUndefined(); expect(result.error).toBeDefined(); diff --git a/src/utils/__tests__/xcode-state-reader.test.ts b/src/utils/__tests__/xcode-state-reader.test.ts index 61ec6c9a..d79a810f 100644 --- a/src/utils/__tests__/xcode-state-reader.test.ts +++ b/src/utils/__tests__/xcode-state-reader.test.ts @@ -153,7 +153,7 @@ describe('lookupSimulatorName', () => { const simctlOutput = JSON.stringify({ devices: { 'com.apple.CoreSimulator.SimRuntime.iOS-18-0': [ - { udid: '2FCB5689-88F1-4CDF-9E7F-8E310CD41D72', name: 'iPhone 16' }, + { udid: '2FCB5689-88F1-4CDF-9E7F-8E310CD41D72', name: 'iPhone 17' }, { udid: 'OTHER-UUID', name: 'iPhone 15' }, ], }, @@ -168,7 +168,7 @@ describe('lookupSimulatorName', () => { '2FCB5689-88F1-4CDF-9E7F-8E310CD41D72', ); - expect(result).toBe('iPhone 16'); + expect(result).toBe('iPhone 17'); }); it('returns undefined for unknown UUID', async () => { diff --git a/src/utils/simulator-resolver.ts b/src/utils/simulator-resolver.ts index e0e87f57..80839336 100644 --- a/src/utils/simulator-resolver.ts +++ b/src/utils/simulator-resolver.ts @@ -14,7 +14,7 @@ export type SimulatorResolutionResult = * Resolves a simulator name to its UUID by querying simctl. * * @param executor - Command executor for running simctl - * @param simulatorName - The human-readable simulator name (e.g., "iPhone 16") + * @param simulatorName - The human-readable simulator name (e.g., "iPhone 17") * @returns Resolution result with simulatorId on success, or error message on failure */ export async function resolveSimulatorNameToId(