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
@@ -0,0 +1,65 @@
1
+ import type { ProjectContext } from "./context.js";
2
+ /** Override the wrapped binary. Unset keeps the PATH lookup. */
3
+ export declare function xcodebuildBin(): string;
4
+ export interface MetadataResult {
5
+ stdout: string;
6
+ stderr: string;
7
+ exitCode: number;
8
+ }
9
+ /**
10
+ * Run a short, read-only xcodebuild invocation and buffer its output.
11
+ *
12
+ * Only for the `-list`/`-showdestinations`/`-showBuildSettings` family, whose
13
+ * output is measured in kilobytes. Builds and tests go through `runBuild`,
14
+ * which never holds a transcript in memory.
15
+ */
16
+ export declare function runMetadata(args: string[]): Promise<MetadataResult>;
17
+ export interface BuildRun {
18
+ exitCode: number;
19
+ logPath: string;
20
+ resultPath: string;
21
+ /** Wall-clock seconds the invocation took. */
22
+ seconds: number;
23
+ /** The tail of the transcript, for failures no result bundle explains. */
24
+ tail: string;
25
+ }
26
+ export interface BuildRunOptions {
27
+ args: string[];
28
+ /** Filename stem for the log and result bundle, e.g. "MyApp-iPhone-17-Pro". */
29
+ label: string;
30
+ /** Omit for invocations that act on the toolchain rather than a project. */
31
+ project?: ProjectContext;
32
+ /** Overrides the cache location for both artifacts. */
33
+ outDir?: string;
34
+ }
35
+ /**
36
+ * Run a build or test, streaming the transcript straight to disk.
37
+ *
38
+ * The transcript is the thing this tool exists to keep out of the agent's
39
+ * context, so it is never buffered: a single passing test run of one app in a
40
+ * real repo measured 549 KB, and a full verify run 2.5 MB. Only the last few
41
+ * KB are kept in memory, for the failures that produce no result bundle.
42
+ */
43
+ export declare function runBuild(options: BuildRunOptions): Promise<BuildRun>;
44
+ /**
45
+ * Where toolchain-level runs land — platform downloads and the like, which
46
+ * belong to the machine rather than to any one project.
47
+ */
48
+ export declare function globalArtifactDir(): string;
49
+ /**
50
+ * Where logs and result bundles land.
51
+ *
52
+ * Under the user's cache directory rather than the repo, so running this tool
53
+ * never dirties a working tree or a `.gitignore`. The project path is hashed
54
+ * into the name so two checkouts of the same repo do not collide.
55
+ */
56
+ export declare function artifactDir(project: ProjectContext): string;
57
+ /**
58
+ * Strip xcodebuild's fixed preamble.
59
+ *
60
+ * Every invocation — including the read-only ones — reprints the command line,
61
+ * "Resolve Package Graph", and the full resolved package list. In a workspace
62
+ * with 16 local packages that is ~1.2 KB of identical text in front of a 349-byte
63
+ * answer, paid on every call.
64
+ */
65
+ export declare function stripPreamble(output: string): string;
@@ -0,0 +1,163 @@
1
+ import { execFile, spawn } from "node:child_process";
2
+ import { createHash } from "node:crypto";
3
+ import { createWriteStream, mkdirSync, rmSync } from "node:fs";
4
+ import { homedir } from "node:os";
5
+ import { join } from "node:path";
6
+ import { AxiError, xcodeNotInstalledError } from "./errors.js";
7
+ const MAX_METADATA_BYTES = 8 * 1024 * 1024;
8
+ /** Override the wrapped binary. Unset keeps the PATH lookup. */
9
+ export function xcodebuildBin() {
10
+ const fromEnv = process.env["XCODEBUILD_BIN"]?.trim();
11
+ return fromEnv && fromEnv.length > 0 ? fromEnv : "xcodebuild";
12
+ }
13
+ /**
14
+ * Run a short, read-only xcodebuild invocation and buffer its output.
15
+ *
16
+ * Only for the `-list`/`-showdestinations`/`-showBuildSettings` family, whose
17
+ * output is measured in kilobytes. Builds and tests go through `runBuild`,
18
+ * which never holds a transcript in memory.
19
+ */
20
+ export function runMetadata(args) {
21
+ return new Promise((resolvePromise, rejectPromise) => {
22
+ execFile(xcodebuildBin(), args, { maxBuffer: MAX_METADATA_BYTES, encoding: "utf-8" }, (error, stdout, stderr) => {
23
+ if (error && error.code === "ENOENT") {
24
+ rejectPromise(xcodeNotInstalledError());
25
+ return;
26
+ }
27
+ if (error &&
28
+ error.code ===
29
+ "ERR_CHILD_PROCESS_STDIO_MAXBUFFER") {
30
+ rejectPromise(new AxiError("xcodebuild produced more output than this command buffers", "UNKNOWN", [
31
+ "Use `xcodebuild-axi build` or `test`, which stream to a log instead",
32
+ ]));
33
+ return;
34
+ }
35
+ resolvePromise({
36
+ stdout,
37
+ stderr,
38
+ exitCode: typeof error?.code === "number" ? error.code : error ? 1 : 0,
39
+ });
40
+ });
41
+ });
42
+ }
43
+ /**
44
+ * Run a build or test, streaming the transcript straight to disk.
45
+ *
46
+ * The transcript is the thing this tool exists to keep out of the agent's
47
+ * context, so it is never buffered: a single passing test run of one app in a
48
+ * real repo measured 549 KB, and a full verify run 2.5 MB. Only the last few
49
+ * KB are kept in memory, for the failures that produce no result bundle.
50
+ */
51
+ export function runBuild(options) {
52
+ const dir = options.outDir ??
53
+ (options.project ? artifactDir(options.project) : globalArtifactDir());
54
+ mkdirSync(dir, { recursive: true });
55
+ const logPath = join(dir, `${options.label}.log`);
56
+ const resultPath = join(dir, `${options.label}.xcresult`);
57
+ // xcodebuild refuses to write over an existing result bundle and dies before
58
+ // running anything — "error: Existing file at -resultBundlePath". The bundle
59
+ // is our artifact, not the user's, so clear it rather than make every caller
60
+ // remember to.
61
+ rmSync(resultPath, { recursive: true, force: true });
62
+ const args = [...options.args, "-resultBundlePath", resultPath];
63
+ const started = Date.now();
64
+ return new Promise((resolvePromise, rejectPromise) => {
65
+ const log = createWriteStream(logPath);
66
+ const child = spawn(xcodebuildBin(), args, {
67
+ cwd: process.cwd(),
68
+ // NSUnbufferedIO makes xcodebuild flush per line, so a wedged run leaves
69
+ // a usable log instead of an empty one.
70
+ env: { ...process.env, NSUnbufferedIO: "YES" },
71
+ stdio: ["ignore", "pipe", "pipe"],
72
+ });
73
+ const tail = [];
74
+ const keepTail = (chunk) => {
75
+ tail.push(chunk.toString("utf-8"));
76
+ while (tail.length > 40)
77
+ tail.shift();
78
+ };
79
+ child.stdout.pipe(log, { end: false });
80
+ child.stderr.pipe(log, { end: false });
81
+ child.stdout.on("data", keepTail);
82
+ child.stderr.on("data", keepTail);
83
+ child.on("error", (error) => {
84
+ log.end();
85
+ rejectPromise(error.code === "ENOENT"
86
+ ? xcodeNotInstalledError()
87
+ : error);
88
+ });
89
+ child.on("close", (code) => {
90
+ log.end(() => resolvePromise({
91
+ exitCode: code ?? 1,
92
+ logPath,
93
+ resultPath,
94
+ seconds: (Date.now() - started) / 1000,
95
+ tail: tail.join("").slice(-8000),
96
+ }));
97
+ });
98
+ });
99
+ }
100
+ /**
101
+ * Where toolchain-level runs land — platform downloads and the like, which
102
+ * belong to the machine rather than to any one project.
103
+ */
104
+ export function globalArtifactDir() {
105
+ return join(homedir(), "Library", "Caches", "xcodebuild-axi", "toolchain");
106
+ }
107
+ /**
108
+ * Where logs and result bundles land.
109
+ *
110
+ * Under the user's cache directory rather than the repo, so running this tool
111
+ * never dirties a working tree or a `.gitignore`. The project path is hashed
112
+ * into the name so two checkouts of the same repo do not collide.
113
+ */
114
+ export function artifactDir(project) {
115
+ const hash = createHash("sha256")
116
+ .update(project.path)
117
+ .digest("hex")
118
+ .slice(0, 8);
119
+ return join(homedir(), "Library", "Caches", "xcodebuild-axi", `${project.name}-${hash}`);
120
+ }
121
+ /**
122
+ * Strip xcodebuild's fixed preamble.
123
+ *
124
+ * Every invocation — including the read-only ones — reprints the command line,
125
+ * "Resolve Package Graph", and the full resolved package list. In a workspace
126
+ * with 16 local packages that is ~1.2 KB of identical text in front of a 349-byte
127
+ * answer, paid on every call.
128
+ */
129
+ export function stripPreamble(output) {
130
+ const lines = output.split("\n");
131
+ const kept = [];
132
+ let skippingPackageList = false;
133
+ for (const line of lines) {
134
+ if (skippingPackageList) {
135
+ // The list is indented; the first unindented line ends it.
136
+ if (line.trim().length === 0 || /^\s/.test(line))
137
+ continue;
138
+ skippingPackageList = false;
139
+ }
140
+ if (/^Resolved source packages:/.test(line)) {
141
+ skippingPackageList = true;
142
+ continue;
143
+ }
144
+ if (NOISE.some((pattern) => pattern.test(line)))
145
+ continue;
146
+ kept.push(line);
147
+ }
148
+ return kept
149
+ .join("\n")
150
+ .replace(/\n{3,}/g, "\n\n")
151
+ .trim();
152
+ }
153
+ const NOISE = [
154
+ /^Command line invocation:/,
155
+ /^\s+\/.*\/xcodebuild /,
156
+ /^Resolve Package Graph$/,
157
+ /^Prepare packages$/,
158
+ /^User defaults from command line:/,
159
+ /^Build settings from command line:/,
160
+ /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ xcodebuild\[\d+:\d+\]/,
161
+ /^note: /,
162
+ ];
163
+ //# sourceMappingURL=xcodebuild.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xcodebuild.js","sourceRoot":"","sources":["../../src/xcodebuild.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,96 @@
1
+ export interface TestDevice {
2
+ deviceName?: string;
3
+ modelName?: string;
4
+ platform?: string;
5
+ osVersion?: string;
6
+ architecture?: string;
7
+ deviceId?: string;
8
+ }
9
+ export interface TestFailure {
10
+ testName?: string;
11
+ targetName?: string;
12
+ failureText?: string;
13
+ testIdentifierString?: string;
14
+ }
15
+ export interface TestSummary {
16
+ title?: string;
17
+ result?: string;
18
+ totalTestCount?: number;
19
+ passedTests?: number;
20
+ failedTests?: number;
21
+ skippedTests?: number;
22
+ expectedFailures?: number;
23
+ startTime?: number;
24
+ finishTime?: number;
25
+ environmentDescription?: string;
26
+ testFailures?: TestFailure[];
27
+ runtimeWarnings?: RawIssue[];
28
+ devicesAndConfigurations?: {
29
+ device?: TestDevice;
30
+ passedTests?: number;
31
+ failedTests?: number;
32
+ skippedTests?: number;
33
+ }[];
34
+ }
35
+ export interface RawIssue {
36
+ issueType?: string;
37
+ message?: string;
38
+ targetName?: string;
39
+ sourceURL?: string;
40
+ }
41
+ export interface BuildResults {
42
+ actionTitle?: string;
43
+ status?: string;
44
+ errorCount?: number;
45
+ warningCount?: number;
46
+ analyzerWarningCount?: number;
47
+ errors?: RawIssue[];
48
+ warnings?: RawIssue[];
49
+ analyzerWarnings?: RawIssue[];
50
+ destination?: TestDevice;
51
+ startTime?: number;
52
+ endTime?: number;
53
+ }
54
+ /** A diagnostic, flattened to the shape a TOON row wants. */
55
+ export interface Diagnostic {
56
+ file: string;
57
+ line: number | "";
58
+ col: number | "";
59
+ type: string;
60
+ message: string;
61
+ }
62
+ export declare function readTestSummary(path: string): Promise<TestSummary>;
63
+ export declare function readBuildResults(path: string): Promise<BuildResults>;
64
+ /**
65
+ * Pull `file`, `line` and `col` out of a diagnostic's sourceURL.
66
+ *
67
+ * The URL looks like
68
+ * file:///path/To.swift#EndingColumnNumber=83&StartingLineNumber=165&...
69
+ * and the line and column in that fragment are **zero-based** — a diagnostic
70
+ * reported at `StartingLineNumber=165` is on line 166 as any editor counts it.
71
+ * Getting this wrong sends the agent to the wrong line, which is worse than
72
+ * sending it nowhere, so the offset is applied here and nowhere else.
73
+ */
74
+ export declare function parseSourceURL(sourceURL: string | undefined): {
75
+ file: string;
76
+ line: number | "";
77
+ col: number | "";
78
+ };
79
+ /**
80
+ * Repo-relative when the file is under cwd, absolute otherwise.
81
+ *
82
+ * Both sides need the `/private` prefix stripped, not just the diagnostic's:
83
+ * on macOS `process.cwd()` in `/tmp` reports `/private/tmp`, so comparing the
84
+ * two raw would make every path look outside the tree and stay absolute.
85
+ */
86
+ export declare function relativize(file: string, cwd?: string): string;
87
+ /**
88
+ * Flatten issues to TOON rows, dropping exact duplicates.
89
+ *
90
+ * The same Swift diagnostic is reported once per target that compiles the
91
+ * file, so a warning in a shared package can appear a dozen times in one
92
+ * build. Only the first is information; the rest are paid-for repetition.
93
+ */
94
+ export declare function toDiagnostics(issues: RawIssue[] | undefined): Diagnostic[];
95
+ /** The device a run actually landed on, as one readable line. */
96
+ export declare function describeDevice(device: TestDevice | undefined): string;
@@ -0,0 +1,156 @@
1
+ import { execFile } from "node:child_process";
2
+ import { existsSync } from "node:fs";
3
+ import { relative } from "node:path";
4
+ import { AxiError } from "./errors.js";
5
+ /**
6
+ * Structured reads of an .xcresult bundle.
7
+ *
8
+ * This is the whole reason the tool can be small: xcodebuild's transcript is
9
+ * megabytes of noise, but the bundle it writes alongside carries the same
10
+ * answers as a few KB of JSON — pass/fail counts, per-device breakdown, and
11
+ * every diagnostic with a precise source location.
12
+ */
13
+ const MAX_BUFFER_BYTES = 64 * 1024 * 1024;
14
+ function xcresulttool(args, path) {
15
+ if (!existsSync(path)) {
16
+ throw new AxiError(`No result bundle at ${path}`, "RESULT_NOT_FOUND", [
17
+ "Run `xcodebuild-axi build` or `test` first — each prints the bundle path it wrote",
18
+ ]);
19
+ }
20
+ return new Promise((resolvePromise, rejectPromise) => {
21
+ execFile("xcrun", ["xcresulttool", ...args, "--path", path, "--compact"], { maxBuffer: MAX_BUFFER_BYTES, encoding: "utf-8" }, (error, stdout) => {
22
+ if (error && stdout.trim().length === 0) {
23
+ rejectPromise(new AxiError(`Could not read result bundle at ${path}`, "RESULT_NOT_FOUND", [
24
+ "The bundle may be from an incompatible Xcode version, or still being written",
25
+ ]));
26
+ return;
27
+ }
28
+ try {
29
+ resolvePromise(JSON.parse(stdout));
30
+ }
31
+ catch {
32
+ rejectPromise(new AxiError(`Result bundle at ${path} produced unreadable output`, "RESULT_NOT_FOUND"));
33
+ }
34
+ });
35
+ });
36
+ }
37
+ export function readTestSummary(path) {
38
+ return xcresulttool(["get", "test-results", "summary"], path);
39
+ }
40
+ export function readBuildResults(path) {
41
+ return xcresulttool(["get", "build-results"], path);
42
+ }
43
+ /**
44
+ * Pull `file`, `line` and `col` out of a diagnostic's sourceURL.
45
+ *
46
+ * The URL looks like
47
+ * file:///path/To.swift#EndingColumnNumber=83&StartingLineNumber=165&...
48
+ * and the line and column in that fragment are **zero-based** — a diagnostic
49
+ * reported at `StartingLineNumber=165` is on line 166 as any editor counts it.
50
+ * Getting this wrong sends the agent to the wrong line, which is worse than
51
+ * sending it nowhere, so the offset is applied here and nowhere else.
52
+ */
53
+ export function parseSourceURL(sourceURL) {
54
+ if (!sourceURL)
55
+ return { file: "", line: "", col: "" };
56
+ const hashIndex = sourceURL.indexOf("#");
57
+ const rawPath = hashIndex === -1 ? sourceURL : sourceURL.slice(0, hashIndex);
58
+ const fragment = hashIndex === -1 ? "" : sourceURL.slice(hashIndex + 1);
59
+ let file = rawPath.startsWith("file://")
60
+ ? rawPath.slice("file://".length)
61
+ : rawPath;
62
+ try {
63
+ file = decodeURIComponent(file);
64
+ }
65
+ catch {
66
+ // A malformed escape is not worth failing a whole build report over.
67
+ }
68
+ const params = new URLSearchParams(fragment);
69
+ const line = toZeroBasedNumber(params.get("StartingLineNumber"));
70
+ const col = toZeroBasedNumber(params.get("StartingColumnNumber"));
71
+ return { file: relativize(file), line, col };
72
+ }
73
+ function toZeroBasedNumber(raw) {
74
+ if (raw === null)
75
+ return "";
76
+ const parsed = Number(raw);
77
+ return Number.isFinite(parsed) ? parsed + 1 : "";
78
+ }
79
+ /**
80
+ * Repo-relative when the file is under cwd, absolute otherwise.
81
+ *
82
+ * Both sides need the `/private` prefix stripped, not just the diagnostic's:
83
+ * on macOS `process.cwd()` in `/tmp` reports `/private/tmp`, so comparing the
84
+ * two raw would make every path look outside the tree and stay absolute.
85
+ */
86
+ export function relativize(file, cwd = process.cwd()) {
87
+ if (!file.startsWith("/"))
88
+ return file;
89
+ const normalized = stripPrivate(file);
90
+ const rel = relative(stripPrivate(cwd), normalized);
91
+ return rel.startsWith("..") ? normalized : rel;
92
+ }
93
+ function stripPrivate(path) {
94
+ return path.startsWith("/private/") ? path.slice("/private".length) : path;
95
+ }
96
+ /**
97
+ * Flatten issues to TOON rows, dropping exact duplicates.
98
+ *
99
+ * The same Swift diagnostic is reported once per target that compiles the
100
+ * file, so a warning in a shared package can appear a dozen times in one
101
+ * build. Only the first is information; the rest are paid-for repetition.
102
+ */
103
+ export function toDiagnostics(issues) {
104
+ const seen = new Set();
105
+ const out = [];
106
+ for (const issue of issues ?? []) {
107
+ const { file, line, col } = parseSourceURL(issue.sourceURL);
108
+ const message = (issue.message ?? "").replace(/\s+/g, " ").trim();
109
+ const key = `${file}:${line}:${col}:${message}`;
110
+ if (seen.has(key))
111
+ continue;
112
+ seen.add(key);
113
+ out.push({
114
+ file,
115
+ line,
116
+ col,
117
+ type: shortIssueType(issue.issueType),
118
+ message,
119
+ });
120
+ }
121
+ return out;
122
+ }
123
+ /** "Swift Compiler Error" is four tokens to say "swift". */
124
+ function shortIssueType(issueType) {
125
+ if (!issueType)
126
+ return "error";
127
+ const lower = issueType.toLowerCase();
128
+ if (lower.includes("swift"))
129
+ return "swift";
130
+ if (lower.includes("clang") || lower.includes("c compiler"))
131
+ return "clang";
132
+ if (lower.includes("linker"))
133
+ return "linker";
134
+ if (lower.includes("script"))
135
+ return "script";
136
+ if (lower.includes("package"))
137
+ return "package";
138
+ if (lower.includes("uncategorized"))
139
+ return "xcodebuild";
140
+ return lower.replace(/ (error|warning)$/, "").replace(/\s+/g, "-");
141
+ }
142
+ /** The device a run actually landed on, as one readable line. */
143
+ export function describeDevice(device) {
144
+ if (!device)
145
+ return "unknown";
146
+ const name = device.deviceName || device.modelName;
147
+ const parts = [
148
+ name,
149
+ device.osVersion
150
+ ? `${device.platform ?? "iOS"} ${device.osVersion}`
151
+ : device.platform,
152
+ ];
153
+ const described = parts.filter((part) => part && part.length > 0).join(" · ");
154
+ return described.length > 0 ? described : "unknown";
155
+ }
156
+ //# sourceMappingURL=xcresult.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xcresult.js","sourceRoot":"","sources":["../../src/xcresult.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "xcodebuild-axi",
3
+ "version": "0.1.0",
4
+ "description": "AXI-compliant xcodebuild wrapper — turns multi-megabyte Xcode build and test transcripts into a few lines of TOON",
5
+ "type": "module",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/alexrrouse/xcodebuild-axi.git"
9
+ },
10
+ "homepage": "https://github.com/alexrrouse/xcodebuild-axi#readme",
11
+ "bugs": {
12
+ "url": "https://github.com/alexrrouse/xcodebuild-axi/issues"
13
+ },
14
+ "keywords": [
15
+ "xcodebuild",
16
+ "xcode",
17
+ "ios",
18
+ "swift",
19
+ "cli",
20
+ "agent",
21
+ "axi",
22
+ "toon"
23
+ ],
24
+ "bin": {
25
+ "xcodebuild-axi": "dist/bin/xcodebuild-axi.js"
26
+ },
27
+ "files": [
28
+ "dist",
29
+ "skills/xcodebuild-axi",
30
+ "LICENSE",
31
+ "README.md",
32
+ "CHANGELOG.md"
33
+ ],
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
37
+ "scripts": {
38
+ "build": "tsc",
39
+ "dev": "tsx bin/xcodebuild-axi.ts",
40
+ "test": "vitest run",
41
+ "test:watch": "vitest",
42
+ "lint": "eslint .",
43
+ "format": "prettier --write .",
44
+ "format:check": "prettier --check .",
45
+ "build:skill": "tsx scripts/build-skill.ts",
46
+ "prepublishOnly": "npm run build",
47
+ "coverage": "tsx scripts/coverage.ts",
48
+ "coverage:check": "tsx scripts/coverage.ts --check",
49
+ "benchmark": "tsx scripts/benchmark.ts"
50
+ },
51
+ "author": "Alex Rouse",
52
+ "license": "MIT",
53
+ "os": [
54
+ "darwin"
55
+ ],
56
+ "engines": {
57
+ "node": ">=20"
58
+ },
59
+ "dependencies": {
60
+ "@toon-format/toon": "^4.1.1",
61
+ "axi-sdk-js": "^0.1.10"
62
+ },
63
+ "devDependencies": {
64
+ "@eslint/js": "^10.0.1",
65
+ "@types/node": "^22.0.0",
66
+ "eslint": "^10.1.0",
67
+ "globals": "^17.4.0",
68
+ "gpt-tokenizer": "^4.0.0",
69
+ "prettier": "^3.8.1",
70
+ "tsx": "^4.0.0",
71
+ "typescript": "^5.7.0",
72
+ "typescript-eslint": "^8.58.0",
73
+ "vitest": "^5.0.1"
74
+ }
75
+ }
@@ -0,0 +1,64 @@
1
+ ---
2
+ name: xcodebuild-axi
3
+ description: >
4
+ Build, test, and inspect Xcode projects without reading xcodebuild's output.
5
+ Use for any iOS/macOS build, test run, scheme or destination lookup, build
6
+ setting read, or .xcresult inspection — raw xcodebuild prints hundreds of
7
+ kilobytes to say what this prints in a few lines.
8
+ ---
9
+
10
+ # xcodebuild-axi
11
+
12
+ Agent-ergonomic wrapper around xcodebuild. Prefer it over raw `xcodebuild` for any build / test / inspect of an Xcode project.
13
+
14
+ Run it with no arguments first — it reports the project in front of you, its
15
+ schemes, and how the last run went.
16
+
17
+ ```sh
18
+ npx -y xcodebuild-axi
19
+ ```
20
+
21
+ ## Commands
22
+
23
+ ```
24
+ usage: xcodebuild-axi [command] [flags]
25
+ commands[23]:
26
+ (none)=dashboard
27
+ build, test, tests, clean, analyze, archive, export
28
+ schemes, destinations, testplans, settings, packages, info
29
+ result, coverage, sim, platforms, setup
30
+ localize, xcframework, find, migrate
31
+ flags[2]:
32
+ --help, -v/-V/--version
33
+ env[2]:
34
+ XCODEBUILD_BIN override the wrapped xcodebuild binary
35
+ DEVELOPER_DIR select an Xcode, as xcodebuild itself reads it
36
+ exit:
37
+ 0 success, 1 the build or tests failed, 2 usage error
38
+ examples:
39
+ xcodebuild-axi
40
+ xcodebuild-axi build --scheme MyApp
41
+ xcodebuild-axi test --scheme MyApp --device "iPhone 17 Pro" --coverage
42
+ xcodebuild-axi settings --key PRODUCT_BUNDLE_IDENTIFIER
43
+ xcodebuild-axi setup hooks
44
+ ```
45
+
46
+ Every command takes `--help` with its own flags and examples. The CLI's own
47
+ output is the authority on everything below the top level; prefer asking it
48
+ over guessing.
49
+
50
+ ## What to reach for
51
+
52
+ - Building or testing: `build` and `test` report only what failed, with
53
+ `file,line,col`. The full transcript is written to a log whose path they
54
+ print, so detail is one read away and never the default.
55
+ - Picking a device: pass `--device "iPhone 17 Pro"`, or nothing at all to get
56
+ the newest simulator. Do not hand-write a `-destination` specifier.
57
+ - Reading a previous run: `result <path.xcresult>`, which re-reads without
58
+ rebuilding.
59
+ - Build settings: `settings --key NAME`, not a full dump.
60
+
61
+ ## Exit codes
62
+
63
+ `0` success, `1` the build or tests failed, `2` usage error. A failed build
64
+ still prints its full report to stdout.