Repository navigation
feat: xcforge skill install - #7
Conversation
Route every xcodebuild call through one entry point that applies per-call options (DerivedData path, exact result bundle, extra args), holds an optional first-come, first-served build lock compatible with lockf(1), and warns on low disk or heavy swap. - simRecovery defaults to off; auto only reboots, erase must be asked for - never pkill every xcodebuild; hang snapshots match this run's bundle - WDA port cleanup only kills WDA runners on the configured local port - collision-free result bundle names; per-project last-result records used by build diagnose, test failures and test coverage - --isolated-sim runs on a throwaway simulator - xcforge lock status / build_lock_status Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NVNakyntY4u9MvhsjbzRhZ
…sting tests Adds an idle timeout (600s of no output) to every build and test xcodebuild, raises the total limit to 1800s, retries test-without-building once when the runner never starts, continues building after errors and reads diagnostics from stdout, lists tests through JSON enumeration (Swift Testing included, with --testplan), and fixes test plan discovery and decoding. Also fixes the Bundle 1 test that failed to type-check and a lint nit. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NVNakyntY4u9MvhsjbzRhZ
…ice Hub Adds wda start/status/stop (CLI and MCP) to build, sign, launch and reach xcforgeWDA on a device, device screenshots, deep links and env vars on device launch, clear messages for locked devices, UI Automation off and signing failures, and stops simulator self-heal from running against device targets. Opens Device Hub when Simulator.app is gone and accepts both apps in capture and accessibility code. Raises xcforgeWDA's deployment target to iOS 15. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NVNakyntY4u9MvhsjbzRhZ
Auto-promotion becomes opt-in, xcforge's own build flags can be turned off, lldb continue and simulator launch timeouts are longer and configurable, bless stops suggesting commit messages, and tool text and the skill lose preference claims and marketing copy. Fixes stale paths in CLAUDE.md. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NVNakyntY4u9MvhsjbzRhZ
Suites that stub WDA's localhost URL ran in parallel and could answer each other's requests. A shared async gate now holds each stubbed region. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NVNakyntY4u9MvhsjbzRhZ
- Cancelling an MCP call, a timeout, or xcforge exiting now kills the whole child process tree, so xcodebuild no longer keeps DerivedData locked. - MCP calls with a progress token get a notification every 10s with the latest output line, so clients don't time out long builds. - xcodebuild calls on the same DerivedData or project queue in-process. - An explicit project uses its own repo's .xcforge.yaml; results name the project used; yaml quotes and comments are handled and problems warned. - Unknown MCP arguments are rejected with the closest real name. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
- One test ID format (Target/Suite/test()) in results, list_tests, last-failures and known-failures; filters accept it as printed, and short IDs get the target from the scheme or test plan - Every failure message with file and line, argument/repetition label, attachments and optional console tail; identical messages grouped, text output capped with a count of the rest - Run options on test_sim/build_and_test and the CLI: retries, iterations, until-failure, parallel, per-test timeout, skip build - Flaky tests (failed then passed on retry) reported - rerun-failed reuses recorded run settings, skips the build when no source changed, and refuses when the last run failed before any test - Test failures are no longer reported as a test target build failure; agent JSON gives a reason when no test reported Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
- CLI test run shows a failed test build as compile errors with file and line; test_sim uses the parsed build errors too - test failures never runs tests: it reads the last test run, says when there is none, and returns compile errors when that run's build-for-testing failed instead of an older run's failures - build-for-testing passes the test plan and builds only the targets a filter names; the last-build record tracks plan and targets - build compile, build-for-testing, test and test list share build flags - hang watchdog samples at 300s/540s and only reports on timeout or diagnose - build compile skips -showBuildSettings and compiles without a booted simulator - one simulator name lookup everywhere (booted first, newest OS, no prefix matches) - a zero-match filter lists suggestions without rebuilding - agent JSON carries the result bundle and the timeout reason - build run exits non-zero when the app didn't launch Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
- launch_app, build_run_sim, sim launch and build run check the app is still running 2s after launch and report the crash (exception, reason, top frames, .ips path) when it isn't - Launch args, env, deep link and terminate options; new open_url and sim openurl - build_run_sim boots after a successful build, waits for boot, and installs the scheme's application target from -showBuildSettings -json - CLI log and console capture run in the background and survive the start command; reads default to the newest 200 lines and say what was left out; wait_for_log no longer clears the buffer - crashes topic matches error/fault lines instead of default-level ones - Auto-detect ignores the workspace inside .xcodeproj and picks the project-named scheme; clean takes a configuration and can delete this project's DerivedData - A new process loads the last build's bundle ID and app path Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
- Every UI tool takes simulator; each simulator gets its own WDA port (saved in ~/.xcforge/wda/simulator-ports.json), XCFORGE_SIMULATOR for the CLI - Recreated WDA sessions send forceAppLaunch:false; restart reruns the cached xctestrun with test-without-building and the runner has no one-hour cap - The AXP fast path runs only with one simulator booted and no phone, reads that simulator's windows, and get_source/list_elements start WDA instead of falling back to Simulator.app - Smaller trees: empty wrappers, hidden and off-screen nodes dropped; value, disabled and selected shown - find_element reports every match and takes index/timeout/until_gone; actions take wait_for, until_gone and timeout - type_text types into the focused field, with key, dismiss_keyboard and secure (redacted) options - alert_action / XCFORGE_ALERT_ACTION and a visible-alert hint in not-found errors - Screenshots report device points, take crop and max_dimension, and never share a fixed /tmp file - sim_content_size, sim_locale, app_container, sim_push, sim_privacy (and sim content-size|locale|container|push|privacy) - Accessibility, localization and multi-device checks restore the text size, language and appearance they found - Coordinate taps and swipes account for interface orientation Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
- build typecheck / build_typecheck compiles one target (its scheme, else -target) - --from-snapshot builds a git-worktree snapshot of the working tree; errors map back - lsp setup / lsp_setup writes buildServer.json pointed at xcforge's DerivedData - --jobs, --all-errors with a separate diagnostic DerivedData slot - SwiftPM tools find the package (path, yaml packagePath, cwd, only package in repo), parse errors and test failures, and take the build lock and idle timeout Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
bb44941 to
9a6a8d6
Compare
|
That file comes from PR #6's bundle 11 commit, not from this PR. The same code is at the head of #6's branch ( Generated by Claude Code |
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
9a6a8d6 to
26c65c0
Compare
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
xcforge-web | 67059fa | Oct 09 2026, 09:38 PM |
- tap replaces eight tap tools; swipe takes hid, get_source takes format: list, find_element takes all, screenshot takes device. Old names stay callable for one release with a note naming the replacement - The diagnose group starts off; XCFORGE_TOOL_GROUPS / yaml toolGroups pick the starting groups, and tool_groups changes send notifications/tools/list_changed - Every listed argument is camelCase; snake_case is still accepted - build_compile, build_sim, build_typecheck, test_sim and build_and_test return compact JSON by default over MCP (for: human for text); diagnose and plan JSON are compact - Read-only tools carry readOnlyHint - test_sim rerunFailed - plan decide resumes from a saved session in a new CLI process - defaults set finds the project first and fails when nothing is saved; a saved simulator that no longer exists is ignored - Last results keyed on the canonical project path, written under a lock - xcforge --version; CLI JSON errors on stdout - Docs: tool-surface reference, counts checked by a test Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
Device Hub's devicectl lists simulators alongside phones. Drop entries that report a non-physical reality, a simulator platform or device type, or a UDID simctl knows, and read the state from the tunnel instead of visibilityClass. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
properties groups fields as hardware, state and connection; simulators report reality: simulated, visibilityClass: simulators and a CoreSimulator provider. Tests use shapes captured from Xcode 27.2. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
936aaa7 to
e9295ac
Compare
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
- typecheck --target builds into the configured DerivedData via SYMROOT/OBJROOT; xcodebuild usage errors are reported as xcodebuild_usage - HID input finds SimulatorKit in Xcode 27's SharedFrameworks; WDA fallbacks say why - launches are watched 8s and wait up to 15s for a late crash report - relative --project paths resolve against the working directory - first WDA start gets time to build and start, and failures name the cause - lock status skips queued xcforge processes; timed-out test runs aren't miscounted; unreported snapshots and old artifacts are cleaned up - landscape screenshot sizes, --simulator on every ui command, JSON errors with --json, bless help text Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
The stub answered every loopback request, so another suite's ungated createSession against 127.0.0.1:1 could take the queued POST /session response and fail the healthy-path test. It now intercepts port 8100 only. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
e9295ac to
291380c
Compare
--help, help <command> and --version end in a parser error with a success exit code; the JSON error path wrapped it as execution_failed and exited 1 whenever output was piped. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
On a loaded runner the 10ms deadline's capture hadn't finished after 200ms, so the result and file were missing. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
…er config The skill is embedded in the binary (scripts/embed-skill.py generates Sources/XCForgeKit/Skill/EmbeddedSkill.swift from Skills/xcforge; a test fails when they differ). `xcforge skill install` writes it to .claude/skills/xcforge at the repo root, --global to ~/.claude/skills (or $CLAUDE_CONFIG_DIR/skills), or --dir for another agent. A manifest records what was written so reinstalling refreshes changed files, drops ones no longer shipped, and never overwrites a directory it didn't create without --force. Also: skill status, show, uninstall. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WcpeCPev4gs4R1Y94UvRUb
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WcpeCPev4gs4R1Y94UvRUb
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WcpeCPev4gs4R1Y94UvRUb
291380c to
6943164
Compare
lookup:error: returns a mach_port_t; calling it through perform() treated the port as an object and crashed ui tap. Call it by its real signature, and return an error for an unexpected one. Screenshot point sizes now follow the interface orientation from WDA, since the framebuffer stays portrait. Document that the lock queue orders xcforge waiters only. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
wda start spots WDA's "Unable to start web server" log line and fails at once with a --port hint, reports errors as JSON under --json, and builds into the configured DerivedData folder. Starting or restarting the simulator runner stops any xcforgeWDA runner already on that simulator. ui source accepts --format list and rejects unknown formats up front. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
On Xcode 27 IndigoHID events are sent without error but never reach the app, so results said "via IndigoHID" for taps that didn't land. HID is now off on Xcode 27 (XCFORGE_FORCE_HID=1 overrides) and taps and swipes use WDA, saying so in the result. Add iPhone 16 Pro, 17 family and Air screen sizes to the HID table. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
Starting WDA ran xcodebuild against the target simulator, which boots it when it is shut down; with another simulator already booted that left two running, which exhausts memory on a 16 GB Mac. UI commands now fail with "Simulator <name> is shutdown; boot it first" and name any booted one. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
…-install-jbjp2g Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
…-install-jbjp2g Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
Requested by Justin · project thread
Before: the only way to install the xcforge skill was
npx skills justinthevoid/xcforge, and nothing told you when an installed copy no longer matched your xcforge.After:
xcforge skill installwrites the skill to.claude/skills/xcforgeat the repo root.--globalwrites it to~/.claude/skills/xcforgeinstead (or to$CLAUDE_CONFIG_DIR/skillswhen that's set), and--dir <skills-dir>writes it into any other agent's skills folder. Running it again after an upgrade refreshes the copy.xcforge skill statusshows where the skill is installed and whether it's current,skill show [file]prints a file, andskill uninstallremoves it.How:
scripts/embed-skill.pyturnsSkills/xcforgeintoSources/XCForgeKit/Skill/EmbeddedSkill.swift, so the skill ships inside the single Homebrew binary. A test fails whenever the embedded copy andSkills/differ. Each install writes a.xcforge-skill.jsonmanifest. A reinstall rewrites only changed files and removes files the new version no longer ships. It leaves alone anyxcforgedirectory this command didn't create, such as a hand copy or an npx install, unless you pass--force.Stacking: this PR is based on PR #6's branch (
claude/project-thread-7t3aif), which now includes bundle 12. The diff against main also includes #5 and #6. Only the last three commits belong to this PR: the feature, a status-string fix, and a re-embed of bundle 12's skill docs. Merge #5 and #6 first.🤖 Generated with Claude Code
https://claude.ai/code/session_01WcpeCPev4gs4R1Y94UvRUb