Skip to content

feat: xcforge skill install - #7

Merged
justinthevoid merged 34 commits into
mainfrom
claude/xcforge-skill-install-jbjp2g
Oct 9, 2026
Merged

justinthevoid merged 34 commits into
mainfrom
claude/xcforge-skill-install-jbjp2g

Conversation

@justinthevoid

@justinthevoid justinthevoid commented Oct 9, 2026 •

Copy link
Copy Markdown
Owner

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 install writes the skill to .claude/skills/xcforge at the repo root. --global writes it to ~/.claude/skills/xcforge instead (or to $CLAUDE_CONFIG_DIR/skills when 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 status shows where the skill is installed and whether it's current, skill show [file] prints a file, and skill uninstall removes it.

How: scripts/embed-skill.py turns Skills/xcforge into Sources/XCForgeKit/Skill/EmbeddedSkill.swift, so the skill ships inside the single Homebrew binary. A test fails whenever the embedded copy and Skills/ differ. Each install writes a .xcforge-skill.json manifest. A reinstall rewrites only changed files and removes files the new version no longer ships. It leaves alone any xcforge directory 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

claude added 14 commits October 9, 2026 14:12
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
@justinthevoid justinthevoid self-assigned this Oct 9, 2026
@justinthevoid
justinthevoid force-pushed the claude/xcforge-skill-install-jbjp2g branch from bb44941 to 9a6a8d6 Compare October 9, 2026 17:06

Copy link
Copy Markdown
Owner Author

build-and-test fails at Sources/XCForgeKit/Tools/SwiftPackageProvider.swift:179. The error is type 'SwiftPackageTools.SPMResult' does not conform to protocol 'Error' (Result<String, SPMResult>).

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 (d8a89cb), and this PR's one commit doesn't touch it. No fix exists yet. The thread that owns #6 will fix it there, and I'll rebase this branch onto that fix so the skill-install code gets a real build and test run.


Generated by Claude Code

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EtxtFPs6UBAWXqEpkj2W2N
@justinthevoid
justinthevoid force-pushed the claude/xcforge-skill-install-jbjp2g branch from 9a6a8d6 to 26c65c0 Compare October 9, 2026 17:12
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
xcforge-web 67059fa Oct 09 2026, 09:38 PM

claude added 4 commits October 9, 2026 17:16
- 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
@justinthevoid
justinthevoid force-pushed the claude/xcforge-skill-install-jbjp2g branch from 936aaa7 to e9295ac Compare October 9, 2026 17:48
@justinthevoid
justinthevoid marked this pull request as ready for review October 9, 2026 18:01
Copilot AI balanced review requested due to automatic review settings October 9, 2026 18:01
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

- 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
@justinthevoid
justinthevoid force-pushed the claude/xcforge-skill-install-jbjp2g branch from e9295ac to 291380c Compare October 9, 2026 19:18
claude added 5 commits October 9, 2026 19:32
--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
@justinthevoid
justinthevoid force-pushed the claude/xcforge-skill-install-jbjp2g branch from 291380c to 6943164 Compare October 9, 2026 19:53
claude added 8 commits October 9, 2026 20:20
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
@justinthevoid
justinthevoid merged commit 8903c9d into main Oct 9, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants