nativeapptemplate-agent 0.1.2 → 0.2.1

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 (108) hide show
  1. package/CLAUDE.md +10 -8
  2. package/README.md +65 -21
  3. package/dist/agents/judge.d.ts +8 -0
  4. package/dist/agents/judge.d.ts.map +1 -1
  5. package/dist/agents/judge.js +167 -29
  6. package/dist/agents/judge.js.map +1 -1
  7. package/dist/agents/repair.d.ts +14 -0
  8. package/dist/agents/repair.d.ts.map +1 -0
  9. package/dist/agents/repair.js +103 -0
  10. package/dist/agents/repair.js.map +1 -0
  11. package/dist/agents/types.d.ts +34 -1
  12. package/dist/agents/types.d.ts.map +1 -1
  13. package/dist/dispatch.d.ts +21 -2
  14. package/dist/dispatch.d.ts.map +1 -1
  15. package/dist/dispatch.js +250 -14
  16. package/dist/dispatch.js.map +1 -1
  17. package/dist/env-bridge.d.ts +17 -0
  18. package/dist/env-bridge.d.ts.map +1 -0
  19. package/dist/env-bridge.js +211 -0
  20. package/dist/env-bridge.js.map +1 -0
  21. package/dist/index.d.ts +11 -0
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +88 -3
  24. package/dist/index.js.map +1 -1
  25. package/dist/mcp.d.ts +5 -0
  26. package/dist/mcp.d.ts.map +1 -0
  27. package/dist/mcp.js +83 -0
  28. package/dist/mcp.js.map +1 -0
  29. package/dist/mobile.d.ts +31 -0
  30. package/dist/mobile.d.ts.map +1 -0
  31. package/dist/mobile.js +179 -0
  32. package/dist/mobile.js.map +1 -0
  33. package/dist/rails-lifecycle.d.ts +16 -0
  34. package/dist/rails-lifecycle.d.ts.map +1 -0
  35. package/dist/rails-lifecycle.js +184 -0
  36. package/dist/rails-lifecycle.js.map +1 -0
  37. package/dist/rename-overrides.d.ts +23 -0
  38. package/dist/rename-overrides.d.ts.map +1 -0
  39. package/dist/rename-overrides.js +58 -0
  40. package/dist/rename-overrides.js.map +1 -0
  41. package/dist/repair-loop.d.ts +25 -0
  42. package/dist/repair-loop.d.ts.map +1 -0
  43. package/dist/repair-loop.js +67 -0
  44. package/dist/repair-loop.js.map +1 -0
  45. package/dist/report/collect.d.ts +28 -0
  46. package/dist/report/collect.d.ts.map +1 -0
  47. package/dist/report/collect.js +117 -0
  48. package/dist/report/collect.js.map +1 -0
  49. package/dist/report/model.d.ts +49 -0
  50. package/dist/report/model.d.ts.map +1 -0
  51. package/dist/report/model.js +2 -0
  52. package/dist/report/model.js.map +1 -0
  53. package/dist/report/render.d.ts +3 -0
  54. package/dist/report/render.d.ts.map +1 -0
  55. package/dist/report/render.js +242 -0
  56. package/dist/report/render.js.map +1 -0
  57. package/dist/report/theme.d.ts +2 -0
  58. package/dist/report/theme.d.ts.map +1 -0
  59. package/dist/report/theme.js +115 -0
  60. package/dist/report/theme.js.map +1 -0
  61. package/dist/slug.d.ts +3 -0
  62. package/dist/slug.d.ts.map +1 -1
  63. package/dist/slug.js +30 -0
  64. package/dist/slug.js.map +1 -1
  65. package/dist/validation/capture.d.ts.map +1 -1
  66. package/dist/validation/capture.js +19 -2
  67. package/dist/validation/capture.js.map +1 -1
  68. package/dist/validation/discover.js +1 -1
  69. package/dist/validation/discover.js.map +1 -1
  70. package/dist/validation/index.d.ts +8 -2
  71. package/dist/validation/index.d.ts.map +1 -1
  72. package/dist/validation/index.js +4 -1
  73. package/dist/validation/index.js.map +1 -1
  74. package/dist/validation/launch.d.ts +10 -0
  75. package/dist/validation/launch.d.ts.map +1 -1
  76. package/dist/validation/launch.js +127 -12
  77. package/dist/validation/launch.js.map +1 -1
  78. package/dist/validation/layer1.d.ts.map +1 -1
  79. package/dist/validation/layer1.js +12 -4
  80. package/dist/validation/layer1.js.map +1 -1
  81. package/dist/validation/layer2.js +1 -1
  82. package/dist/validation/layer2.js.map +1 -1
  83. package/dist/validation/scenarios/queue.d.ts +12 -0
  84. package/dist/validation/scenarios/queue.d.ts.map +1 -0
  85. package/dist/validation/scenarios/queue.js +216 -0
  86. package/dist/validation/scenarios/queue.js.map +1 -0
  87. package/dist/validation/stage1.d.ts +2 -0
  88. package/dist/validation/stage1.d.ts.map +1 -1
  89. package/dist/validation/stage1.js +2 -0
  90. package/dist/validation/stage1.js.map +1 -1
  91. package/dist/validation/stage2-judge.d.ts +23 -0
  92. package/dist/validation/stage2-judge.d.ts.map +1 -0
  93. package/dist/validation/stage2-judge.js +225 -0
  94. package/dist/validation/stage2-judge.js.map +1 -0
  95. package/dist/validation/stage2.d.ts +74 -0
  96. package/dist/validation/stage2.d.ts.map +1 -0
  97. package/dist/validation/stage2.js +328 -0
  98. package/dist/validation/stage2.js.map +1 -0
  99. package/dist/validation/visual-judge.d.ts +37 -0
  100. package/dist/validation/visual-judge.d.ts.map +1 -1
  101. package/dist/validation/visual-judge.js +100 -16
  102. package/dist/validation/visual-judge.js.map +1 -1
  103. package/dist/version.d.ts +2 -0
  104. package/dist/version.d.ts.map +1 -0
  105. package/dist/version.js +17 -0
  106. package/dist/version.js.map +1 -0
  107. package/package.json +6 -3
  108. package/scripts/ruby/rename.rb +7 -3
package/dist/mobile.js ADDED
@@ -0,0 +1,179 @@
1
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
+ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
3
+ import { scrubbedEnv } from "./env.js";
4
+ const DEFAULT_COMMAND = "npx";
5
+ // Pinned to 0.0.54 (was @latest). mobile-mcp 0.0.55+ closes the stdio
6
+ // connection during the MCP `initialize` handshake with our pinned
7
+ // @modelcontextprotocol/sdk — reproduced 2026-05-23: 0.0.54 connects and
8
+ // lists tools; 0.0.55 and 0.0.56 both fail with "MCP error -32000:
9
+ // Connection closed" before any tool call. The wrapper's tool-shape parsing
10
+ // (envelope/prefix handling, `submit`/`device` args) is also written against
11
+ // 0.0.54. Re-validate the handshake + tool schemas (and likely bump the SDK)
12
+ // before moving this version forward.
13
+ const DEFAULT_ARGS = ["-y", "@mobilenext/mobile-mcp@0.0.54"];
14
+ // Lazy: spawns mobile-mcp on call, returns a typed client. close() shuts
15
+ // the subprocess down cleanly.
16
+ //
17
+ // Honors NATIVEAPPTEMPLATE_STUB_MOBILE=1 (and NATIVEAPPTEMPLATE_STUB_ALL=1)
18
+ // to short-circuit to a no-op client so unit tests don't need a sim or an
19
+ // npx download.
20
+ export async function createMobileClient(opts) {
21
+ if (isStubMobile())
22
+ return createStubMobileClient();
23
+ const transport = new StdioClientTransport({
24
+ command: opts?.command ?? DEFAULT_COMMAND,
25
+ args: [...(opts?.args ?? DEFAULT_ARGS)],
26
+ env: scrubbedEnv(),
27
+ stderr: "pipe",
28
+ });
29
+ const client = new Client({ name: "nativeapptemplate-agent-stage2", version: "0.1.0" });
30
+ await client.connect(transport);
31
+ return attachMobileClient(client);
32
+ }
33
+ // Wraps an already-connected MCP Client. Exported so tests can pair the
34
+ // wrapper with an in-memory MCP server impersonating mobile-mcp, without
35
+ // spawning npx. Production callers should use createMobileClient().
36
+ export function attachMobileClient(client) {
37
+ return wrapClient(client);
38
+ }
39
+ export function isStubMobile() {
40
+ return (process.env["NATIVEAPPTEMPLATE_STUB_ALL"] === "1" ||
41
+ process.env["NATIVEAPPTEMPLATE_STUB_MOBILE"] === "1");
42
+ }
43
+ // Tools that operate at the mobile-mcp server level rather than against
44
+ // a specific device. These don't take a `device` argument and would
45
+ // fail validation if we injected one.
46
+ const DEVICE_INDEPENDENT_TOOLS = new Set([
47
+ "mobile_list_available_devices",
48
+ ]);
49
+ function wrapClient(client) {
50
+ let activeDevice;
51
+ const callTool = async (name, args) => {
52
+ const merged = activeDevice !== undefined && !DEVICE_INDEPENDENT_TOOLS.has(name)
53
+ ? { device: activeDevice, ...(args ?? {}) }
54
+ : (args ?? {});
55
+ const result = (await client.callTool({
56
+ name,
57
+ arguments: merged,
58
+ }));
59
+ if (result.isError) {
60
+ throw new Error(`mobile-mcp ${name} failed: ${textOf(result)}`);
61
+ }
62
+ return result;
63
+ };
64
+ return {
65
+ callTool,
66
+ useDevice(name) {
67
+ activeDevice = name;
68
+ },
69
+ async listDevices() {
70
+ const result = await callTool("mobile_list_available_devices");
71
+ // mobile-mcp 0.0.54+ returns {"devices": [...]} (envelope object,
72
+ // not a bare array). Earlier behavior was a bare array. Accept
73
+ // either so the wrapper survives version drift in either direction.
74
+ const text = textOf(result);
75
+ if (!text)
76
+ return [];
77
+ try {
78
+ const parsed = JSON.parse(text);
79
+ if (Array.isArray(parsed))
80
+ return parsed;
81
+ if (parsed && typeof parsed === "object" && Array.isArray(parsed.devices)) {
82
+ return parsed.devices;
83
+ }
84
+ return [];
85
+ }
86
+ catch {
87
+ return [];
88
+ }
89
+ },
90
+ async listElements() {
91
+ const result = await callTool("mobile_list_elements_on_screen");
92
+ // mobile-mcp 0.0.54 returns "Found these elements on screen: [...]"
93
+ // — a human-readable text prefix followed by a JSON array, not
94
+ // pure JSON. Slice from the first '[' so JSON.parse sees just
95
+ // the array. Defensive against the prefix being absent in other
96
+ // versions too.
97
+ const text = textOf(result);
98
+ if (!text)
99
+ return [];
100
+ const arrayStart = text.indexOf("[");
101
+ if (arrayStart === -1)
102
+ return [];
103
+ try {
104
+ const parsed = JSON.parse(text.slice(arrayStart));
105
+ return Array.isArray(parsed) ? parsed : [];
106
+ }
107
+ catch {
108
+ return [];
109
+ }
110
+ },
111
+ async click(x, y) {
112
+ // mobile-mcp 0.0.54's iOS backend (mobilecli `io tap`) rejects
113
+ // non-integer coordinates. centerOf often produces .5 values for
114
+ // even-width elements; floor them at the tool boundary so callers
115
+ // don't have to think about it.
116
+ await callTool("mobile_click_on_screen_at_coordinates", {
117
+ x: Math.floor(x),
118
+ y: Math.floor(y),
119
+ });
120
+ },
121
+ async typeKeys(text, submit = false) {
122
+ // mobile-mcp 0.0.54 requires `submit: boolean` — passing only
123
+ // {text} fails Zod validation with "expected boolean, received
124
+ // undefined". Default false; opt in to true via the second
125
+ // arg when the caller wants the keyboard's return/submit key
126
+ // tapped after typing (form-submit / keyboard-dismiss).
127
+ await callTool("mobile_type_keys", { text, submit });
128
+ },
129
+ async pressButton(button) {
130
+ await callTool("mobile_press_button", { button });
131
+ },
132
+ async launchApp(packageName) {
133
+ // mobile-mcp uses `packageName` for the app id on both platforms (the
134
+ // bundle id on iOS). `device` is injected by callTool.
135
+ await callTool("mobile_launch_app", { packageName });
136
+ },
137
+ async takeScreenshot() {
138
+ const result = await callTool("mobile_take_screenshot");
139
+ const image = (result.content ?? []).find((c) => c.type === "image");
140
+ if (!image || typeof image.data !== "string") {
141
+ throw new Error("mobile-mcp mobile_take_screenshot returned no image data");
142
+ }
143
+ return {
144
+ data: Buffer.from(image.data, "base64"),
145
+ mimeType: typeof image.mimeType === "string" ? image.mimeType : "image/png",
146
+ };
147
+ },
148
+ async saveScreenshot(absolutePath) {
149
+ await callTool("mobile_save_screenshot", { saveTo: absolutePath });
150
+ },
151
+ async close() {
152
+ await client.close();
153
+ },
154
+ };
155
+ }
156
+ function textOf(result) {
157
+ for (const c of result.content ?? []) {
158
+ if (c.type === "text" && typeof c.text === "string")
159
+ return c.text;
160
+ }
161
+ return "";
162
+ }
163
+ function createStubMobileClient() {
164
+ const stubResult = { content: [] };
165
+ return {
166
+ callTool: async () => stubResult,
167
+ useDevice: () => { },
168
+ listDevices: async () => [],
169
+ listElements: async () => [],
170
+ click: async () => { },
171
+ typeKeys: async (_text, _submit) => { },
172
+ pressButton: async () => { },
173
+ launchApp: async (_packageName) => { },
174
+ takeScreenshot: async () => ({ data: Buffer.alloc(0), mimeType: "image/png" }),
175
+ saveScreenshot: async () => { },
176
+ close: async () => { },
177
+ };
178
+ }
179
+ //# sourceMappingURL=mobile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mobile.js","sourceRoot":"","sources":["../src/mobile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { type ChildProcess } from "node:child_process";
2
+ export type RailsHandle = {
3
+ readonly child: ChildProcess;
4
+ readonly url: string;
5
+ stop(): Promise<void>;
6
+ };
7
+ export type StartRailsInput = {
8
+ outDir: string;
9
+ host?: string;
10
+ port?: number;
11
+ readyTimeoutMs?: number;
12
+ shutdownGraceMs?: number;
13
+ };
14
+ export declare function startRails(input: StartRailsInput): Promise<RailsHandle>;
15
+ export declare function waitForReady(host: string, port: number, timeoutMs: number, childExitedEarly?: () => boolean): Promise<boolean>;
16
+ //# sourceMappingURL=rails-lifecycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rails-lifecycle.d.ts","sourceRoot":"","sources":["../src/rails-lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAoB9D,MAAM,MAAM,WAAW,GAAG;IAGxB,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,MAAM,CAAC;IAIf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IAId,cAAc,CAAC,EAAE,MAAM,CAAC;IAGxB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAKF,wBAAsB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,WAAW,CAAC,CAkG7E;AAED,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,gBAAgB,CAAC,EAAE,MAAM,OAAO,GAC/B,OAAO,CAAC,OAAO,CAAC,CAgBlB"}
@@ -0,0 +1,184 @@
1
+ import { spawn } from "node:child_process";
2
+ import { setTimeout as sleep } from "node:timers/promises";
3
+ import { readFile } from "node:fs/promises";
4
+ import { join } from "node:path";
5
+ import { scrubbedEnv } from "./env.js";
6
+ import { trace } from "./trace.js";
7
+ const DEFAULT_READY_TIMEOUT_MS = 60_000;
8
+ const DEFAULT_SHUTDOWN_GRACE_MS = 5_000;
9
+ export async function startRails(input) {
10
+ const { host, port } = await resolveHostPort(input);
11
+ const url = `http://${host}:${port}`;
12
+ // Two prep steps before bin/dev:
13
+ // 1. bundle install — Layer 2 ran it but gems land in a path
14
+ // bin/rails sees for one-shot CLI; bin/dev needs them again
15
+ // in the same shell context. Idempotent + fast (~1s warm).
16
+ // 2. bin/rails db:prepare — creates+migrates the primary db AND
17
+ // the cable/queue/cache databases the substrate uses
18
+ // (config/database.yml multi-db). Without it, Solid Queue
19
+ // (loaded as a Puma plugin via SOLID_QUEUE_IN_PUMA=true in
20
+ // .env) can't open its queue connection and crashes Puma:
21
+ // "Detected Solid Queue has gone away, stopping Puma".
22
+ trace("dispatch", `rails-lifecycle: bundle install in ${input.outDir}`);
23
+ await runMiseStep(input.outDir, ["bundle", "install"], 180_000, "bundle install");
24
+ trace("dispatch", `rails-lifecycle: bin/rails db:prepare in ${input.outDir}`);
25
+ await runMiseStep(input.outDir, ["bin/rails", "db:prepare"], 60_000, "db:prepare");
26
+ // db:seed_fu — substrate uses the seed-fu gem (NOT standard
27
+ // db:seed). Required because db:prepare only runs seeds on
28
+ // fresh-create; for an already-existing DB it just migrates,
29
+ // leaving PrivacyVersion / TermsVersion empty — which causes
30
+ // signup to 500 in render_create_success when it looks up the
31
+ // current version. seed-fu is idempotent.
32
+ trace("dispatch", `rails-lifecycle: bin/rails db:seed_fu in ${input.outDir}`);
33
+ await runMiseStep(input.outDir, ["bin/rails", "db:seed_fu"], 60_000, "db:seed_fu");
34
+ // Wrap with `mise exec --` so the substrate's pinned Ruby /
35
+ // bundler version activates (substrate's .mise.toml or .tool-versions
36
+ // pins a specific version that may not match system Ruby). Layer 2
37
+ // uses the same wrapper for `bin/rails runner` — match it here so
38
+ // Stage 2's server uses the same toolchain that Layer 2 validated.
39
+ trace("dispatch", `rails-lifecycle: spawning mise exec -- bin/dev in ${input.outDir} (target ${url})`);
40
+ const child = spawn("mise", ["exec", "--", "bin/dev"], {
41
+ cwd: input.outDir,
42
+ env: scrubbedEnv(),
43
+ stdio: ["ignore", "pipe", "pipe"],
44
+ detached: false,
45
+ });
46
+ // Drain stdio so the child doesn't block on backed-up pipes; route
47
+ // a tail to the dispatch trace so silent failures (port already in
48
+ // use, missing gem, etc.) surface in tmp/trace/dispatch.log.
49
+ const tail = [];
50
+ const onData = (c) => {
51
+ const text = c.toString("utf8");
52
+ tail.push(text);
53
+ if (tail.length > 50)
54
+ tail.shift();
55
+ };
56
+ child.stdout?.on("data", onData);
57
+ child.stderr?.on("data", onData);
58
+ // Surface child crash before we time out waiting for ready.
59
+ let exitedEarly = false;
60
+ child.on("exit", (code) => {
61
+ exitedEarly = true;
62
+ trace("dispatch", `rails-lifecycle: child exited early (code=${code}); recent output: ${tail.join("").slice(-500)}`);
63
+ });
64
+ const readyTimeoutMs = input.readyTimeoutMs ?? DEFAULT_READY_TIMEOUT_MS;
65
+ const ready = await waitForReady(host, port, readyTimeoutMs, () => exitedEarly);
66
+ if (!ready) {
67
+ child.kill("SIGTERM");
68
+ const lastOutput = tail.join("").slice(-500);
69
+ throw new Error(`Rails server at ${url} did not become ready within ${readyTimeoutMs}ms` +
70
+ (exitedEarly ? ` (child exited early)` : "") +
71
+ `; recent output: ${lastOutput}`);
72
+ }
73
+ trace("dispatch", `rails-lifecycle: server reachable at ${url}`);
74
+ const shutdownGraceMs = input.shutdownGraceMs ?? DEFAULT_SHUTDOWN_GRACE_MS;
75
+ return {
76
+ child,
77
+ url,
78
+ async stop() {
79
+ if (exitedEarly)
80
+ return;
81
+ trace("dispatch", `rails-lifecycle: stopping ${url}`);
82
+ child.kill("SIGTERM");
83
+ const exit = new Promise((resolve) => {
84
+ if (child.exitCode !== null) {
85
+ resolve();
86
+ }
87
+ else {
88
+ child.on("exit", () => resolve());
89
+ }
90
+ });
91
+ const timeout = sleep(shutdownGraceMs).then(() => {
92
+ if (child.exitCode === null) {
93
+ trace("dispatch", `rails-lifecycle: SIGTERM did not exit in ${shutdownGraceMs}ms; SIGKILL`);
94
+ child.kill("SIGKILL");
95
+ }
96
+ });
97
+ await Promise.race([exit, timeout]);
98
+ },
99
+ };
100
+ }
101
+ export async function waitForReady(host, port, timeoutMs, childExitedEarly) {
102
+ const url = `http://${host}:${port}/`;
103
+ const deadline = Date.now() + timeoutMs;
104
+ while (Date.now() < deadline) {
105
+ if (childExitedEarly?.())
106
+ return false;
107
+ try {
108
+ const res = await fetch(url, { signal: AbortSignal.timeout(2_000) });
109
+ // Any HTTP response (even 404) means Rails is up. Network/connection
110
+ // errors throw; we keep polling.
111
+ if (res.status < 500)
112
+ return true;
113
+ }
114
+ catch {
115
+ // ECONNREFUSED, EHOSTUNREACH, AbortError — not ready, keep polling.
116
+ }
117
+ await sleep(1_000);
118
+ }
119
+ return false;
120
+ }
121
+ async function resolveHostPort(input) {
122
+ if (input.host !== undefined && input.port !== undefined) {
123
+ return { host: input.host, port: input.port };
124
+ }
125
+ // Read from the generated rails dir's .env (which mirrors substrate's
126
+ // .env via the rails worker's verbatim copy).
127
+ const envPath = join(input.outDir, ".env");
128
+ const env = await readDotenv(envPath);
129
+ const host = input.host ?? env["HOST"] ?? "0.0.0.0";
130
+ const portStr = env["PORT"];
131
+ const port = input.port ?? (portStr ? Number.parseInt(portStr, 10) : 3000);
132
+ if (Number.isNaN(port))
133
+ throw new Error(`rails-lifecycle: PORT in ${envPath} is not a number: ${portStr}`);
134
+ return { host, port };
135
+ }
136
+ async function runMiseStep(cwd, args, timeoutMs, label) {
137
+ await new Promise((resolveStep, rejectStep) => {
138
+ const child = spawn("mise", ["exec", "--", ...args], {
139
+ cwd,
140
+ env: scrubbedEnv(),
141
+ stdio: ["ignore", "pipe", "pipe"],
142
+ });
143
+ const out = [];
144
+ child.stdout?.on("data", (c) => out.push(c.toString("utf8")));
145
+ child.stderr?.on("data", (c) => out.push(c.toString("utf8")));
146
+ const timer = setTimeout(() => child.kill("SIGTERM"), timeoutMs);
147
+ child.on("exit", (code) => {
148
+ clearTimeout(timer);
149
+ if (code === 0)
150
+ resolveStep();
151
+ else
152
+ rejectStep(new Error(`${label} exited ${code}: ${out.join("").slice(-800)}`));
153
+ });
154
+ child.on("error", (err) => {
155
+ clearTimeout(timer);
156
+ rejectStep(err);
157
+ });
158
+ });
159
+ }
160
+ async function readDotenv(path) {
161
+ let raw;
162
+ try {
163
+ raw = await readFile(path, "utf8");
164
+ }
165
+ catch {
166
+ return {};
167
+ }
168
+ const out = {};
169
+ for (const line of raw.split("\n")) {
170
+ const trimmed = line.trim();
171
+ if (!trimmed || trimmed.startsWith("#"))
172
+ continue;
173
+ const eq = trimmed.indexOf("=");
174
+ if (eq < 1)
175
+ continue;
176
+ const key = trimmed.slice(0, eq).trim();
177
+ const valueRaw = trimmed.slice(eq + 1).trim();
178
+ const value = valueRaw.replace(/^"(.*)"$/, "$1").replace(/^'(.*)'$/, "$1");
179
+ if (key && value)
180
+ out[key] = value;
181
+ }
182
+ return out;
183
+ }
184
+ //# sourceMappingURL=rails-lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rails-lifecycle.js","sourceRoot":"","sources":["../src/rails-lifecycle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { Entity, RenamePair } from "./agents/types.js";
2
+ export type OverrideOutcome = {
3
+ kind: "changed";
4
+ from: string;
5
+ was: string;
6
+ to: string;
7
+ } | {
8
+ kind: "noop";
9
+ from: string;
10
+ to: string;
11
+ } | {
12
+ kind: "unmatched";
13
+ from: string;
14
+ to: string;
15
+ };
16
+ export type ApplyOverridesResult = {
17
+ plan: RenamePair[];
18
+ outcomes: OverrideOutcome[];
19
+ };
20
+ export declare function parseRenamePair(raw: string | undefined): RenamePair | null;
21
+ export declare function applyRenameOverrides(plan: readonly RenamePair[], overrides: readonly RenamePair[]): ApplyOverridesResult;
22
+ export declare function syncEntityNames(entities: readonly Entity[], outcomes: readonly OverrideOutcome[]): readonly Entity[];
23
+ //# sourceMappingURL=rename-overrides.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rename-overrides.d.ts","sourceRoot":"","sources":["../src/rename-overrides.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAO5D,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC1D;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpD,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,UAAU,EAAE,CAAC;IACnB,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B,CAAC;AAKF,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,IAAI,CAQ1E;AAOD,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,SAAS,UAAU,EAAE,EAC3B,SAAS,EAAE,SAAS,UAAU,EAAE,GAC/B,oBAAoB,CAiBtB;AAQD,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,QAAQ,EAAE,SAAS,eAAe,EAAE,GACnC,SAAS,MAAM,EAAE,CAUnB"}
@@ -0,0 +1,58 @@
1
+ // Parse a single `--rename From=To` value. Splits on the first `=` only;
2
+ // returns null for malformed input (missing flag value, empty side) so the
3
+ // caller can warn and skip rather than push a junk pair into the plan.
4
+ export function parseRenamePair(raw) {
5
+ if (raw === undefined)
6
+ return null;
7
+ const eq = raw.indexOf("=");
8
+ if (eq <= 0)
9
+ return null;
10
+ const from = raw.slice(0, eq).trim();
11
+ const to = raw.slice(eq + 1).trim();
12
+ if (!from || !to)
13
+ return null;
14
+ return { from, to };
15
+ }
16
+ // Merge manual overrides onto the planner's rename plan. Semantics are
17
+ // "change a planned target": an override keys on the substrate token (`from`)
18
+ // and replaces the planner's chosen target (`to`). Overrides whose `from`
19
+ // matches no planned rename are reported as `unmatched` and dropped — we don't
20
+ // silently invent new renames. Later overrides for the same `from` win.
21
+ export function applyRenameOverrides(plan, overrides) {
22
+ const merged = plan.map((p) => ({ ...p }));
23
+ const outcomes = [];
24
+ for (const override of overrides) {
25
+ const target = merged.find((p) => p.from === override.from);
26
+ if (!target) {
27
+ outcomes.push({ kind: "unmatched", from: override.from, to: override.to });
28
+ continue;
29
+ }
30
+ if (target.to === override.to) {
31
+ outcomes.push({ kind: "noop", from: override.from, to: override.to });
32
+ continue;
33
+ }
34
+ outcomes.push({ kind: "changed", from: override.from, was: target.to, to: override.to });
35
+ target.to = override.to;
36
+ }
37
+ return { plan: merged, outcomes };
38
+ }
39
+ // Keep entity names in step with rename-plan overrides. The planner names each
40
+ // entity after its rename target (e.g. it emits both `Shopkeeper -> Monitor`
41
+ // and an entity `Monitor` that `replaces` Shopkeeper), so a `--rename` that
42
+ // changes the target must also rename the entity or the report contradicts its
43
+ // own rename plan. Only entities whose name still matches the planner's old
44
+ // target are touched — an entity deliberately named differently is left alone.
45
+ export function syncEntityNames(entities, outcomes) {
46
+ const changes = new Map();
47
+ for (const o of outcomes) {
48
+ if (o.kind === "changed")
49
+ changes.set(o.from, { was: o.was, to: o.to });
50
+ }
51
+ if (changes.size === 0)
52
+ return entities;
53
+ return entities.map((e) => {
54
+ const change = changes.get(e.replaces);
55
+ return change && e.name === change.was ? { ...e, name: change.to } : e;
56
+ });
57
+ }
58
+ //# sourceMappingURL=rename-overrides.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rename-overrides.js","sourceRoot":"","sources":["../src/rename-overrides.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { Platform, PlatformDetail } from "./agents/types.js";
2
+ import type { RepairAttempt } from "./report/model.js";
3
+ import type { RepairLayer } from "./agents/repair.js";
4
+ export type RevalidateResult = Pick<PlatformDetail, "layer1" | "layer2">;
5
+ export type RepairLoopDeps = {
6
+ repair: (platform: Platform, layer: RepairLayer, detail: PlatformDetail) => Promise<{
7
+ action: string;
8
+ }>;
9
+ revalidate: (platform: Platform) => Promise<RevalidateResult>;
10
+ };
11
+ export type RepairLoopInput = {
12
+ platforms: readonly PlatformDetail[];
13
+ reviewerPass: boolean;
14
+ maxIterations: number;
15
+ deps: RepairLoopDeps;
16
+ };
17
+ export type RepairLoopResult = {
18
+ platforms: PlatformDetail[];
19
+ attempts: RepairAttempt[];
20
+ overallPass: boolean;
21
+ summary: string;
22
+ };
23
+ export declare const REPAIR_ITERATION_CAP = 5;
24
+ export declare function runRepairLoop(input: RepairLoopInput): Promise<RepairLoopResult>;
25
+ //# sourceMappingURL=repair-loop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repair-loop.d.ts","sourceRoot":"","sources":["../src/repair-loop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAKtD,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,cAAc,EAAE,QAAQ,GAAG,QAAQ,CAAC,CAAC;AAEzE,MAAM,MAAM,cAAc,GAAG;IAG3B,MAAM,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,cAAc,KAAK,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAExG,UAAU,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;CAC/D,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,SAAS,cAAc,EAAE,CAAC;IACrC,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,cAAc,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAMF,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAYtC,wBAAsB,aAAa,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA4BrF"}
@@ -0,0 +1,67 @@
1
+ // The CLAUDE.md hard cap: never iterate more than this regardless of the
2
+ // requested maxIterations.
3
+ export const REPAIR_ITERATION_CAP = 5;
4
+ // Bounded self-repair: while a code-repairable layer is failing, repair the
5
+ // highest-priority failure, re-validate that platform, and record the
6
+ // attempt — until everything passes or the iteration cap is hit. Pure
7
+ // control flow: all I/O (the repair agent, the validators) is injected via
8
+ // deps, so this is unit-testable without the LLM or a device.
9
+ //
10
+ // Scope: Layer 1 (leftover tokens) then Layer 2 (build) are the
11
+ // code-repairable, cheaply re-checkable layers. Layer 3 (vision) and the
12
+ // contract reviewer are surfaced but not auto-repaired — a Layer 3 miss is
13
+ // often environmental (e.g. a first-launch system dialog), not a source bug.
14
+ export async function runRepairLoop(input) {
15
+ const cap = Math.min(input.maxIterations, REPAIR_ITERATION_CAP);
16
+ const platforms = input.platforms.map((p) => ({ ...p }));
17
+ const attempts = [];
18
+ for (let iteration = 1; iteration <= cap; iteration++) {
19
+ const target = nextTarget(platforms);
20
+ if (!target)
21
+ break; // no code-repairable failure remains
22
+ const detail = platforms.find((p) => p.platform === target.platform);
23
+ const { action } = await input.deps.repair(target.platform, target.layer, detail);
24
+ const revalidated = await input.deps.revalidate(target.platform);
25
+ const idx = platforms.findIndex((p) => p.platform === target.platform);
26
+ platforms[idx] = { ...platforms[idx], layer1: revalidated.layer1, layer2: revalidated.layer2 };
27
+ const resolved = target.layer === "layer1" ? revalidated.layer1.pass : revalidated.layer2.pass;
28
+ attempts.push({ iteration, failingLayer: target.layer, platform: target.platform, action, resolved });
29
+ if (computeOverall(platforms, input.reviewerPass))
30
+ break;
31
+ }
32
+ return {
33
+ platforms,
34
+ attempts,
35
+ overallPass: computeOverall(platforms, input.reviewerPass),
36
+ summary: summarize(platforms, input.reviewerPass),
37
+ };
38
+ }
39
+ // Highest-priority code-repairable failure: all Layer 1 misses before any
40
+ // Layer 2 miss (leftover tokens routinely cause the build error, so fixing
41
+ // structure first avoids chasing a downstream symptom).
42
+ function nextTarget(platforms) {
43
+ for (const p of platforms)
44
+ if (!p.layer1.pass)
45
+ return { platform: p.platform, layer: "layer1" };
46
+ for (const p of platforms)
47
+ if (!p.layer2.pass)
48
+ return { platform: p.platform, layer: "layer2" };
49
+ return undefined;
50
+ }
51
+ function computeOverall(platforms, reviewerPass) {
52
+ const layer1And2 = platforms.every((p) => p.layer1.pass && p.layer2.pass);
53
+ const layer3 = platforms.every((p) => p.layer3 === undefined || p.layer3.pass);
54
+ return layer1And2 && layer3 && reviewerPass;
55
+ }
56
+ // Mirrors the judge's one-line summary so the post-repair report reads
57
+ // identically to a first-pass report.
58
+ function summarize(platforms, reviewerPass) {
59
+ const total = platforms.length;
60
+ const l1 = platforms.filter((p) => p.layer1.pass).length;
61
+ const l2 = platforms.filter((p) => p.layer2.pass).length;
62
+ const l3Plats = platforms.filter((p) => p.layer3 !== undefined);
63
+ const l3 = l3Plats.filter((p) => p.layer3.pass).length;
64
+ const l3Summary = l3Plats.length > 0 ? `Layer 3 ${l3}/${l3Plats.length} pass` : "Layer 3 skipped";
65
+ return `Layer 1 ${l1}/${total} pass · Layer 2 ${l2}/${total} pass · ${l3Summary} · reviewer ${reviewerPass ? "PASS" : "FAIL"}`;
66
+ }
67
+ //# sourceMappingURL=repair-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repair-loop.js","sourceRoot":"","sources":["../src/repair-loop.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { DomainSpec, JudgeResult, ReviewerResult } from "../agents/types.js";
2
+ import type { RepairAttempt, RunReport } from "./model.js";
3
+ export type ReportFormat = "html" | "json" | "both";
4
+ export type BuildRunReportInput = {
5
+ spec: string;
6
+ domain: DomainSpec;
7
+ judge: JudgeResult;
8
+ reviewer: ReviewerResult;
9
+ agentVersion: string;
10
+ judgeModel: string;
11
+ visualLevel: 0 | 1 | 2;
12
+ startedAt: number;
13
+ finishedAt: number;
14
+ repairAttempts?: readonly RepairAttempt[];
15
+ };
16
+ export declare function buildRunReport(input: BuildRunReportInput): RunReport;
17
+ export type WriteReportOptions = {
18
+ dir: string;
19
+ format?: ReportFormat;
20
+ embed?: boolean;
21
+ };
22
+ export type ReportPaths = {
23
+ jsonPath?: string;
24
+ htmlPath?: string;
25
+ };
26
+ export declare function writeReport(report: RunReport, options: WriteReportOptions): Promise<ReportPaths>;
27
+ export declare function collectScreenshotPaths(report: RunReport): string[];
28
+ //# sourceMappingURL=collect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collect.d.ts","sourceRoot":"","sources":["../../src/report/collect.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAElF,OAAO,KAAK,EAAY,aAAa,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAErE,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAEpD,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,WAAW,CAAC;IACnB,QAAQ,EAAE,cAAc,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;CAC3C,CAAC;AAIF,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,SAAS,CAqCpE;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,wBAAsB,WAAW,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,WAAW,CAAC,CAqBtG;AAID,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,EAAE,CAgBlE"}