-
Notifications
You must be signed in to change notification settings - Fork 1.2k
AGENTS.md tweaks #25513
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+260
−26
Merged
AGENTS.md tweaks #25513
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| --- | ||
| name: switching-xcode-scheme | ||
| description: Use when you need to change the active scheme of the running Xcode app (typically before invoking the Xcode MCP BuildProject/RunSomeTests/RunAllTests tools, which always operate on Xcode's currently-selected scheme), or when you need to discover what schemes or run destinations a workspace defines. Listing-only for run destinations — Xcode 26.x's AppleScript cannot persistently change the active destination, so destination switching must be done manually in the Xcode toolbar. | ||
| --- | ||
|
|
||
| # Switching Xcode Scheme | ||
|
|
||
| ## Overview | ||
|
|
||
| The Xcode MCP tools (`BuildProject`, `RunSomeTests`, `RunAllTests`) always act on the active scheme and active destination of the focused Xcode workspace. They take no scheme or destination arguments. To build a different target or run on a different simulator, you must change Xcode's selection first. | ||
|
|
||
| This skill wraps an AppleScript helper that: | ||
| - **Sets the active scheme** of the workspace document Xcode currently has focused (works reliably; verified by read-after-write). | ||
| - **Lists schemes and run destinations** for that workspace, with optional substring filtering. | ||
| - **Cannot change the active run destination** — see the limitation section below. | ||
|
|
||
| ## When to use | ||
|
|
||
| - Before calling Xcode MCP `BuildProject` / `RunSomeTests` / `RunAllTests` tools and the active scheme isn't the target you want. | ||
| - The user asks to "build/test/run scheme X" and you don't know whether X is currently selected. | ||
| - The user asks "what schemes are in this project?" or "what simulators can I run on?" and you want a definitive list straight from Xcode (rather than parsing project files). | ||
|
|
||
| ## When NOT to use | ||
|
|
||
| - Xcode is not running, or no workspace/project is open. The script will fail with a clear message — don't use it as a way to launch Xcode. | ||
| - You need to switch the active run destination (simulator/device). AppleScript silently no-ops on Xcode 26.x. Ask the user to pick the destination in Xcode's toolbar instead. | ||
| - You're working with `xcodebuild` (CLI) rather than driving the Xcode app. `xcodebuild` takes `-scheme` and `-destination` flags directly — this skill is for the IDE, not the CLI. | ||
|
|
||
| ## Quick reference | ||
|
|
||
| The helper lives at `xcode-state.sh` in this skill's directory. Run it directly via its absolute path. | ||
|
|
||
| | Need to do | Command | | ||
| |---|---| | ||
| | See which workspace is focused | `xcode-state.sh workspace` | | ||
| | Read the current scheme | `xcode-state.sh scheme` | | ||
| | Switch the active scheme | `xcode-state.sh scheme "WordPress"` | | ||
| | List all schemes | `xcode-state.sh schemes` | | ||
| | Find schemes matching a pattern | `xcode-state.sh schemes Jetpack` | | ||
| | List all run destinations | `xcode-state.sh destinations` | | ||
| | Find destinations matching a pattern | `xcode-state.sh destinations "iPhone 17 Pro"` | | ||
|
|
||
| Run with no arguments or `help` to print full usage. Exit code 1 means Xcode is not running / no workspace open / item not found; exit code 2 means usage error. | ||
|
|
||
| The scheme `<name>` for `scheme <name>` must match exactly (use the listing command first if unsure). Destination names already include the OS version in parens, e.g. `iPhone 17 Pro (26.4.1)`. | ||
|
|
||
| ## The run destination limitation | ||
|
|
||
| Xcode's AppleScript dictionary defines `active run destination` as a settable property of the workspace document. In Xcode 26.x this property is broken in both directions: | ||
| - **Reads** always return `missing value`, even immediately after a successful set and even when Xcode visibly has a destination selected. | ||
| - **Writes** don't error, but the underlying `xcuserstate` doesn't update and there's no observable change in subsequent Xcode behavior. | ||
|
|
||
| UI scripting via System Events can drive Xcode's toolbar dropdown, but it requires the invoker to have Accessibility permission, which Claude Code typically doesn't have. So this skill doesn't attempt destination writes. If destination switching is essential, ask the user to change it in the Xcode toolbar before running an MCP build. | ||
|
|
||
| ## Common mistakes | ||
|
|
||
| - **Forgetting that scheme set is a precondition for the Xcode MCP tools.** If you call `BuildProject` without first switching scheme, it builds whatever was selected — possibly not what the user asked for. Read the current scheme first if you're unsure. | ||
| - **Passing a substring to `scheme <name>`.** That command requires an exact match. For substring search, use `schemes <pattern>` to find the full name first. | ||
| - **Treating "set destination" as a no-op safely.** The AppleScript `set` will not error, but the destination will not change. Don't claim success based on the absence of an error. | ||
| - **Operating on the wrong workspace.** The helper always targets `active workspace document`. If the user has multiple Xcode windows open, confirm with `xcode-state.sh workspace` before switching schemes. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,192 @@ | ||
| #!/bin/bash | ||
| # Read or change the active scheme of the workspace currently focused in | ||
| # the running Xcode application, and list the run destinations available | ||
| # to that workspace. | ||
| # | ||
| # Operates on Xcode's `active workspace document`. Xcode must already be | ||
| # running with at least one workspace/project open — this script does not | ||
| # launch Xcode and does not open files. | ||
| # | ||
| # Subcommands: | ||
| # workspace Print the path of the active workspace document. | ||
| # schemes [substring] List schemes (newline-delimited), optionally | ||
| # filtered by case-insensitive substring. | ||
| # scheme Print the active scheme's name. | ||
| # scheme <name> Set the active scheme. <name> must match exactly. | ||
| # destinations [substring] | ||
| # List run destinations (newline-delimited), | ||
| # optionally filtered by case-insensitive | ||
| # substring. Destination names already include | ||
| # the OS version in parens, e.g. | ||
| # "iPhone 17 Pro (26.4.1)". | ||
| # | ||
| # This script intentionally does NOT support reading or setting the | ||
| # active run destination. Xcode's AppleScript dictionary advertises | ||
| # `active run destination` as a property of the workspace document, but | ||
| # in Xcode 26.x reads always return `missing value` and writes appear to | ||
| # silently no-op. Until Apple fixes this, change the destination by hand | ||
| # in Xcode's toolbar, or use UI scripting via System Events (which | ||
| # requires Accessibility permission). | ||
| # | ||
| # Exit codes: | ||
| # 0 success | ||
| # 1 Xcode not running, or no workspace open, or item not found | ||
| # 2 usage error | ||
|
|
||
| set -euo pipefail | ||
|
|
||
| usage() { | ||
| awk 'NR>1 && /^#/ {sub(/^# ?/, ""); print; next} NR>1 {exit}' "$0" | ||
| exit 2 | ||
| } | ||
|
|
||
| # Run an AppleScript snippet against Xcode after first verifying that | ||
| # Xcode is running and that it has a workspace document open. The wrapper | ||
| # converts those preconditions into clean, single-line error messages | ||
| # instead of the noisy AppleScript stack traces you'd otherwise get. | ||
| run_osa() { | ||
| local script="$1" | ||
| osascript <<APPLESCRIPT | ||
| if application "Xcode" is not running then | ||
| return "ERR:not_running" | ||
| end if | ||
| tell application "Xcode" | ||
| if (count of workspace documents) = 0 then | ||
| return "ERR:no_workspace" | ||
| end if | ||
| set ws to active workspace document | ||
| ${script} | ||
| end tell | ||
| APPLESCRIPT | ||
| } | ||
|
|
||
| handle_error() { | ||
| local result="$1" | ||
| case "$result" in | ||
| ERR:not_running) | ||
| echo "error: Xcode is not running" >&2 | ||
| exit 1 | ||
| ;; | ||
| ERR:no_workspace) | ||
| echo "error: Xcode has no workspace or project open" >&2 | ||
| exit 1 | ||
| ;; | ||
| ERR:scheme_not_found) | ||
| echo "error: no scheme matching the requested name" >&2 | ||
| echo "hint: run 'xcode-state.sh schemes' to list available schemes" >&2 | ||
| exit 1 | ||
| ;; | ||
| ERR:no_active_scheme) | ||
| echo "error: workspace has no active scheme" >&2 | ||
| exit 1 | ||
| ;; | ||
| esac | ||
| } | ||
|
|
||
| cmd_workspace() { | ||
| local result | ||
| result=$(run_osa 'return (path of ws as string)') | ||
| handle_error "$result" | ||
| echo "$result" | ||
| } | ||
|
|
||
| # Newline-delimited list. AppleScript's text item delimiters are the | ||
| # standard way to join a list into a string with a custom separator. | ||
| list_named() { | ||
| local elementName="$1" | ||
| local filter="${2:-}" | ||
| local result | ||
| result=$(run_osa " | ||
| set savedTID to AppleScript's text item delimiters | ||
| set AppleScript's text item delimiters to linefeed | ||
| set out to (name of every $elementName of ws) as string | ||
| set AppleScript's text item delimiters to savedTID | ||
| return out | ||
| ") | ||
| handle_error "$result" | ||
| if [[ -n "$filter" ]]; then | ||
| echo "$result" | grep -i -- "$filter" || { | ||
| echo "error: nothing matching '$filter'" >&2 | ||
| exit 1 | ||
| } | ||
| else | ||
| echo "$result" | ||
| fi | ||
| } | ||
|
|
||
| cmd_schemes() { | ||
| list_named "scheme" "${1:-}" | ||
| } | ||
|
|
||
| cmd_destinations() { | ||
| list_named "run destination" "${1:-}" | ||
| } | ||
|
|
||
| cmd_get_scheme() { | ||
| local result | ||
| result=$(run_osa ' | ||
| try | ||
| return name of active scheme of ws | ||
| on error | ||
| return "ERR:no_active_scheme" | ||
| end try | ||
| ') | ||
| handle_error "$result" | ||
| echo "$result" | ||
| } | ||
|
|
||
| cmd_set_scheme() { | ||
| local name="$1" | ||
| local escaped | ||
| escaped=$(printf '%s' "$name" | sed 's/"/\\"/g') | ||
| local result | ||
| result=$(run_osa " | ||
| try | ||
| set s to first scheme of ws whose name is \"$escaped\" | ||
| on error | ||
| return \"ERR:scheme_not_found\" | ||
| end try | ||
| set active scheme of ws to s | ||
| return name of active scheme of ws | ||
| ") | ||
| handle_error "$result" | ||
| echo "$result" | ||
| } | ||
|
|
||
| if [[ $# -eq 0 ]]; then | ||
| usage | ||
| fi | ||
|
|
||
| cmd="$1" | ||
| shift || true | ||
|
|
||
| case "$cmd" in | ||
| workspace) | ||
| [[ $# -eq 0 ]] || usage | ||
| cmd_workspace | ||
| ;; | ||
| schemes) | ||
| [[ $# -le 1 ]] || usage | ||
| cmd_schemes "${1:-}" | ||
| ;; | ||
| scheme) | ||
| if [[ $# -eq 0 ]]; then | ||
| cmd_get_scheme | ||
| elif [[ $# -eq 1 ]]; then | ||
| cmd_set_scheme "$1" | ||
| else | ||
| usage | ||
| fi | ||
| ;; | ||
| destinations) | ||
| [[ $# -le 1 ]] || usage | ||
| cmd_destinations "${1:-}" | ||
| ;; | ||
| -h|--help|help) | ||
| usage | ||
| ;; | ||
| *) | ||
| echo "error: unknown command '$cmd'" >&2 | ||
| usage | ||
| ;; | ||
| esac |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
swift-format puts the opening brace in a new line when there are multiple lines of conditions in the if. There is no option to tweak that configuration in swift-format. So, I updated the swift-lint rule configuration to make swiftlint and swift-format to be compatible.