nativeapptemplate-agent 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +137 -0
- package/LICENSE +21 -0
- package/README.md +192 -0
- package/dist/adb.d.ts +2 -0
- package/dist/adb.d.ts.map +1 -0
- package/dist/adb.js +31 -0
- package/dist/adb.js.map +1 -0
- package/dist/agents/contract-extract.d.ts +26 -0
- package/dist/agents/contract-extract.d.ts.map +1 -0
- package/dist/agents/contract-extract.js +205 -0
- package/dist/agents/contract-extract.js.map +1 -0
- package/dist/agents/judge.d.ts +21 -0
- package/dist/agents/judge.d.ts.map +1 -0
- package/dist/agents/judge.js +119 -0
- package/dist/agents/judge.js.map +1 -0
- package/dist/agents/planner.d.ts +3 -0
- package/dist/agents/planner.d.ts.map +1 -0
- package/dist/agents/planner.js +154 -0
- package/dist/agents/planner.js.map +1 -0
- package/dist/agents/reviewer.d.ts +9 -0
- package/dist/agents/reviewer.d.ts.map +1 -0
- package/dist/agents/reviewer.js +108 -0
- package/dist/agents/reviewer.js.map +1 -0
- package/dist/agents/types.d.ts +54 -0
- package/dist/agents/types.d.ts.map +1 -0
- package/dist/agents/types.js +2 -0
- package/dist/agents/types.js.map +1 -0
- package/dist/agents/workers/android.d.ts +3 -0
- package/dist/agents/workers/android.d.ts.map +1 -0
- package/dist/agents/workers/android.js +116 -0
- package/dist/agents/workers/android.js.map +1 -0
- package/dist/agents/workers/ios.d.ts +3 -0
- package/dist/agents/workers/ios.d.ts.map +1 -0
- package/dist/agents/workers/ios.js +110 -0
- package/dist/agents/workers/ios.js.map +1 -0
- package/dist/agents/workers/rails.d.ts +3 -0
- package/dist/agents/workers/rails.d.ts.map +1 -0
- package/dist/agents/workers/rails.js +145 -0
- package/dist/agents/workers/rails.js.map +1 -0
- package/dist/dispatch.d.ts +3 -0
- package/dist/dispatch.d.ts.map +1 -0
- package/dist/dispatch.js +38 -0
- package/dist/dispatch.js.map +1 -0
- package/dist/env.d.ts +3 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +48 -0
- package/dist/env.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/ruby.d.ts +2 -0
- package/dist/ruby.d.ts.map +1 -0
- package/dist/ruby.js +21 -0
- package/dist/ruby.js.map +1 -0
- package/dist/slug.d.ts +3 -0
- package/dist/slug.d.ts.map +1 -0
- package/dist/slug.js +11 -0
- package/dist/slug.js.map +1 -0
- package/dist/stub.d.ts +3 -0
- package/dist/stub.d.ts.map +1 -0
- package/dist/stub.js +6 -0
- package/dist/stub.js.map +1 -0
- package/dist/trace.d.ts +3 -0
- package/dist/trace.d.ts.map +1 -0
- package/dist/trace.js +13 -0
- package/dist/trace.js.map +1 -0
- package/dist/validation/capture.d.ts +15 -0
- package/dist/validation/capture.d.ts.map +1 -0
- package/dist/validation/capture.js +141 -0
- package/dist/validation/capture.js.map +1 -0
- package/dist/validation/discover.d.ts +11 -0
- package/dist/validation/discover.d.ts.map +1 -0
- package/dist/validation/discover.js +113 -0
- package/dist/validation/discover.js.map +1 -0
- package/dist/validation/index.d.ts +17 -0
- package/dist/validation/index.d.ts.map +1 -0
- package/dist/validation/index.js +9 -0
- package/dist/validation/index.js.map +1 -0
- package/dist/validation/launch.d.ts +21 -0
- package/dist/validation/launch.d.ts.map +1 -0
- package/dist/validation/launch.js +109 -0
- package/dist/validation/launch.js.map +1 -0
- package/dist/validation/layer1.d.ts +16 -0
- package/dist/validation/layer1.d.ts.map +1 -0
- package/dist/validation/layer1.js +98 -0
- package/dist/validation/layer1.js.map +1 -0
- package/dist/validation/layer2.d.ts +17 -0
- package/dist/validation/layer2.d.ts.map +1 -0
- package/dist/validation/layer2.js +168 -0
- package/dist/validation/layer2.js.map +1 -0
- package/dist/validation/layer3.d.ts +22 -0
- package/dist/validation/layer3.d.ts.map +1 -0
- package/dist/validation/layer3.js +96 -0
- package/dist/validation/layer3.js.map +1 -0
- package/dist/validation/stage1.d.ts +17 -0
- package/dist/validation/stage1.d.ts.map +1 -0
- package/dist/validation/stage1.js +71 -0
- package/dist/validation/stage1.js.map +1 -0
- package/dist/validation/visual-judge.d.ts +25 -0
- package/dist/validation/visual-judge.d.ts.map +1 -0
- package/dist/validation/visual-judge.js +89 -0
- package/dist/validation/visual-judge.js.map +1 -0
- package/package.json +69 -0
- package/scripts/ruby/.gitkeep +0 -0
- package/scripts/ruby/rename.rb +221 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { readdir, readFile, stat } from "node:fs/promises";
|
|
2
|
+
import { extname, join, relative, basename } from "node:path";
|
|
3
|
+
const TEXT_EXTS = new Set([
|
|
4
|
+
".rb", ".erb", ".yml", ".yaml", ".json", ".md", ".gemspec", ".rake", ".ru",
|
|
5
|
+
".txt", ".sample", ".example", ".conf", ".html", ".css", ".scss", ".js", ".mjs",
|
|
6
|
+
".tt", ".lock",
|
|
7
|
+
".swift", ".plist", ".strings", ".xcconfig", ".entitlements", ".pbxproj",
|
|
8
|
+
".xcworkspacedata", ".modulemap", ".xcscheme",
|
|
9
|
+
".kt", ".kts", ".xml", ".gradle", ".pro", ".toml", ".properties", ".cfg",
|
|
10
|
+
".proto",
|
|
11
|
+
]);
|
|
12
|
+
const TEXT_BASENAMES = new Set([
|
|
13
|
+
"Gemfile", "Gemfile.lock", "Rakefile", "Procfile", "Procfile.dev",
|
|
14
|
+
"config.ru", "Dockerfile",
|
|
15
|
+
"Podfile", "Podfile.lock", "Package.swift", "Cartfile", "Makefile",
|
|
16
|
+
"gradlew", "gradlew.bat", "gradle.properties", "local.properties",
|
|
17
|
+
]);
|
|
18
|
+
const SKIP_SEGMENTS = new Set([
|
|
19
|
+
".git", "node_modules", "tmp", "log", "vendor",
|
|
20
|
+
"DerivedData", "Pods", "Carthage", "xcuserdata", ".build",
|
|
21
|
+
"build", ".gradle", ".idea", ".kotlin", "captures",
|
|
22
|
+
]);
|
|
23
|
+
export async function runLayer1(input) {
|
|
24
|
+
if (input.forbiddenTokens.length === 0) {
|
|
25
|
+
return { pass: true, findings: [] };
|
|
26
|
+
}
|
|
27
|
+
const root = await realRoot(input.projectDir);
|
|
28
|
+
if (!root) {
|
|
29
|
+
return { pass: false, findings: [] };
|
|
30
|
+
}
|
|
31
|
+
const regex = buildRegex(input.forbiddenTokens);
|
|
32
|
+
const findings = [];
|
|
33
|
+
for await (const filePath of walk(root)) {
|
|
34
|
+
const content = await safeRead(filePath);
|
|
35
|
+
if (!content)
|
|
36
|
+
continue;
|
|
37
|
+
const lines = content.split("\n");
|
|
38
|
+
for (let i = 0; i < lines.length; i++) {
|
|
39
|
+
const line = lines[i];
|
|
40
|
+
regex.lastIndex = 0;
|
|
41
|
+
let m;
|
|
42
|
+
while ((m = regex.exec(line)) !== null) {
|
|
43
|
+
findings.push({
|
|
44
|
+
token: m[0],
|
|
45
|
+
file: relative(root, filePath),
|
|
46
|
+
line: i + 1,
|
|
47
|
+
text: line,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return { pass: findings.length === 0, findings };
|
|
53
|
+
}
|
|
54
|
+
async function realRoot(dir) {
|
|
55
|
+
try {
|
|
56
|
+
const s = await stat(dir);
|
|
57
|
+
return s.isDirectory() ? dir : null;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function buildRegex(tokens) {
|
|
64
|
+
const escaped = tokens.map(escapeRegex).join("|");
|
|
65
|
+
return new RegExp(`(?:${escaped})`, "g");
|
|
66
|
+
}
|
|
67
|
+
function escapeRegex(s) {
|
|
68
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
69
|
+
}
|
|
70
|
+
async function* walk(dir) {
|
|
71
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
72
|
+
for (const entry of entries) {
|
|
73
|
+
if (SKIP_SEGMENTS.has(entry.name))
|
|
74
|
+
continue;
|
|
75
|
+
const full = join(dir, entry.name);
|
|
76
|
+
if (entry.isDirectory()) {
|
|
77
|
+
yield* walk(full);
|
|
78
|
+
}
|
|
79
|
+
else if (entry.isFile() && isTextFile(full)) {
|
|
80
|
+
yield full;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function isTextFile(path) {
|
|
85
|
+
const base = basename(path);
|
|
86
|
+
if (TEXT_BASENAMES.has(base))
|
|
87
|
+
return true;
|
|
88
|
+
return TEXT_EXTS.has(extname(path));
|
|
89
|
+
}
|
|
90
|
+
async function safeRead(path) {
|
|
91
|
+
try {
|
|
92
|
+
return await readFile(path, "utf8");
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=layer1.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layer1.js","sourceRoot":"","sources":["../../src/validation/layer1.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type Layer2Platform = "rails" | "ios" | "android";
|
|
2
|
+
export type Layer2Mode = "fast" | "build";
|
|
3
|
+
export type Layer2Input = {
|
|
4
|
+
platform: Layer2Platform;
|
|
5
|
+
outDir: string;
|
|
6
|
+
mode?: Layer2Mode;
|
|
7
|
+
timeoutMs?: number;
|
|
8
|
+
};
|
|
9
|
+
export type Layer2Result = {
|
|
10
|
+
pass: boolean;
|
|
11
|
+
command: string;
|
|
12
|
+
exitCode: number | null;
|
|
13
|
+
durationMs: number;
|
|
14
|
+
stderrTail?: string;
|
|
15
|
+
};
|
|
16
|
+
export declare function runLayer2(input: Layer2Input): Promise<Layer2Result>;
|
|
17
|
+
//# sourceMappingURL=layer2.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layer2.d.ts","sourceRoot":"","sources":["../../src/validation/layer2.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,KAAK,GAAG,SAAS,CAAC;AAEzD,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC;AAE1C,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,EAAE,cAAc,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAOF,wBAAsB,SAAS,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAoBzE"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { access, readdir } from "node:fs/promises";
|
|
3
|
+
import { scrubbedEnv } from "../env.js";
|
|
4
|
+
const DEFAULT_TIMEOUT_FAST_MS = 300_000;
|
|
5
|
+
const DEFAULT_TIMEOUT_BUILD_MS = 900_000;
|
|
6
|
+
const IOS_DESTINATION = "platform=iOS Simulator,name=iPhone 17,OS=26.2";
|
|
7
|
+
export async function runLayer2(input) {
|
|
8
|
+
const mode = input.mode ?? "fast";
|
|
9
|
+
const timeoutMs = input.timeoutMs ?? (mode === "build" ? DEFAULT_TIMEOUT_BUILD_MS : DEFAULT_TIMEOUT_FAST_MS);
|
|
10
|
+
const start = Date.now();
|
|
11
|
+
if (!(await exists(input.outDir))) {
|
|
12
|
+
return {
|
|
13
|
+
pass: false,
|
|
14
|
+
command: "stat",
|
|
15
|
+
exitCode: null,
|
|
16
|
+
durationMs: Date.now() - start,
|
|
17
|
+
stderrTail: `outDir does not exist: ${input.outDir}`,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
switch (input.platform) {
|
|
21
|
+
case "rails": return runRailsLayer2(input.outDir, mode, timeoutMs, start);
|
|
22
|
+
case "ios": return runIosLayer2(input.outDir, mode, timeoutMs, start);
|
|
23
|
+
case "android": return runAndroidLayer2(input.outDir, mode, timeoutMs, start);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async function runRailsLayer2(outDir, mode, timeoutMs, start) {
|
|
27
|
+
const useMise = await commandAvailable("mise");
|
|
28
|
+
const stream = mode === "build";
|
|
29
|
+
const bundleCheck = await runIn(outDir, ["bundle", "check"], timeoutMs, useMise, false);
|
|
30
|
+
if (bundleCheck.exitCode !== 0) {
|
|
31
|
+
const bundleInstall = await runIn(outDir, ["bundle", "install"], timeoutMs, useMise, stream);
|
|
32
|
+
if (bundleInstall.exitCode !== 0)
|
|
33
|
+
return failed("bundle install", bundleInstall, start, useMise);
|
|
34
|
+
}
|
|
35
|
+
if (mode === "fast") {
|
|
36
|
+
const routes = await runIn(outDir, ["bin/rails", "routes"], timeoutMs, useMise, false);
|
|
37
|
+
if (routes.exitCode !== 0)
|
|
38
|
+
return failed("bin/rails routes", routes, start, useMise);
|
|
39
|
+
return pass("bin/rails routes", start, useMise);
|
|
40
|
+
}
|
|
41
|
+
// Build mode proves "does the generated app boot" without the
|
|
42
|
+
// test-runner overhead. db:prepare ensures schema is applied;
|
|
43
|
+
// `rails runner` loads the full app (class graph + initializers +
|
|
44
|
+
// DB connection). Running `bin/rails test` here is too heavy —
|
|
45
|
+
// parallel test workers + DB pool + spec-specific test state
|
|
46
|
+
// can hang or take minutes, which isn't what Layer 2 is for.
|
|
47
|
+
const dbPrepare = await runIn(outDir, ["bin/rails", "db:prepare"], timeoutMs, useMise, stream);
|
|
48
|
+
if (dbPrepare.exitCode !== 0)
|
|
49
|
+
return failed("bin/rails db:prepare", dbPrepare, start, useMise);
|
|
50
|
+
const runner = await runIn(outDir, ["bin/rails", "runner", "puts 'OK'"], timeoutMs, useMise, stream);
|
|
51
|
+
if (runner.exitCode !== 0)
|
|
52
|
+
return failed("bin/rails runner", runner, start, useMise);
|
|
53
|
+
return pass("bin/rails runner 'puts OK'", start, useMise);
|
|
54
|
+
}
|
|
55
|
+
async function runIosLayer2(outDir, mode, timeoutMs, start) {
|
|
56
|
+
const xcodeproj = (await readdir(outDir)).find((e) => e.endsWith(".xcodeproj"));
|
|
57
|
+
if (!xcodeproj) {
|
|
58
|
+
return {
|
|
59
|
+
pass: false,
|
|
60
|
+
command: "find .xcodeproj",
|
|
61
|
+
exitCode: null,
|
|
62
|
+
durationMs: Date.now() - start,
|
|
63
|
+
stderrTail: `no .xcodeproj in ${outDir}`,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
if (mode === "fast") {
|
|
67
|
+
const list = await runIn(outDir, ["xcodebuild", "-list", "-project", xcodeproj], timeoutMs, false, false);
|
|
68
|
+
if (list.exitCode !== 0)
|
|
69
|
+
return failed(`xcodebuild -list -project ${xcodeproj}`, list, start, false);
|
|
70
|
+
return { pass: true, command: `xcodebuild -list -project ${xcodeproj}`, exitCode: 0, durationMs: Date.now() - start };
|
|
71
|
+
}
|
|
72
|
+
const scheme = xcodeproj.replace(/\.xcodeproj$/, "");
|
|
73
|
+
const args = [
|
|
74
|
+
"xcodebuild",
|
|
75
|
+
"-project", xcodeproj,
|
|
76
|
+
"-scheme", scheme,
|
|
77
|
+
"-destination", IOS_DESTINATION,
|
|
78
|
+
"-configuration", "Debug",
|
|
79
|
+
"build",
|
|
80
|
+
];
|
|
81
|
+
const build = await runIn(outDir, args, timeoutMs, false, true);
|
|
82
|
+
if (build.exitCode !== 0)
|
|
83
|
+
return failed(`xcodebuild build -scheme ${scheme}`, build, start, false);
|
|
84
|
+
return { pass: true, command: `xcodebuild build -scheme ${scheme}`, exitCode: 0, durationMs: Date.now() - start };
|
|
85
|
+
}
|
|
86
|
+
async function runAndroidLayer2(outDir, mode, timeoutMs, start) {
|
|
87
|
+
const wrapper = await exists(`${outDir}/gradlew`);
|
|
88
|
+
if (!wrapper) {
|
|
89
|
+
return {
|
|
90
|
+
pass: false,
|
|
91
|
+
command: "stat ./gradlew",
|
|
92
|
+
exitCode: null,
|
|
93
|
+
durationMs: Date.now() - start,
|
|
94
|
+
stderrTail: `no ./gradlew wrapper in ${outDir}`,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
if (mode === "fast") {
|
|
98
|
+
const version = await runIn(outDir, ["./gradlew", "--version"], timeoutMs, false, false);
|
|
99
|
+
if (version.exitCode !== 0)
|
|
100
|
+
return failed("./gradlew --version", version, start, false);
|
|
101
|
+
return { pass: true, command: "./gradlew --version", exitCode: 0, durationMs: Date.now() - start };
|
|
102
|
+
}
|
|
103
|
+
const assemble = await runIn(outDir, ["./gradlew", "assembleDebug", "--no-daemon"], timeoutMs, false, true);
|
|
104
|
+
if (assemble.exitCode !== 0)
|
|
105
|
+
return failed("./gradlew assembleDebug", assemble, start, false);
|
|
106
|
+
return { pass: true, command: "./gradlew assembleDebug", exitCode: 0, durationMs: Date.now() - start };
|
|
107
|
+
}
|
|
108
|
+
function pass(command, start, useMise) {
|
|
109
|
+
return { pass: true, command: withPrefix(command, useMise), exitCode: 0, durationMs: Date.now() - start };
|
|
110
|
+
}
|
|
111
|
+
function failed(action, result, start, useMise) {
|
|
112
|
+
return {
|
|
113
|
+
pass: false,
|
|
114
|
+
command: withPrefix(action, useMise),
|
|
115
|
+
exitCode: result.exitCode,
|
|
116
|
+
durationMs: Date.now() - start,
|
|
117
|
+
stderrTail: tail(result.stderr, 30),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function withPrefix(action, useMise) {
|
|
121
|
+
return useMise ? `mise exec -- ${action}` : action;
|
|
122
|
+
}
|
|
123
|
+
function runIn(cwd, argv, timeoutMs, useMise, stream) {
|
|
124
|
+
const [command, ...rest] = useMise ? ["mise", "exec", "--", ...argv] : argv;
|
|
125
|
+
return new Promise((resolvePromise) => {
|
|
126
|
+
// Build mode: inherit parent's TTY so Ruby / xcodebuild / gradle
|
|
127
|
+
// detect a terminal and line-buffer stdout normally. We give up
|
|
128
|
+
// programmatic stderr capture in exchange — user sees it live.
|
|
129
|
+
// Fast mode: pipe + drain, silent + fast.
|
|
130
|
+
const child = stream
|
|
131
|
+
? spawn(command, rest, { cwd, stdio: "inherit", env: scrubbedEnv() })
|
|
132
|
+
: spawn(command, rest, { cwd, stdio: ["ignore", "pipe", "pipe"], env: scrubbedEnv() });
|
|
133
|
+
const stderrChunks = [];
|
|
134
|
+
const timer = setTimeout(() => { child.kill("SIGTERM"); }, timeoutMs);
|
|
135
|
+
if (!stream) {
|
|
136
|
+
child.stdout?.on("data", () => { });
|
|
137
|
+
child.stderr?.on("data", (c) => stderrChunks.push(c));
|
|
138
|
+
}
|
|
139
|
+
child.on("close", (code) => {
|
|
140
|
+
clearTimeout(timer);
|
|
141
|
+
resolvePromise({ exitCode: code, stderr: Buffer.concat(stderrChunks).toString("utf8") });
|
|
142
|
+
});
|
|
143
|
+
child.on("error", () => {
|
|
144
|
+
clearTimeout(timer);
|
|
145
|
+
resolvePromise({ exitCode: null, stderr: Buffer.concat(stderrChunks).toString("utf8") });
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
async function commandAvailable(bin) {
|
|
150
|
+
return new Promise((resolvePromise) => {
|
|
151
|
+
const c = spawn("which", [bin], { env: scrubbedEnv() });
|
|
152
|
+
c.on("close", (code) => resolvePromise(code === 0));
|
|
153
|
+
c.on("error", () => resolvePromise(false));
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
async function exists(path) {
|
|
157
|
+
try {
|
|
158
|
+
await access(path);
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function tail(s, lines) {
|
|
166
|
+
return s.split("\n").slice(-lines).join("\n");
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=layer2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layer2.js","sourceRoot":"","sources":["../../src/validation/layer2.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type Layer3Criterion = {
|
|
2
|
+
id: string;
|
|
3
|
+
question: string;
|
|
4
|
+
};
|
|
5
|
+
export type Layer3Input = {
|
|
6
|
+
screenshotPath: string;
|
|
7
|
+
rubric: readonly Layer3Criterion[];
|
|
8
|
+
spec: string;
|
|
9
|
+
model?: string;
|
|
10
|
+
samplesPerCriterion?: number;
|
|
11
|
+
};
|
|
12
|
+
export type Layer3Score = {
|
|
13
|
+
criterionId: string;
|
|
14
|
+
pass: boolean;
|
|
15
|
+
rationale: string;
|
|
16
|
+
};
|
|
17
|
+
export type Layer3Result = {
|
|
18
|
+
pass: boolean;
|
|
19
|
+
scores: Layer3Score[];
|
|
20
|
+
};
|
|
21
|
+
export declare function runLayer3(input: Layer3Input): Promise<Layer3Result>;
|
|
22
|
+
//# sourceMappingURL=layer3.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layer3.d.ts","sourceRoot":"","sources":["../../src/validation/layer3.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,SAAS,eAAe,EAAE,CAAC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB,CAAC;AA8BF,wBAAsB,SAAS,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAgCzE"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { isStub } from "../stub.js";
|
|
4
|
+
const DEFAULT_MODEL = "claude-opus-4-7";
|
|
5
|
+
const DEFAULT_SAMPLES = 3;
|
|
6
|
+
const MAX_TOKENS = 1024;
|
|
7
|
+
const SYSTEM_PROMPT = `You are a vision judge for a SaaS code generator. You receive a one-sentence natural-language spec describing a SaaS product and a screenshot of a generated app's home screen, and you assess one rubric criterion at a time.
|
|
8
|
+
|
|
9
|
+
Score conservatively: when in doubt, answer No. Base the verdict ONLY on what is visible in the screenshot — do not assume features that aren't shown.`;
|
|
10
|
+
const VERDICT_TOOL = {
|
|
11
|
+
name: "submit_verdict",
|
|
12
|
+
description: "Record the Yes/No verdict and one-sentence rationale for this rubric criterion.",
|
|
13
|
+
input_schema: {
|
|
14
|
+
type: "object",
|
|
15
|
+
properties: {
|
|
16
|
+
pass: {
|
|
17
|
+
type: "boolean",
|
|
18
|
+
description: "true if the criterion is met based on what is visible in the screenshot; false otherwise. Be conservative — when uncertain, answer false.",
|
|
19
|
+
},
|
|
20
|
+
rationale: {
|
|
21
|
+
type: "string",
|
|
22
|
+
description: "One-sentence rationale grounded in what is visible.",
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
required: ["pass", "rationale"],
|
|
26
|
+
additionalProperties: false,
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
export async function runLayer3(input) {
|
|
30
|
+
if (isStub("judge"))
|
|
31
|
+
return runStubLayer3(input);
|
|
32
|
+
const samplesPerCriterion = input.samplesPerCriterion ?? DEFAULT_SAMPLES;
|
|
33
|
+
const model = input.model ?? DEFAULT_MODEL;
|
|
34
|
+
const png = await readFile(input.screenshotPath);
|
|
35
|
+
const imageBase64 = png.toString("base64");
|
|
36
|
+
const apiKey = process.env['NATIVEAPPTEMPLATE_AGENT_ANTHROPIC_KEY'] ?? process.env['ANTHROPIC_API_KEY'];
|
|
37
|
+
const client = new Anthropic({ apiKey });
|
|
38
|
+
const scores = await Promise.all(input.rubric.map(async (criterion) => {
|
|
39
|
+
const samples = await Promise.all(Array.from({ length: samplesPerCriterion }, () => judgeOne(client, model, imageBase64, input.spec, criterion)));
|
|
40
|
+
const passCount = samples.filter((s) => s.pass).length;
|
|
41
|
+
const median = passCount > samplesPerCriterion / 2;
|
|
42
|
+
const winningSample = samples.find((s) => s.pass === median) ?? samples[0];
|
|
43
|
+
return {
|
|
44
|
+
criterionId: criterion.id,
|
|
45
|
+
pass: median,
|
|
46
|
+
rationale: winningSample.rationale,
|
|
47
|
+
};
|
|
48
|
+
}));
|
|
49
|
+
const pass = scores.every((s) => s.pass);
|
|
50
|
+
return { pass, scores };
|
|
51
|
+
}
|
|
52
|
+
async function judgeOne(client, model, imageBase64, spec, criterion) {
|
|
53
|
+
const response = await client.messages.create({
|
|
54
|
+
model,
|
|
55
|
+
max_tokens: MAX_TOKENS,
|
|
56
|
+
system: SYSTEM_PROMPT,
|
|
57
|
+
tools: [VERDICT_TOOL],
|
|
58
|
+
tool_choice: { type: "tool", name: "submit_verdict" },
|
|
59
|
+
messages: [
|
|
60
|
+
{
|
|
61
|
+
role: "user",
|
|
62
|
+
content: [
|
|
63
|
+
{
|
|
64
|
+
type: "image",
|
|
65
|
+
source: { type: "base64", media_type: "image/png", data: imageBase64 },
|
|
66
|
+
cache_control: { type: "ephemeral" },
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
type: "text",
|
|
70
|
+
text: `Spec: ${spec}\n\nCriterion: ${criterion.question}`,
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
});
|
|
76
|
+
const toolUse = response.content.find((c) => c.type === "tool_use");
|
|
77
|
+
if (!toolUse || toolUse.type !== "tool_use") {
|
|
78
|
+
throw new Error("layer3 judge: expected tool_use in response, got: " + JSON.stringify(response.content));
|
|
79
|
+
}
|
|
80
|
+
const verdict = toolUse.input;
|
|
81
|
+
if (typeof verdict.pass !== "boolean" || typeof verdict.rationale !== "string") {
|
|
82
|
+
throw new Error("layer3 judge: malformed verdict: " + JSON.stringify(verdict));
|
|
83
|
+
}
|
|
84
|
+
return verdict;
|
|
85
|
+
}
|
|
86
|
+
async function runStubLayer3(input) {
|
|
87
|
+
return {
|
|
88
|
+
pass: true,
|
|
89
|
+
scores: input.rubric.map((c) => ({
|
|
90
|
+
criterionId: c.id,
|
|
91
|
+
pass: true,
|
|
92
|
+
rationale: "(stub)",
|
|
93
|
+
})),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=layer3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layer3.js","sourceRoot":"","sources":["../../src/validation/layer3.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type VisualJudgeResult } from "./visual-judge.js";
|
|
2
|
+
import type { Layer3Criterion } from "./layer3.js";
|
|
3
|
+
export type Stage1VisualInput = {
|
|
4
|
+
iosDir?: string;
|
|
5
|
+
androidDir?: string;
|
|
6
|
+
screenshotDir?: string;
|
|
7
|
+
spec: string;
|
|
8
|
+
rubric?: readonly Layer3Criterion[];
|
|
9
|
+
renderWaitMs?: number;
|
|
10
|
+
samplesPerCriterion?: number;
|
|
11
|
+
};
|
|
12
|
+
export type Stage1VisualResult = {
|
|
13
|
+
ios?: VisualJudgeResult;
|
|
14
|
+
android?: VisualJudgeResult;
|
|
15
|
+
};
|
|
16
|
+
export declare function runStage1Visual(input: Stage1VisualInput): Promise<Stage1VisualResult>;
|
|
17
|
+
//# sourceMappingURL=stage1.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stage1.d.ts","sourceRoot":"","sources":["../../src/validation/stage1.ts"],"names":[],"mappings":"AAGA,OAAO,EAAyC,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAClG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,MAAM,MAAM,iBAAiB,GAAG;IAE9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACpC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,CAAC,EAAE,iBAAiB,CAAC;IACxB,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC7B,CAAC;AAaF,wBAAsB,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA4C3F"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { mkdir } from "node:fs/promises";
|
|
3
|
+
import { discoverIosArtifact, discoverAndroidArtifact } from "./discover.js";
|
|
4
|
+
import { runVisualJudge, DEFAULT_STAGE1_RUBRIC } from "./visual-judge.js";
|
|
5
|
+
// One-call convenience wrapper for the Stage 1 visual judge: discover the
|
|
6
|
+
// pre-built artifact + identifier on each requested platform (#42), then run
|
|
7
|
+
// the install → launch → capture → judge chain (#40, #41) for that platform.
|
|
8
|
+
//
|
|
9
|
+
// Returns a Stage1VisualResult shaped to match JudgeInput.visual's per-
|
|
10
|
+
// platform expectation, so callers can pass it through to runJudge directly.
|
|
11
|
+
//
|
|
12
|
+
// Caller responsibilities:
|
|
13
|
+
// - Run Layer 2 in build mode first so the .app / .apk exists.
|
|
14
|
+
// - Ensure a sim/emulator is booted for each platform being judged.
|
|
15
|
+
// - Pick which platforms to judge — pass undefined for the others.
|
|
16
|
+
export async function runStage1Visual(input) {
|
|
17
|
+
const screenshotDir = input.screenshotDir ?? join(process.cwd(), "tmp", "screenshots");
|
|
18
|
+
await mkdir(screenshotDir, { recursive: true });
|
|
19
|
+
const rubric = input.rubric ?? DEFAULT_STAGE1_RUBRIC;
|
|
20
|
+
const result = {};
|
|
21
|
+
if (input.iosDir) {
|
|
22
|
+
const ios = await discoverIosArtifact(input.iosDir);
|
|
23
|
+
if (ios) {
|
|
24
|
+
result.ios = await runVisualJudge({
|
|
25
|
+
platform: "ios",
|
|
26
|
+
artifactPath: ios.appPath,
|
|
27
|
+
bundleId: ios.bundleId,
|
|
28
|
+
screenshotPath: join(screenshotDir, "ios-home.png"),
|
|
29
|
+
spec: input.spec,
|
|
30
|
+
rubric,
|
|
31
|
+
...(input.renderWaitMs !== undefined ? { renderWaitMs: input.renderWaitMs } : {}),
|
|
32
|
+
...(input.samplesPerCriterion !== undefined ? { samplesPerCriterion: input.samplesPerCriterion } : {}),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
result.ios = stubFailure("ios", "iOS artifact not discovered (run Layer 2 build mode first)");
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (input.androidDir) {
|
|
40
|
+
const android = await discoverAndroidArtifact(input.androidDir);
|
|
41
|
+
if (android) {
|
|
42
|
+
result.android = await runVisualJudge({
|
|
43
|
+
platform: "android",
|
|
44
|
+
artifactPath: android.apkPath,
|
|
45
|
+
packageName: android.packageName,
|
|
46
|
+
screenshotPath: join(screenshotDir, "android-home.png"),
|
|
47
|
+
spec: input.spec,
|
|
48
|
+
rubric,
|
|
49
|
+
...(input.renderWaitMs !== undefined ? { renderWaitMs: input.renderWaitMs } : {}),
|
|
50
|
+
...(input.samplesPerCriterion !== undefined ? { samplesPerCriterion: input.samplesPerCriterion } : {}),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
result.android = stubFailure("android", "Android artifact not discovered (run Layer 2 build mode first)");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
function stubFailure(platform, error) {
|
|
60
|
+
return {
|
|
61
|
+
ok: false,
|
|
62
|
+
launch: {
|
|
63
|
+
ok: false,
|
|
64
|
+
command: `discover ${platform} artifact`,
|
|
65
|
+
durationMs: 0,
|
|
66
|
+
error,
|
|
67
|
+
},
|
|
68
|
+
error,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=stage1.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stage1.js","sourceRoot":"","sources":["../../src/validation/stage1.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type Layer3Criterion, type Layer3Result } from "./layer3.js";
|
|
2
|
+
import { type CapturePlatform } from "./capture.js";
|
|
3
|
+
import { type LaunchResult } from "./launch.js";
|
|
4
|
+
export type VisualJudgeInput = {
|
|
5
|
+
platform: CapturePlatform;
|
|
6
|
+
artifactPath: string;
|
|
7
|
+
bundleId?: string;
|
|
8
|
+
packageName?: string;
|
|
9
|
+
screenshotPath: string;
|
|
10
|
+
spec: string;
|
|
11
|
+
rubric: readonly Layer3Criterion[];
|
|
12
|
+
renderWaitMs?: number;
|
|
13
|
+
samplesPerCriterion?: number;
|
|
14
|
+
model?: string;
|
|
15
|
+
};
|
|
16
|
+
export type VisualJudgeResult = {
|
|
17
|
+
ok: boolean;
|
|
18
|
+
launch: LaunchResult;
|
|
19
|
+
screenshotPath?: string;
|
|
20
|
+
layer3?: Layer3Result;
|
|
21
|
+
error?: string;
|
|
22
|
+
};
|
|
23
|
+
export declare function runVisualJudge(input: VisualJudgeInput): Promise<VisualJudgeResult>;
|
|
24
|
+
export declare const DEFAULT_STAGE1_RUBRIC: readonly Layer3Criterion[];
|
|
25
|
+
//# sourceMappingURL=visual-judge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visual-judge.d.ts","sourceRoot":"","sources":["../../src/validation/visual-judge.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,KAAK,eAAe,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AACjF,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,EAAoB,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAElE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,eAAe,CAAC;IAE1B,YAAY,EAAE,MAAM,CAAC;IAErB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,eAAe,EAAE,CAAC;IAGnC,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,YAAY,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAcF,wBAAsB,cAAc,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAkDxF;AAeD,eAAO,MAAM,qBAAqB,EAAE,SAAS,eAAe,EAW3D,CAAC"}
|