xcodebuild-axi 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +35 -0
- package/LICENSE +21 -0
- package/README.md +304 -0
- package/dist/bin/xcodebuild-axi.d.ts +2 -0
- package/dist/bin/xcodebuild-axi.js +8 -0
- package/dist/bin/xcodebuild-axi.js.map +1 -0
- package/dist/src/action.d.ts +68 -0
- package/dist/src/action.js +373 -0
- package/dist/src/action.js.map +1 -0
- package/dist/src/archive.d.ts +33 -0
- package/dist/src/archive.js +77 -0
- package/dist/src/archive.js.map +1 -0
- package/dist/src/args.d.ts +23 -0
- package/dist/src/args.js +123 -0
- package/dist/src/args.js.map +1 -0
- package/dist/src/auth.d.ts +11 -0
- package/dist/src/auth.js +51 -0
- package/dist/src/auth.js.map +1 -0
- package/dist/src/cli.d.ts +20 -0
- package/dist/src/cli.js +132 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/commands/analyze.d.ts +2 -0
- package/dist/src/commands/analyze.js +32 -0
- package/dist/src/commands/analyze.js.map +1 -0
- package/dist/src/commands/archive.d.ts +2 -0
- package/dist/src/commands/archive.js +63 -0
- package/dist/src/commands/archive.js.map +1 -0
- package/dist/src/commands/build.d.ts +2 -0
- package/dist/src/commands/build.js +60 -0
- package/dist/src/commands/build.js.map +1 -0
- package/dist/src/commands/clean.d.ts +2 -0
- package/dist/src/commands/clean.js +39 -0
- package/dist/src/commands/clean.js.map +1 -0
- package/dist/src/commands/coverage.d.ts +2 -0
- package/dist/src/commands/coverage.js +111 -0
- package/dist/src/commands/coverage.js.map +1 -0
- package/dist/src/commands/destinations.d.ts +2 -0
- package/dist/src/commands/destinations.js +56 -0
- package/dist/src/commands/destinations.js.map +1 -0
- package/dist/src/commands/export.d.ts +2 -0
- package/dist/src/commands/export.js +170 -0
- package/dist/src/commands/export.js.map +1 -0
- package/dist/src/commands/find.d.ts +2 -0
- package/dist/src/commands/find.js +70 -0
- package/dist/src/commands/find.js.map +1 -0
- package/dist/src/commands/home.d.ts +9 -0
- package/dist/src/commands/home.js +97 -0
- package/dist/src/commands/home.js.map +1 -0
- package/dist/src/commands/info.d.ts +2 -0
- package/dist/src/commands/info.js +95 -0
- package/dist/src/commands/info.js.map +1 -0
- package/dist/src/commands/localize.d.ts +2 -0
- package/dist/src/commands/localize.js +121 -0
- package/dist/src/commands/localize.js.map +1 -0
- package/dist/src/commands/migrate.d.ts +8 -0
- package/dist/src/commands/migrate.js +177 -0
- package/dist/src/commands/migrate.js.map +1 -0
- package/dist/src/commands/packages.d.ts +2 -0
- package/dist/src/commands/packages.js +108 -0
- package/dist/src/commands/packages.js.map +1 -0
- package/dist/src/commands/platforms.d.ts +2 -0
- package/dist/src/commands/platforms.js +379 -0
- package/dist/src/commands/platforms.js.map +1 -0
- package/dist/src/commands/result.d.ts +2 -0
- package/dist/src/commands/result.js +100 -0
- package/dist/src/commands/result.js.map +1 -0
- package/dist/src/commands/schemes.d.ts +2 -0
- package/dist/src/commands/schemes.js +39 -0
- package/dist/src/commands/schemes.js.map +1 -0
- package/dist/src/commands/settings.d.ts +2 -0
- package/dist/src/commands/settings.js +191 -0
- package/dist/src/commands/settings.js.map +1 -0
- package/dist/src/commands/setup.d.ts +2 -0
- package/dist/src/commands/setup.js +84 -0
- package/dist/src/commands/setup.js.map +1 -0
- package/dist/src/commands/sim.d.ts +2 -0
- package/dist/src/commands/sim.js +187 -0
- package/dist/src/commands/sim.js.map +1 -0
- package/dist/src/commands/test.d.ts +2 -0
- package/dist/src/commands/test.js +317 -0
- package/dist/src/commands/test.js.map +1 -0
- package/dist/src/commands/testplans.d.ts +8 -0
- package/dist/src/commands/testplans.js +75 -0
- package/dist/src/commands/testplans.js.map +1 -0
- package/dist/src/commands/tests.d.ts +13 -0
- package/dist/src/commands/tests.js +154 -0
- package/dist/src/commands/tests.js.map +1 -0
- package/dist/src/commands/xcframework.d.ts +2 -0
- package/dist/src/commands/xcframework.js +77 -0
- package/dist/src/commands/xcframework.js.map +1 -0
- package/dist/src/context.d.ts +23 -0
- package/dist/src/context.js +72 -0
- package/dist/src/context.js.map +1 -0
- package/dist/src/destination.d.ts +50 -0
- package/dist/src/destination.js +174 -0
- package/dist/src/destination.js.map +1 -0
- package/dist/src/errors.d.ts +6 -0
- package/dist/src/errors.js +55 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/packages.d.ts +31 -0
- package/dist/src/packages.js +117 -0
- package/dist/src/packages.js.map +1 -0
- package/dist/src/report.d.ts +20 -0
- package/dist/src/report.js +39 -0
- package/dist/src/report.js.map +1 -0
- package/dist/src/scheme.d.ts +22 -0
- package/dist/src/scheme.js +66 -0
- package/dist/src/scheme.js.map +1 -0
- package/dist/src/simctl.d.ts +33 -0
- package/dist/src/simctl.js +85 -0
- package/dist/src/simctl.js.map +1 -0
- package/dist/src/surface.d.ts +55 -0
- package/dist/src/surface.js +350 -0
- package/dist/src/surface.js.map +1 -0
- package/dist/src/toon.d.ts +34 -0
- package/dist/src/toon.js +79 -0
- package/dist/src/toon.js.map +1 -0
- package/dist/src/version.d.ts +1 -0
- package/dist/src/version.js +24 -0
- package/dist/src/version.js.map +1 -0
- package/dist/src/xccov.d.ts +25 -0
- package/dist/src/xccov.js +78 -0
- package/dist/src/xccov.js.map +1 -0
- package/dist/src/xcodebuild.d.ts +65 -0
- package/dist/src/xcodebuild.js +163 -0
- package/dist/src/xcodebuild.js.map +1 -0
- package/dist/src/xcresult.d.ts +96 -0
- package/dist/src/xcresult.js +156 -0
- package/dist/src/xcresult.js.map +1 -0
- package/package.json +75 -0
- package/skills/xcodebuild-axi/SKILL.md +64 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Notable changes to `xcodebuild-axi`. Versions follow
|
|
4
|
+
[semver](https://semver.org/); the format follows
|
|
5
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.1.0] - 2026-09-21
|
|
10
|
+
|
|
11
|
+
First release.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **`build`, `test`, `tests`, `clean`, `analyze`, `archive`, `export`** — run an
|
|
16
|
+
action and report from the `.xcresult` bundle rather than the transcript.
|
|
17
|
+
Diagnostics carry `file,line,col`; the full log is streamed to a file and
|
|
18
|
+
never read into memory.
|
|
19
|
+
- **`schemes`, `destinations`, `testplans`, `settings`, `packages`, `info`** —
|
|
20
|
+
read-only queries that answer one question each.
|
|
21
|
+
- **`result`, `coverage`** — re-read a previous run's bundle without rebuilding.
|
|
22
|
+
- **`sim`, `platforms`** — inspect and boot simulators, list runtimes, and drive
|
|
23
|
+
platform downloads.
|
|
24
|
+
- **`localize`, `xcframework`, `find`, `migrate`** — XLIFF import/export,
|
|
25
|
+
`.xcframework` assembly, toolchain lookup, and project-format conversion.
|
|
26
|
+
- **`setup hooks`** — session-start hooks for Claude Code, Codex, and OpenCode,
|
|
27
|
+
plus a bundled skill for agents that read the skill format.
|
|
28
|
+
- Destinations resolve to a simulator **udid** before the run, so a duplicated
|
|
29
|
+
device name cannot silently send a run to the wrong runtime, and the reported
|
|
30
|
+
destination is read back out of the result bundle.
|
|
31
|
+
- 100% of the 117 options `xcodebuild -help` lists are covered, declared in
|
|
32
|
+
`src/surface.ts` and checked against the installed Xcode in CI.
|
|
33
|
+
|
|
34
|
+
[unreleased]: https://github.com/alexrrouse/xcodebuild-axi/compare/v0.1.0...HEAD
|
|
35
|
+
[0.1.0]: https://github.com/alexrrouse/xcodebuild-axi/releases/tag/v0.1.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alex Rouse
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
<h1 align="center">xcodebuild-axi</h1>
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<a href="https://www.npmjs.com/package/xcodebuild-axi"><img alt="npm" src="https://img.shields.io/npm/v/xcodebuild-axi?style=flat-square" /></a>
|
|
5
|
+
<a href="https://axi.md/"><img alt="AXI" src="https://img.shields.io/badge/AXI-compliant-blue?style=flat-square" /></a>
|
|
6
|
+
<img alt="Platform" src="https://img.shields.io/badge/platform-macOS-lightgrey?style=flat-square" />
|
|
7
|
+
<!-- coverage-badge:start --><img alt="xcodebuild coverage" src="https://img.shields.io/badge/xcodebuild_coverage-100%25-brightgreen?style=flat-square" /><!-- coverage-badge:end -->
|
|
8
|
+
<img alt="License" src="https://img.shields.io/badge/license-MIT-green?style=flat-square" />
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<h3 align="center">A passing test run should not cost 549 KB of context.</h3>
|
|
12
|
+
|
|
13
|
+
`xcodebuild` tells an agent everything and therefore nothing. One passing test
|
|
14
|
+
run of a single app in a real iOS workspace prints **549,290 bytes across 6,595
|
|
15
|
+
lines** — 767 of which say a test passed, and none of which say anything went
|
|
16
|
+
wrong. A full verify run of the same repo prints 2.5 MB. An agent that has to
|
|
17
|
+
read that to learn one number pays for it on every change, several times over.
|
|
18
|
+
|
|
19
|
+
`xcodebuild-axi` runs the same build and reports the answer.
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
$ xcodebuild-axi test --scheme MyApp --device "iPhone 17 Pro"
|
|
23
|
+
test: passed
|
|
24
|
+
scheme: MyApp
|
|
25
|
+
destination: iPhone 17 Pro · iOS Simulator 26.5
|
|
26
|
+
tests: 767 passed / 0 failed / 0 skipped
|
|
27
|
+
duration: 15m34s
|
|
28
|
+
log: ~/Library/Caches/xcodebuild-axi/MyApps-1a2b3c4d/MyApp-iPhone-17-Pro-test.log
|
|
29
|
+
result: ~/Library/Caches/xcodebuild-axi/MyApps-1a2b3c4d/MyApp-iPhone-17-Pro-test.xcresult
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The whole transcript still lands in `log`, so nothing is lost — it just stops
|
|
33
|
+
being the default answer.
|
|
34
|
+
|
|
35
|
+
## Why it can be this small
|
|
36
|
+
|
|
37
|
+
The transcript is not the best record of a build. The `.xcresult` bundle
|
|
38
|
+
xcodebuild writes alongside it is: a few KB of JSON carrying pass/fail counts,
|
|
39
|
+
the device the run actually landed on, and every diagnostic with a precise
|
|
40
|
+
source location. `xcodebuild-axi` streams the transcript straight to a log file
|
|
41
|
+
it never reads into memory, then reports from the bundle.
|
|
42
|
+
|
|
43
|
+
That also makes it more accurate than grepping. A failed build reports the real
|
|
44
|
+
error with the real line:
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
$ xcodebuild-axi build --scheme Probe
|
|
48
|
+
build: failed
|
|
49
|
+
scheme: Probe
|
|
50
|
+
destination: My Mac · macOS 26.6.1
|
|
51
|
+
duration: 4.6s
|
|
52
|
+
errors[1]{file,line,col,type,message}:
|
|
53
|
+
Sources/Probe/Probe.swift,2,25,swift,Cannot convert value of type 'String' to specified type 'Int'
|
|
54
|
+
log: ~/Library/Caches/xcodebuild-axi/Probe-9f8e7d6c/Probe-My-Mac-build.log
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
21,636 bytes of transcript, one line of answer.
|
|
58
|
+
|
|
59
|
+
## Measured
|
|
60
|
+
|
|
61
|
+
Every number below is produced by `npm run benchmark`, which asks raw
|
|
62
|
+
`xcodebuild` and `xcodebuild-axi` the same question against a real 12-scheme
|
|
63
|
+
iOS workspace with 16 local Swift packages, and tokenizes both answers.
|
|
64
|
+
|
|
65
|
+
<!-- benchmark:start -->
|
|
66
|
+
|
|
67
|
+
| Question | `xcodebuild` | `xcodebuild-axi` | Saved |
|
|
68
|
+
| --------------------------------------- | -------------- | ---------------- | ---------- |
|
|
69
|
+
| what can I build? | 498 tok | 97 tok | **80.52%** |
|
|
70
|
+
| what can I run it on? | 1,720 tok | 339 tok | **80.29%** |
|
|
71
|
+
| what is the bundle id? | 12,221 tok | 21 tok | **99.83%** |
|
|
72
|
+
| which targets have index settings? | 61,850 tok | 42 tok | **99.93%** |
|
|
73
|
+
| which SDKs are installed? | 244 tok | 70 tok | **71.31%** |
|
|
74
|
+
| which test plans does this scheme have? | 445 tok | 42 tok | **90.56%** |
|
|
75
|
+
| **all 6 together** | **76,978 tok** | **611 tok** | **99.21%** |
|
|
76
|
+
|
|
77
|
+
Token counts are GPT-4o BPE via `gpt-tokenizer` — Anthropic's tokenizer is not public, so this is a stand-in, and the ratios are what matter rather than the absolute numbers. Both stdout and stderr are counted, because that is what an agent running the command in a shell actually reads. Measured by `npm run benchmark` against one app of a real multi-scheme iOS workspace on 2026-09-21.
|
|
78
|
+
|
|
79
|
+
<!-- benchmark:end -->
|
|
80
|
+
|
|
81
|
+
Those are the read-only commands, and they win anyway, because xcodebuild
|
|
82
|
+
reprints its invocation, `Resolve Package Graph`, and the full resolved package
|
|
83
|
+
list on _every_ call — about 1.2 KB of identical preamble in front of a
|
|
84
|
+
349-byte answer.
|
|
85
|
+
|
|
86
|
+
The `build` and `test` rows are where the margin is widest and are not in the
|
|
87
|
+
table above: they drive a cold build per side and want a quiet machine. Add
|
|
88
|
+
them with
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
npm run benchmark -- --project ~/YourApp --scheme YourScheme --only build --resume --write
|
|
92
|
+
npm run benchmark -- --project ~/YourApp --scheme YourScheme --only test --resume --write
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
For scale in the meantime: one `build` of the scheme above wrote a **4.7 MB**
|
|
96
|
+
transcript to its log, and a `test` run of another app in the same workspace
|
|
97
|
+
wrote **432 KB**. Both are reported in well under 400 bytes.
|
|
98
|
+
|
|
99
|
+
Reproduce any of it yourself:
|
|
100
|
+
|
|
101
|
+
```sh
|
|
102
|
+
npm run benchmark -- --project ~/YourApp --scheme YourScheme # everything
|
|
103
|
+
npm run benchmark -- --project ~/YourApp --scheme YourScheme --quick # skip build and test
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Builds and tests get a separate derived-data directory per side, wiped before
|
|
107
|
+
each run, so neither side gets an incremental-build advantage over the other.
|
|
108
|
+
Every scenario is checkpointed as it finishes, so `--resume` picks up whatever
|
|
109
|
+
already ran.
|
|
110
|
+
|
|
111
|
+
## Install
|
|
112
|
+
|
|
113
|
+
```sh
|
|
114
|
+
npm install -g xcodebuild-axi
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Or run it without installing:
|
|
118
|
+
|
|
119
|
+
```sh
|
|
120
|
+
npx -y xcodebuild-axi
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Requires macOS with Xcode installed, and Node 20+.
|
|
124
|
+
|
|
125
|
+
## Commands
|
|
126
|
+
|
|
127
|
+
Running it with no arguments shows the project in front of you, not a manual:
|
|
128
|
+
|
|
129
|
+
```sh
|
|
130
|
+
$ xcodebuild-axi
|
|
131
|
+
bin: ~/.local/bin/xcodebuild-axi
|
|
132
|
+
description: Agent-ergonomic wrapper around xcodebuild.
|
|
133
|
+
workspace: MyApps
|
|
134
|
+
scheme_count: 12
|
|
135
|
+
schemes[12]: Analytics,Checkout,DesignSystem,Feed,MyApp,MyApp-Widget,...
|
|
136
|
+
last: Test - Checkout on iPhone 17 Pro · iOS Simulator 26.5 — 89 passed (4m ago)
|
|
137
|
+
help[2]:
|
|
138
|
+
Run `xcodebuild-axi build --scheme <name>` to build
|
|
139
|
+
Run `xcodebuild-axi test --scheme <name>` to run tests
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
| Command | What it does |
|
|
143
|
+
| -------------- | ----------------------------------------------------------------- |
|
|
144
|
+
| _(none)_ | Dashboard: what is here, what can be built, how the last run went |
|
|
145
|
+
| `build` | Build a scheme; report only errors, with `file,line,col` |
|
|
146
|
+
| `test` | Run tests; report counts and only the failures |
|
|
147
|
+
| `tests` | Enumerate the tests a scheme defines, without running them |
|
|
148
|
+
| `clean` | Clean a scheme's build products |
|
|
149
|
+
| `analyze` | Run the static analyzer; report only what it found |
|
|
150
|
+
| `archive` | Archive a scheme and report the archive's bundle id and version |
|
|
151
|
+
| `export` | Export an archive, writing the export options plist for you |
|
|
152
|
+
| `schemes` | List the schemes in the workspace or project |
|
|
153
|
+
| `destinations` | List the destinations a scheme can actually run on |
|
|
154
|
+
| `testplans` | List a scheme's test plans |
|
|
155
|
+
| `settings` | Read named build settings instead of dumping all 400 |
|
|
156
|
+
| `packages` | Read the pinned Swift package versions; resolve them on request |
|
|
157
|
+
| `info` | Xcode version, SDKs, and what this tool is pointed at |
|
|
158
|
+
| `result` | Re-read a previous run's `.xcresult` without rebuilding |
|
|
159
|
+
| `coverage` | Code coverage from a result bundle, per target or per file |
|
|
160
|
+
| `sim` | Boot, shut down, and inspect simulators |
|
|
161
|
+
| `platforms` | Installed runtimes, and the downloads that add more |
|
|
162
|
+
| `localize` | Export and import XLIFF localization catalogs |
|
|
163
|
+
| `xcframework` | Bundle built frameworks or libraries into an `.xcframework` |
|
|
164
|
+
| `find` | Resolve an executable or library to its toolchain path |
|
|
165
|
+
| `migrate` | Report the project file format, and convert it to a newer one |
|
|
166
|
+
| `setup` | Install session-start hooks for Claude Code, Codex, and OpenCode |
|
|
167
|
+
|
|
168
|
+
Every command takes `--help`.
|
|
169
|
+
|
|
170
|
+
## How much of xcodebuild
|
|
171
|
+
|
|
172
|
+
<!-- coverage:start -->
|
|
173
|
+
|
|
174
|
+
**Coverage: 100% — every one of the 117 options `xcodebuild -help` lists, and 9 of its 10 build actions.**
|
|
175
|
+
|
|
176
|
+
107 options map to an `xcodebuild-axi` flag. The other 10 are reachable without one:
|
|
177
|
+
|
|
178
|
+
| Option | How |
|
|
179
|
+
| ------------------------------- | ---------------------------------------------------------------------------------------------- |
|
|
180
|
+
| `-json` | every read-only query asks for JSON, then reports TOON |
|
|
181
|
+
| `-project` | set from the .xcodeproj found in the working directory |
|
|
182
|
+
| `-resultBundlePath` | every action writes a bundle to the tool's cache — that bundle is what the report is read from |
|
|
183
|
+
| `-skipMacroValidation` | macro trust is an interactive prompt in disguise, and an agent cannot answer it |
|
|
184
|
+
| `-test-enumeration-format` | always json, so `tests` can parse it |
|
|
185
|
+
| `-test-enumeration-output-path` | written to the tool's cache and read back, never printed |
|
|
186
|
+
| `-test-enumeration-style` | always flat; `tests` does its own grouping by target and suite |
|
|
187
|
+
| `-workspace` | set from the .xcworkspace found in the working directory |
|
|
188
|
+
| `-help` | `xcodebuild-axi --help`, which answers it in a fraction of the tokens |
|
|
189
|
+
| `-usage` | `xcodebuild-axi <command> --help`, per command rather than all 117 at once |
|
|
190
|
+
|
|
191
|
+
The one action left out is `installsrc` — it copies sources into `SRCROOT` as root, which is a packaging step rather than anything an agent loop needs.
|
|
192
|
+
|
|
193
|
+
The denominator is read from `xcodebuild -help` rather than hand-maintained, and this table is written against **Xcode 27.0** — the option list moves between releases. `npm run coverage:check` fails on that Xcode if an option here is unclassified or has been dropped, and reports the difference without failing on any other.
|
|
194
|
+
|
|
195
|
+
<!-- coverage:end -->
|
|
196
|
+
|
|
197
|
+
### Destinations you do not have to spell
|
|
198
|
+
|
|
199
|
+
The `-destination` specifier is the thing agents most reliably get wrong
|
|
200
|
+
against raw `xcodebuild`, and a miss costs a whole failed invocation. Pass a
|
|
201
|
+
name, or pass nothing:
|
|
202
|
+
|
|
203
|
+
```sh
|
|
204
|
+
xcodebuild-axi test --scheme MyApp --device "iPhone 17 Pro" # matched for you
|
|
205
|
+
xcodebuild-axi test --scheme MyApp # newest simulator
|
|
206
|
+
xcodebuild-axi test --scheme MyApp --destination "platform=iOS Simulator,id=…"
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Names are resolved to a simulator **udid** before the run, because two runtimes
|
|
210
|
+
routinely publish the same device name and a name-based specifier silently
|
|
211
|
+
picks whichever xcodebuild sees first. The reported destination is the one the
|
|
212
|
+
run actually landed on, read back out of the result bundle.
|
|
213
|
+
|
|
214
|
+
### Reading a run again
|
|
215
|
+
|
|
216
|
+
`build` and `test` both print the bundle they wrote, so nothing needs re-running
|
|
217
|
+
to be re-read:
|
|
218
|
+
|
|
219
|
+
```sh
|
|
220
|
+
xcodebuild-axi result ~/Library/Caches/xcodebuild-axi/MyApps-1a2b3c4d/MyApp-test.xcresult --failures --full
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## Ambient context
|
|
224
|
+
|
|
225
|
+
Two ways to get this in front of an agent before it reaches for raw
|
|
226
|
+
`xcodebuild`. You only need one.
|
|
227
|
+
|
|
228
|
+
**Session hooks** — the project's schemes and last run become context at the
|
|
229
|
+
start of every session:
|
|
230
|
+
|
|
231
|
+
```sh
|
|
232
|
+
xcodebuild-axi setup hooks # your home directory
|
|
233
|
+
xcodebuild-axi setup hooks --project # just this repository
|
|
234
|
+
xcodebuild-axi setup hooks --status # report, writing nothing
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Covers Claude Code, Codex, and OpenCode. Installs are idempotent and repair a
|
|
238
|
+
stale path.
|
|
239
|
+
|
|
240
|
+
**A skill** — loads on demand instead of on every session, and works in any
|
|
241
|
+
agent that reads the skill format:
|
|
242
|
+
|
|
243
|
+
```sh
|
|
244
|
+
npx skills add alexrrouse/xcodebuild-axi --skill xcodebuild-axi
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Conventions
|
|
248
|
+
|
|
249
|
+
- **Output is [TOON](https://toonformat.dev/)** on stdout, ~40% cheaper than the
|
|
250
|
+
equivalent JSON.
|
|
251
|
+
- **Errors are data.** They go to stdout in the same shape as an answer, with a
|
|
252
|
+
`code` and a `help[]` that names the command that fixes it.
|
|
253
|
+
- **Exit codes**: `0` success, `1` the build or tests failed, `2` usage error.
|
|
254
|
+
A failed build still prints its full report — the exit code is for your `&&`,
|
|
255
|
+
the report is for the agent.
|
|
256
|
+
- **Unknown flags fail loudly**, by name, with the valid set listed inline. A
|
|
257
|
+
silently dropped filter is worse than an error.
|
|
258
|
+
- **Nothing is written to your repository.** Logs and result bundles live under
|
|
259
|
+
`~/Library/Caches/xcodebuild-axi/`, keyed by project path.
|
|
260
|
+
- **No interactive prompts, ever.** Code signing is off by default so simulator
|
|
261
|
+
builds need no team; pass `--sign` when you mean it.
|
|
262
|
+
|
|
263
|
+
## Environment
|
|
264
|
+
|
|
265
|
+
| Variable | Effect |
|
|
266
|
+
| ---------------- | ------------------------------------------------ |
|
|
267
|
+
| `XCODEBUILD_BIN` | Override the wrapped `xcodebuild` binary |
|
|
268
|
+
| `DEVELOPER_DIR` | Select an Xcode, as `xcodebuild` itself reads it |
|
|
269
|
+
|
|
270
|
+
## Development
|
|
271
|
+
|
|
272
|
+
```sh
|
|
273
|
+
npm install
|
|
274
|
+
npm run dev -- destinations --scheme MyApp # run from source
|
|
275
|
+
npm test
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Everything CI checks, in order:
|
|
279
|
+
|
|
280
|
+
```sh
|
|
281
|
+
npm run format:check && npm run lint && npx tsc --noEmit && npm test
|
|
282
|
+
npm run build && npm run build:skill -- --check && npm run coverage:check
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Three committed files are generated and fail CI when stale: the skill
|
|
286
|
+
(`npm run build:skill`, from the CLI's own help text), the coverage table
|
|
287
|
+
(`npm run coverage`, from `src/surface.ts`), and the benchmark table
|
|
288
|
+
(`npm run benchmark -- --write`). Regenerate rather than editing them.
|
|
289
|
+
|
|
290
|
+
Adding a flag means three edits — the command, `src/surface.ts`, and
|
|
291
|
+
`npm run coverage` — and the `flags[N]:` count in a help block is asserted by
|
|
292
|
+
`test/help.test.ts`, so a forgotten count fails the suite.
|
|
293
|
+
|
|
294
|
+
Examples in help text, tests, and this README use a fictional project
|
|
295
|
+
vocabulary (`MyApp`, `MyApps`, `MyApps-1a2b3c4d`); see `AGENTS.md`.
|
|
296
|
+
|
|
297
|
+
## Built on
|
|
298
|
+
|
|
299
|
+
[AXI](https://axi.md/) — the design standard for agent-facing CLIs — via
|
|
300
|
+
[`axi-sdk-js`](https://www.npmjs.com/package/axi-sdk-js).
|
|
301
|
+
|
|
302
|
+
## License
|
|
303
|
+
|
|
304
|
+
MIT
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { tryFastPath } from "axi-sdk-js/fast-path";
|
|
3
|
+
import { VERSION } from "../src/version.js";
|
|
4
|
+
if (!tryFastPath(process.argv.slice(2), { version: VERSION })) {
|
|
5
|
+
const { main } = await import("../src/cli.js");
|
|
6
|
+
await main();
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=xcodebuild-axi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xcodebuild-axi.js","sourceRoot":"","sources":["../../bin/xcodebuild-axi.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAE5C,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IAC9D,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,CAAC;IAC/C,MAAM,IAAI,EAAE,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { type ProjectContext } from "./context.js";
|
|
2
|
+
import { type BuildRun } from "./xcodebuild.js";
|
|
3
|
+
/**
|
|
4
|
+
* The machinery shared by every command that runs an xcodebuild *action* —
|
|
5
|
+
* build, analyze, archive, clean, and the test family. They differ in which
|
|
6
|
+
* action word they pass and what they say afterwards; everything before that
|
|
7
|
+
* (finding the project, resolving the scheme and destination, assembling
|
|
8
|
+
* flags, streaming to a log, reading the bundle back) is identical, and was
|
|
9
|
+
* worth extracting the first time a second command needed it.
|
|
10
|
+
*/
|
|
11
|
+
/** Flags every build-family command accepts. Keep in sync with `BUILD_FLAG_HELP`. */
|
|
12
|
+
export declare const SHARED_BUILD_FLAGS: readonly ["--scheme", "--target", "--all-targets", "--device", "--destination", "--configuration", "--sdk", "--arch", "--toolchain", "--xcconfig", "--derived-data", "--jobs", "--setting", "--sign", "--allow-provisioning", "--sanitizer", "--timing", "--destination-timeout", "--parallelize-targets", "--hide-script-env", "--skip-unavailable-actions", "--max-errors", "--full", "--codesize", "--log-level", "--bundle-version", "--stream", "--offline", "--cache", "--package-cache", "--no-auto-resolve", "--skip-package-updates", "--no-package-cache", "--skip-plugin-validation", "--skip-signature-validation", "--package-auth", "--registry-url", "--scm-to-registry", "--fingerprint-policy", "--signing-entity-policy", "--scm-provider"];
|
|
13
|
+
export declare const SHARED_BUILD_VALUE_FLAGS: readonly ["--scheme", "--target", "--device", "--destination", "--configuration", "--sdk", "--arch", "--toolchain", "--xcconfig", "--derived-data", "--jobs", "--setting", "--sanitizer", "--destination-timeout", "--max-errors", "--codesize", "--log-level", "--bundle-version", "--stream", "--cache", "--package-cache", "--package-auth", "--registry-url", "--scm-to-registry", "--fingerprint-policy", "--signing-entity-policy", "--scm-provider"];
|
|
14
|
+
/** The shared flag block, so each command's `--help` stays consistent. */
|
|
15
|
+
export declare const BUILD_FLAG_HELP = " --scheme <name> scheme to act on (required only when the project has more than one)\n --target <name> build a target instead of a scheme; repeatable (project only)\n --all-targets build every target in the project (project only)\n --device <name> simulator or device name, e.g. \"iPhone 17 Pro\" (default: newest simulator)\n --destination <spec> raw xcodebuild destination specifier, passed through untouched\n --configuration <name> build configuration (default: the scheme's own)\n --sdk <name> base SDK, e.g. iphonesimulator\n --arch <arch> architecture to build; repeatable or comma-separated\n --toolchain <name> toolchain identifier or name\n --xcconfig <path> apply build settings from this file as overrides\n --derived-data <path> derived data directory\n --jobs <n> maximum concurrent build operations\n --setting KEY=VALUE build setting override; repeatable or comma-separated\n --sign allow code signing (off by default, so simulator builds need no team)\n --allow-provisioning let xcodebuild talk to the developer portal for profiles\n --sanitizer <name> address, thread, or undefined; repeatable or comma-separated\n --timing report per-command build timings\n --destination-timeout <secs> how long to wait for the destination device\n --parallelize-targets build independent targets in parallel\n --hide-script-env omit shell script environment variables from the log\n --skip-unavailable-actions skip scheme actions that cannot run instead of failing\n --max-errors <n> errors to list before summarizing the rest (default: 20)\n --full list every warning instead of the first 10\n --codesize <dir> write a code size profile to this directory\n --log-level <level> quiet, normal, or verbose \u2014 how much lands in the log file\n --bundle-version <n> result bundle format version (default: xcodebuild's own)\n --stream <path> also write xcodebuild's live result stream here\n --offline resolve using only the versions already in Package.resolved\n --cache <path> clone remote packages into this directory\n --package-cache <path> shared package cache directory (across projects)\n --no-auto-resolve never resolve automatically; fail if Package.resolved is stale\n --skip-package-updates use the packages already cloned, without checking for updates\n --no-package-cache ignore the shared package repository cache\n --skip-plugin-validation trust build plugins without prompting\n --skip-signature-validation do not verify package signatures\n --package-auth <kind> keychain or netrc\n --registry-url <url> default package registry\n --scm-to-registry <kind> none, identity, or swift-package-registry\n --fingerprint-policy <kind> strict or warn\n --signing-entity-policy <kind> strict or warn\n --scm-provider <kind> system or xcode";
|
|
16
|
+
/**
|
|
17
|
+
* The flags that map straight onto an xcodebuild switch with no interpretation
|
|
18
|
+
* beyond validation. Grouped so they can be tested without a project.
|
|
19
|
+
*/
|
|
20
|
+
export declare function buildPassthroughArgs(args: string[], command: string): string[];
|
|
21
|
+
export interface BuildContext {
|
|
22
|
+
project: ProjectContext;
|
|
23
|
+
/** The scheme, or a description of the targets when in target mode. */
|
|
24
|
+
scheme: string;
|
|
25
|
+
/** Human-readable destination, or undefined when the command skipped one. */
|
|
26
|
+
destination: string | undefined;
|
|
27
|
+
/** Everything before the action word. */
|
|
28
|
+
xcodebuildArgs: string[];
|
|
29
|
+
maxErrors: number;
|
|
30
|
+
full: boolean;
|
|
31
|
+
}
|
|
32
|
+
export interface ResolveBuildContextOptions {
|
|
33
|
+
args: string[];
|
|
34
|
+
command: string;
|
|
35
|
+
/** Skip destination resolution — `clean` does not need one, and it costs a subprocess. */
|
|
36
|
+
needsDestination?: boolean;
|
|
37
|
+
}
|
|
38
|
+
export declare function resolveBuildContext(options: ResolveBuildContextOptions): Promise<BuildContext>;
|
|
39
|
+
/** A filesystem stem that distinguishes runs of different commands and devices. */
|
|
40
|
+
export declare function runLabel(context: BuildContext, command: string): string;
|
|
41
|
+
export interface RunActionOptions {
|
|
42
|
+
context: BuildContext;
|
|
43
|
+
command: string;
|
|
44
|
+
/** xcodebuild action words, in order, e.g. `["clean", "build"]`. */
|
|
45
|
+
actions: string[];
|
|
46
|
+
/** Extra arguments appended after the shared ones. */
|
|
47
|
+
extraArgs?: string[];
|
|
48
|
+
}
|
|
49
|
+
export declare function runAction(options: RunActionOptions): Promise<BuildRun>;
|
|
50
|
+
export interface ReportActionOptions {
|
|
51
|
+
context: BuildContext;
|
|
52
|
+
run: BuildRun;
|
|
53
|
+
/** Field name and success wording, e.g. `{ key: "build", ok: "succeeded" }`. */
|
|
54
|
+
key: string;
|
|
55
|
+
ok: string;
|
|
56
|
+
command: string;
|
|
57
|
+
/** Extra fields rendered after the destination, e.g. an archive path. */
|
|
58
|
+
extra?: Record<string, unknown>;
|
|
59
|
+
/** Report analyzer warnings as the payload rather than as a count. */
|
|
60
|
+
analyzer?: boolean;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The shared report for any action that compiles something.
|
|
64
|
+
*
|
|
65
|
+
* Errors are the payload on failure, warnings on success, and the full
|
|
66
|
+
* transcript is always a path away.
|
|
67
|
+
*/
|
|
68
|
+
export declare function reportAction(options: ReportActionOptions): Promise<string>;
|