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.
Files changed (131) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/LICENSE +21 -0
  3. package/README.md +304 -0
  4. package/dist/bin/xcodebuild-axi.d.ts +2 -0
  5. package/dist/bin/xcodebuild-axi.js +8 -0
  6. package/dist/bin/xcodebuild-axi.js.map +1 -0
  7. package/dist/src/action.d.ts +68 -0
  8. package/dist/src/action.js +373 -0
  9. package/dist/src/action.js.map +1 -0
  10. package/dist/src/archive.d.ts +33 -0
  11. package/dist/src/archive.js +77 -0
  12. package/dist/src/archive.js.map +1 -0
  13. package/dist/src/args.d.ts +23 -0
  14. package/dist/src/args.js +123 -0
  15. package/dist/src/args.js.map +1 -0
  16. package/dist/src/auth.d.ts +11 -0
  17. package/dist/src/auth.js +51 -0
  18. package/dist/src/auth.js.map +1 -0
  19. package/dist/src/cli.d.ts +20 -0
  20. package/dist/src/cli.js +132 -0
  21. package/dist/src/cli.js.map +1 -0
  22. package/dist/src/commands/analyze.d.ts +2 -0
  23. package/dist/src/commands/analyze.js +32 -0
  24. package/dist/src/commands/analyze.js.map +1 -0
  25. package/dist/src/commands/archive.d.ts +2 -0
  26. package/dist/src/commands/archive.js +63 -0
  27. package/dist/src/commands/archive.js.map +1 -0
  28. package/dist/src/commands/build.d.ts +2 -0
  29. package/dist/src/commands/build.js +60 -0
  30. package/dist/src/commands/build.js.map +1 -0
  31. package/dist/src/commands/clean.d.ts +2 -0
  32. package/dist/src/commands/clean.js +39 -0
  33. package/dist/src/commands/clean.js.map +1 -0
  34. package/dist/src/commands/coverage.d.ts +2 -0
  35. package/dist/src/commands/coverage.js +111 -0
  36. package/dist/src/commands/coverage.js.map +1 -0
  37. package/dist/src/commands/destinations.d.ts +2 -0
  38. package/dist/src/commands/destinations.js +56 -0
  39. package/dist/src/commands/destinations.js.map +1 -0
  40. package/dist/src/commands/export.d.ts +2 -0
  41. package/dist/src/commands/export.js +170 -0
  42. package/dist/src/commands/export.js.map +1 -0
  43. package/dist/src/commands/find.d.ts +2 -0
  44. package/dist/src/commands/find.js +70 -0
  45. package/dist/src/commands/find.js.map +1 -0
  46. package/dist/src/commands/home.d.ts +9 -0
  47. package/dist/src/commands/home.js +97 -0
  48. package/dist/src/commands/home.js.map +1 -0
  49. package/dist/src/commands/info.d.ts +2 -0
  50. package/dist/src/commands/info.js +95 -0
  51. package/dist/src/commands/info.js.map +1 -0
  52. package/dist/src/commands/localize.d.ts +2 -0
  53. package/dist/src/commands/localize.js +121 -0
  54. package/dist/src/commands/localize.js.map +1 -0
  55. package/dist/src/commands/migrate.d.ts +8 -0
  56. package/dist/src/commands/migrate.js +177 -0
  57. package/dist/src/commands/migrate.js.map +1 -0
  58. package/dist/src/commands/packages.d.ts +2 -0
  59. package/dist/src/commands/packages.js +108 -0
  60. package/dist/src/commands/packages.js.map +1 -0
  61. package/dist/src/commands/platforms.d.ts +2 -0
  62. package/dist/src/commands/platforms.js +379 -0
  63. package/dist/src/commands/platforms.js.map +1 -0
  64. package/dist/src/commands/result.d.ts +2 -0
  65. package/dist/src/commands/result.js +100 -0
  66. package/dist/src/commands/result.js.map +1 -0
  67. package/dist/src/commands/schemes.d.ts +2 -0
  68. package/dist/src/commands/schemes.js +39 -0
  69. package/dist/src/commands/schemes.js.map +1 -0
  70. package/dist/src/commands/settings.d.ts +2 -0
  71. package/dist/src/commands/settings.js +191 -0
  72. package/dist/src/commands/settings.js.map +1 -0
  73. package/dist/src/commands/setup.d.ts +2 -0
  74. package/dist/src/commands/setup.js +84 -0
  75. package/dist/src/commands/setup.js.map +1 -0
  76. package/dist/src/commands/sim.d.ts +2 -0
  77. package/dist/src/commands/sim.js +187 -0
  78. package/dist/src/commands/sim.js.map +1 -0
  79. package/dist/src/commands/test.d.ts +2 -0
  80. package/dist/src/commands/test.js +317 -0
  81. package/dist/src/commands/test.js.map +1 -0
  82. package/dist/src/commands/testplans.d.ts +8 -0
  83. package/dist/src/commands/testplans.js +75 -0
  84. package/dist/src/commands/testplans.js.map +1 -0
  85. package/dist/src/commands/tests.d.ts +13 -0
  86. package/dist/src/commands/tests.js +154 -0
  87. package/dist/src/commands/tests.js.map +1 -0
  88. package/dist/src/commands/xcframework.d.ts +2 -0
  89. package/dist/src/commands/xcframework.js +77 -0
  90. package/dist/src/commands/xcframework.js.map +1 -0
  91. package/dist/src/context.d.ts +23 -0
  92. package/dist/src/context.js +72 -0
  93. package/dist/src/context.js.map +1 -0
  94. package/dist/src/destination.d.ts +50 -0
  95. package/dist/src/destination.js +174 -0
  96. package/dist/src/destination.js.map +1 -0
  97. package/dist/src/errors.d.ts +6 -0
  98. package/dist/src/errors.js +55 -0
  99. package/dist/src/errors.js.map +1 -0
  100. package/dist/src/packages.d.ts +31 -0
  101. package/dist/src/packages.js +117 -0
  102. package/dist/src/packages.js.map +1 -0
  103. package/dist/src/report.d.ts +20 -0
  104. package/dist/src/report.js +39 -0
  105. package/dist/src/report.js.map +1 -0
  106. package/dist/src/scheme.d.ts +22 -0
  107. package/dist/src/scheme.js +66 -0
  108. package/dist/src/scheme.js.map +1 -0
  109. package/dist/src/simctl.d.ts +33 -0
  110. package/dist/src/simctl.js +85 -0
  111. package/dist/src/simctl.js.map +1 -0
  112. package/dist/src/surface.d.ts +55 -0
  113. package/dist/src/surface.js +350 -0
  114. package/dist/src/surface.js.map +1 -0
  115. package/dist/src/toon.d.ts +34 -0
  116. package/dist/src/toon.js +79 -0
  117. package/dist/src/toon.js.map +1 -0
  118. package/dist/src/version.d.ts +1 -0
  119. package/dist/src/version.js +24 -0
  120. package/dist/src/version.js.map +1 -0
  121. package/dist/src/xccov.d.ts +25 -0
  122. package/dist/src/xccov.js +78 -0
  123. package/dist/src/xccov.js.map +1 -0
  124. package/dist/src/xcodebuild.d.ts +65 -0
  125. package/dist/src/xcodebuild.js +163 -0
  126. package/dist/src/xcodebuild.js.map +1 -0
  127. package/dist/src/xcresult.d.ts +96 -0
  128. package/dist/src/xcresult.js +156 -0
  129. package/dist/src/xcresult.js.map +1 -0
  130. package/package.json +75 -0
  131. 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,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -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>;