@iamu-dev/iamu 0.2.0-beta.4 → 0.2.0-beta.6
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/dist/bin.js +238 -23
- package/dist/commands/start-run.d.ts +52 -0
- package/dist/index.js +238 -23
- package/dist/templates/skills/iamu-findings.md +27 -29
- package/dist/templates/skills/iamu-new.md +42 -43
- package/dist/templates/skills/iamu-propose.md +34 -18
- package/dist/templates/skills/iamu-run.md +99 -43
- package/dist/templates/skills/iamu-status.md +21 -14
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -199,17 +199,24 @@ function defaultConfig(params) {
|
|
|
199
199
|
cloud_api_url: null
|
|
200
200
|
};
|
|
201
201
|
}
|
|
202
|
-
var baseProjectFields, localOnlyConfig, cloudLinkedConfig, projectConfigSchema;
|
|
202
|
+
var modelTierSchema, projectModelsConfigSchema, baseProjectFields, localOnlyConfig, cloudLinkedConfig, projectConfigSchema;
|
|
203
203
|
var init_config = __esm({
|
|
204
204
|
"../../packages/protocol/dist/schemas/frontmatter/config.js"() {
|
|
205
205
|
"use strict";
|
|
206
206
|
init_common();
|
|
207
207
|
init_version();
|
|
208
|
+
modelTierSchema = z4.string().min(1).max(128).describe("Model role tier ('fast', 'thorough', 'inherit') or a specific model identifier.");
|
|
209
|
+
projectModelsConfigSchema = z4.object({
|
|
210
|
+
walker: modelTierSchema.optional(),
|
|
211
|
+
matcher: modelTierSchema.optional(),
|
|
212
|
+
judge: modelTierSchema.optional()
|
|
213
|
+
}).strict();
|
|
208
214
|
baseProjectFields = {
|
|
209
215
|
schema_version: z4.literal(SCHEMA_VERSION),
|
|
210
216
|
id: uuidSchema,
|
|
211
217
|
project_slug: slugSchema,
|
|
212
|
-
project_name: z4.string().min(1)
|
|
218
|
+
project_name: z4.string().min(1),
|
|
219
|
+
models: projectModelsConfigSchema.optional()
|
|
213
220
|
};
|
|
214
221
|
localOnlyConfig = z4.object({
|
|
215
222
|
...baseProjectFields,
|
|
@@ -1153,7 +1160,21 @@ var init_walkthrough_bundle = __esm({
|
|
|
1153
1160
|
judge_prompt: z19.string().optional(),
|
|
1154
1161
|
judge_model: z19.string().optional()
|
|
1155
1162
|
}).optional()
|
|
1156
|
-
}).optional()
|
|
1163
|
+
}).optional(),
|
|
1164
|
+
models: z19.object({
|
|
1165
|
+
walker: z19.string().optional(),
|
|
1166
|
+
matcher: z19.string().optional(),
|
|
1167
|
+
judge: z19.string().optional(),
|
|
1168
|
+
walker_reported: z19.string().optional(),
|
|
1169
|
+
matcher_reported: z19.string().optional(),
|
|
1170
|
+
judge_reported: z19.string().optional(),
|
|
1171
|
+
walker_tier: z19.string().optional(),
|
|
1172
|
+
matcher_tier: z19.string().optional(),
|
|
1173
|
+
judge_tier: z19.string().optional(),
|
|
1174
|
+
execution_mode: z19.enum(["multi_agent", "single_agent"]).optional(),
|
|
1175
|
+
velocity_sec_per_step: z19.number().optional(),
|
|
1176
|
+
total_duration_sec: z19.number().optional()
|
|
1177
|
+
}).optional().describe("AI model configuration and reported runtime provenance for this walkthrough.")
|
|
1157
1178
|
});
|
|
1158
1179
|
walkthroughBundleSchema = z19.object({
|
|
1159
1180
|
manifest: bundleManifestSchema,
|
|
@@ -1376,6 +1397,7 @@ var init_walkthrough_canonical = __esm({
|
|
|
1376
1397
|
build: buildMetadataSchema.optional(),
|
|
1377
1398
|
viewport: runViewportSchema.optional(),
|
|
1378
1399
|
environment: z20.string().optional(),
|
|
1400
|
+
models: z20.record(z20.unknown()).optional(),
|
|
1379
1401
|
findings: z20.array(canonicalFindingSchema),
|
|
1380
1402
|
steps: z20.array(canonicalStepSchema),
|
|
1381
1403
|
human_tests: z20.array(canonicalHumanTestSchema),
|
|
@@ -7247,6 +7269,7 @@ async function buildCanonical(projectRoot, manifest, completedAt, inputs) {
|
|
|
7247
7269
|
...manifest.build ? { build: manifest.build } : {},
|
|
7248
7270
|
...manifest.viewport ? { viewport: manifest.viewport } : {},
|
|
7249
7271
|
...manifest.environment ? { environment: manifest.environment } : {},
|
|
7272
|
+
...manifest.models ? { models: manifest.models } : {},
|
|
7250
7273
|
findings: canonicalFindings,
|
|
7251
7274
|
steps: canonicalSteps,
|
|
7252
7275
|
human_tests: humanTests,
|
|
@@ -7407,6 +7430,20 @@ function renderCanonicalMarkdown(canonical) {
|
|
|
7407
7430
|
lines.push(`- Viewport: ${canonical.viewport.device} (${canonical.viewport.width}\xD7${canonical.viewport.height})`);
|
|
7408
7431
|
}
|
|
7409
7432
|
lines.push(`- Started: ${canonical.provenance.started_at}`, `- Completed: ${canonical.provenance.completed_at}`, `- Walker prompt: ${canonical.provenance.walker_prompt.id}@${canonical.provenance.walker_prompt.version}`, `- Findings: ${canonical.findings.length}`, `- Steps: ${canonical.steps.length}`);
|
|
7433
|
+
const models = canonical.models;
|
|
7434
|
+
if (models) {
|
|
7435
|
+
const parts = [
|
|
7436
|
+
models.walker ? `Walker: ${models.walker}${models.walker_reported ? ` (${models.walker_reported})` : ""}` : "",
|
|
7437
|
+
models.judge ? `Judge: ${models.judge}${models.judge_reported ? ` (${models.judge_reported})` : ""}` : "",
|
|
7438
|
+
models.matcher ? `Matcher: ${models.matcher}` : ""
|
|
7439
|
+
].filter(Boolean);
|
|
7440
|
+
if (parts.length > 0) {
|
|
7441
|
+
lines.push(`- Models: ${parts.join(" \xB7 ")}`);
|
|
7442
|
+
}
|
|
7443
|
+
if (typeof models.velocity_sec_per_step === "number") {
|
|
7444
|
+
lines.push(`- Velocity: ~${models.velocity_sec_per_step}s/step (${models.total_duration_sec}s total)`);
|
|
7445
|
+
}
|
|
7446
|
+
}
|
|
7410
7447
|
const what = canonical.what_worked;
|
|
7411
7448
|
if (what && what.length > 0) {
|
|
7412
7449
|
lines.push("", "## What worked");
|
|
@@ -7776,6 +7813,7 @@ async function assembleWalkthroughBundle(projectRoot, runId) {
|
|
|
7776
7813
|
report_markdown: report3,
|
|
7777
7814
|
verdict: verdictArray,
|
|
7778
7815
|
judge_meta: judgeMeta,
|
|
7816
|
+
...manifest.models ? { models: manifest.models } : {},
|
|
7779
7817
|
...buildFields(manifest),
|
|
7780
7818
|
...await readCanonicalExtras(projectRoot, runId)
|
|
7781
7819
|
},
|
|
@@ -14413,6 +14451,18 @@ async function startRun(scenarioSlug, opts) {
|
|
|
14413
14451
|
}
|
|
14414
14452
|
const build = detectBuild({ cwd: projectRoot, label: opts.build });
|
|
14415
14453
|
const environment = resolveEnvironment(opts.env, start2.url);
|
|
14454
|
+
const cfg = await readConfig(projectRoot).catch(() => null);
|
|
14455
|
+
const walker = opts.walker ?? process.env.IAMU_WALKER_MODEL ?? cfg?.models?.walker ?? "fast";
|
|
14456
|
+
const judge = opts.judge ?? process.env.IAMU_JUDGE_MODEL ?? cfg?.models?.judge ?? "thorough";
|
|
14457
|
+
const matcher = opts.matcher ?? process.env.IAMU_MATCHER_MODEL ?? cfg?.models?.matcher ?? "thorough";
|
|
14458
|
+
const models = {
|
|
14459
|
+
walker,
|
|
14460
|
+
judge,
|
|
14461
|
+
matcher,
|
|
14462
|
+
walker_tier: ["fast", "thorough", "inherit"].includes(walker) ? walker : "custom",
|
|
14463
|
+
judge_tier: ["fast", "thorough", "inherit"].includes(judge) ? judge : "custom",
|
|
14464
|
+
matcher_tier: ["fast", "thorough", "inherit"].includes(matcher) ? matcher : "custom"
|
|
14465
|
+
};
|
|
14416
14466
|
let revisit = [];
|
|
14417
14467
|
try {
|
|
14418
14468
|
revisit = await computeRevisitPlaces(projectRoot, scenarioSlug, start2.url);
|
|
@@ -14430,6 +14480,7 @@ async function startRun(scenarioSlug, opts) {
|
|
|
14430
14480
|
status: "running",
|
|
14431
14481
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14432
14482
|
completed_at: null,
|
|
14483
|
+
models,
|
|
14433
14484
|
...build ? { build } : {},
|
|
14434
14485
|
...environment ? { environment } : {},
|
|
14435
14486
|
...revisit.length > 0 ? { revisit } : {},
|
|
@@ -14451,6 +14502,7 @@ async function startRun(scenarioSlug, opts) {
|
|
|
14451
14502
|
...targetWarning ? { target_warning: targetWarning } : {},
|
|
14452
14503
|
persona_slug: fm.persona,
|
|
14453
14504
|
outline_slug: fm.outline,
|
|
14505
|
+
models,
|
|
14454
14506
|
build: build ?? null,
|
|
14455
14507
|
environment: environment ?? null,
|
|
14456
14508
|
revisit,
|
|
@@ -14469,6 +14521,9 @@ async function startRun(scenarioSlug, opts) {
|
|
|
14469
14521
|
console.log(` ${chalk11.dim("persona:")} ${fm.persona}`);
|
|
14470
14522
|
console.log(` ${chalk11.dim("outline:")} ${fm.outline}`);
|
|
14471
14523
|
console.log(` ${chalk11.dim("target:")} ${start2.url}`);
|
|
14524
|
+
console.log(
|
|
14525
|
+
` ${chalk11.dim("models:")} walker: ${chalk11.cyan(walker)} \xB7 judge: ${chalk11.cyan(judge)} \xB7 matcher: ${chalk11.cyan(matcher)}`
|
|
14526
|
+
);
|
|
14472
14527
|
if (targetWarning) {
|
|
14473
14528
|
console.log(` ${symbols.warning} ${chalk11.yellow(targetWarning)}`);
|
|
14474
14529
|
}
|
|
@@ -16652,6 +16707,7 @@ __export(browse_exports, {
|
|
|
16652
16707
|
readBrowseState: () => readBrowseState,
|
|
16653
16708
|
shouldDisableSandbox: () => shouldDisableSandbox,
|
|
16654
16709
|
start: () => start,
|
|
16710
|
+
waitForVisualStability: () => waitForVisualStability,
|
|
16655
16711
|
withPage: () => withPage
|
|
16656
16712
|
});
|
|
16657
16713
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
@@ -16858,6 +16914,45 @@ async function stop(opts, driver) {
|
|
|
16858
16914
|
}
|
|
16859
16915
|
report2(opts, { stopped: true }, [` ${chalk20.green("\u2713")} Browser stopped`]);
|
|
16860
16916
|
}
|
|
16917
|
+
async function waitForVisualStability(page, maxWaitMs = 300) {
|
|
16918
|
+
let timer;
|
|
16919
|
+
try {
|
|
16920
|
+
const nodeTimeout = new Promise((resolve3) => {
|
|
16921
|
+
timer = setTimeout(resolve3, maxWaitMs + 100);
|
|
16922
|
+
});
|
|
16923
|
+
const browserWait = page.evaluate((maxWait) => {
|
|
16924
|
+
return new Promise((resolve3) => {
|
|
16925
|
+
const timeout = setTimeout(resolve3, maxWait);
|
|
16926
|
+
const done = () => {
|
|
16927
|
+
clearTimeout(timeout);
|
|
16928
|
+
resolve3();
|
|
16929
|
+
};
|
|
16930
|
+
const g = globalThis;
|
|
16931
|
+
if (typeof g.requestAnimationFrame !== "function") {
|
|
16932
|
+
done();
|
|
16933
|
+
return;
|
|
16934
|
+
}
|
|
16935
|
+
g.requestAnimationFrame(() => {
|
|
16936
|
+
g.requestAnimationFrame?.(() => {
|
|
16937
|
+
const animations = g.document?.getAnimations ? g.document.getAnimations().filter((a) => a.playState === "running" && a.finished) : [];
|
|
16938
|
+
if (animations.length === 0) {
|
|
16939
|
+
done();
|
|
16940
|
+
return;
|
|
16941
|
+
}
|
|
16942
|
+
Promise.allSettled(
|
|
16943
|
+
animations.map((a) => a.finished)
|
|
16944
|
+
).finally(done);
|
|
16945
|
+
});
|
|
16946
|
+
});
|
|
16947
|
+
});
|
|
16948
|
+
}, maxWaitMs).catch(() => {
|
|
16949
|
+
});
|
|
16950
|
+
await Promise.race([browserWait, nodeTimeout]);
|
|
16951
|
+
} catch {
|
|
16952
|
+
} finally {
|
|
16953
|
+
if (timer) clearTimeout(timer);
|
|
16954
|
+
}
|
|
16955
|
+
}
|
|
16861
16956
|
async function withPage(opts, driver, fn, target) {
|
|
16862
16957
|
const state = readBrowseState();
|
|
16863
16958
|
if (!state) {
|
|
@@ -16877,9 +16972,9 @@ async function withPage(opts, driver, fn, target) {
|
|
|
16877
16972
|
await fn(page);
|
|
16878
16973
|
await page.waitForLoadState("domcontentloaded").catch(() => {
|
|
16879
16974
|
});
|
|
16880
|
-
await page.waitForTimeout(400);
|
|
16881
16975
|
let screenshot = null;
|
|
16882
16976
|
if (opts.shot) {
|
|
16977
|
+
await waitForVisualStability(page);
|
|
16883
16978
|
screenshot = path22.resolve(opts.shot);
|
|
16884
16979
|
mkdirSync(path22.dirname(screenshot), { recursive: true });
|
|
16885
16980
|
await page.screenshot({ path: screenshot, fullPage: Boolean(opts.full) });
|
|
@@ -16980,6 +17075,35 @@ function printStep(opts, record, projectRoot) {
|
|
|
16980
17075
|
}
|
|
16981
17076
|
}
|
|
16982
17077
|
async function browseStep(args, opts, driver) {
|
|
17078
|
+
if (opts.stepFile) {
|
|
17079
|
+
try {
|
|
17080
|
+
let raw;
|
|
17081
|
+
if (opts.stepFile === "-") {
|
|
17082
|
+
raw = readFileSync6(0, "utf8");
|
|
17083
|
+
} else {
|
|
17084
|
+
raw = readFileSync6(opts.stepFile, "utf8");
|
|
17085
|
+
}
|
|
17086
|
+
const data = JSON.parse(raw);
|
|
17087
|
+
if (data.run && typeof data.run === "string") opts.run = data.run;
|
|
17088
|
+
if (data.action && typeof data.action === "string")
|
|
17089
|
+
opts.action = data.action;
|
|
17090
|
+
if (data.note && typeof data.note === "string") opts.note = data.note;
|
|
17091
|
+
if (data.surface && typeof data.surface === "string")
|
|
17092
|
+
opts.surface = data.surface;
|
|
17093
|
+
if (args.length === 0 && data.cmd) {
|
|
17094
|
+
if (Array.isArray(data.cmd)) {
|
|
17095
|
+
args = data.cmd.map(String);
|
|
17096
|
+
} else if (typeof data.cmd === "string") {
|
|
17097
|
+
args = data.cmd.trim().split(/\s+/);
|
|
17098
|
+
}
|
|
17099
|
+
}
|
|
17100
|
+
} catch (err) {
|
|
17101
|
+
return fail(
|
|
17102
|
+
`Failed to read step payload from ${opts.stepFile}: ${err.message}`,
|
|
17103
|
+
opts
|
|
17104
|
+
);
|
|
17105
|
+
}
|
|
17106
|
+
}
|
|
16983
17107
|
if (!opts.run)
|
|
16984
17108
|
return fail("--run <run-id> is required for browse step.", opts);
|
|
16985
17109
|
if (!opts.action) {
|
|
@@ -18837,7 +18961,7 @@ function composeWalkerPrompt(p) {
|
|
|
18837
18961
|
`Screenshots to: ${rd}/screenshots/step-N.png \u2014 absolute paths.`,
|
|
18838
18962
|
`Record findings with: ${iamu} new-finding --scenario ${p.scenarioSlug} --run ${p.runId} --step <N> --title "..." --type <helps|hinders|missing|irrelevant|incorrect> --severity <critical|major|minor|observation> --ui-fact "..." --consequence "..." --task-impact <blocks|impairs|slows|neutral|accelerates|enables> --confidence <high|medium|low> --json \u2014 then add the body (description + "## Recommendation") below the frontmatter of the file it returns.`,
|
|
18839
18963
|
"`--step <N>` is the step whose screenshot shows the problem (or strength): it is the evidence people see next to the finding, so record that step and its screenshot first, and pick the screenshot where the thing is actually visible.",
|
|
18840
|
-
`Before you finish, write ${rd}/summary.json: {"what_worked": [...], "open_questions": [...]} \u2014 one sentence each, in the persona's language.`,
|
|
18964
|
+
`Before you finish, write ${rd}/summary.json: {"what_worked": [...], "open_questions": [...], "model": "<your model identifier if known, e.g. claude-3-5-haiku, gemini-3.8-flash, or unspecified>"} \u2014 one sentence each, in the persona's language.`,
|
|
18841
18965
|
"",
|
|
18842
18966
|
"Do not read the product's source code, other iamu/ entity files, or earlier runs: you know only what the persona knows.",
|
|
18843
18967
|
"",
|
|
@@ -19436,8 +19560,21 @@ async function evaluate(runId, opts) {
|
|
|
19436
19560
|
)
|
|
19437
19561
|
};
|
|
19438
19562
|
const prompt = composeJudgePrompt(input);
|
|
19563
|
+
const promptFilePath = opts?.promptFile ?? path25.join(runDirPath, "judge_prompt.md");
|
|
19564
|
+
await fs25.writeFile(promptFilePath, prompt, "utf8");
|
|
19439
19565
|
if (opts?.json) {
|
|
19440
|
-
console.log(
|
|
19566
|
+
console.log(
|
|
19567
|
+
JSON.stringify(
|
|
19568
|
+
{
|
|
19569
|
+
...input,
|
|
19570
|
+
prompt_file: path25.relative(projectRoot, promptFilePath),
|
|
19571
|
+
prompt_file_abs: path25.resolve(promptFilePath),
|
|
19572
|
+
judge_prompt: prompt
|
|
19573
|
+
},
|
|
19574
|
+
null,
|
|
19575
|
+
2
|
|
19576
|
+
)
|
|
19577
|
+
);
|
|
19441
19578
|
return;
|
|
19442
19579
|
}
|
|
19443
19580
|
console.log(prompt);
|
|
@@ -19529,7 +19666,7 @@ ${formatIssues(fileResult.error)}`,
|
|
|
19529
19666
|
runId,
|
|
19530
19667
|
validatedVerdicts
|
|
19531
19668
|
);
|
|
19532
|
-
const cloudInfo = await pushRunToCloud(projectRoot, runId);
|
|
19669
|
+
const cloudInfo = opts?.push === false ? void 0 : await pushRunToCloud(projectRoot, runId);
|
|
19533
19670
|
if (opts?.json) {
|
|
19534
19671
|
console.log(
|
|
19535
19672
|
JSON.stringify(
|
|
@@ -19646,6 +19783,36 @@ async function complete(runId, opts) {
|
|
|
19646
19783
|
return;
|
|
19647
19784
|
}
|
|
19648
19785
|
const completedAt = pickCompletedAt(run.manifest);
|
|
19786
|
+
const walkerSummary = await readWalkerSummary(run.runDirPath);
|
|
19787
|
+
let judgeReported;
|
|
19788
|
+
try {
|
|
19789
|
+
const verdictsRaw = await fs26.readFile(
|
|
19790
|
+
path26.join(run.runDirPath, "verdicts.json"),
|
|
19791
|
+
"utf8"
|
|
19792
|
+
);
|
|
19793
|
+
const parsed = JSON.parse(verdictsRaw);
|
|
19794
|
+
if (parsed.provenance?.model) {
|
|
19795
|
+
judgeReported = parsed.provenance.model;
|
|
19796
|
+
}
|
|
19797
|
+
} catch {
|
|
19798
|
+
}
|
|
19799
|
+
const stepCount = provenanceAudit.step_count;
|
|
19800
|
+
const durationSec = provenanceAudit.duration_seconds ?? (run.manifest.created_at && Date.parse(completedAt) > Date.parse(run.manifest.created_at) ? Math.round(
|
|
19801
|
+
(Date.parse(completedAt) - Date.parse(run.manifest.created_at)) / 1e3 * 10
|
|
19802
|
+
) / 10 : null);
|
|
19803
|
+
const velocitySec = provenanceAudit.avg_step_interval_seconds ?? (durationSec != null && stepCount > 1 ? Math.round(durationSec / (stepCount - 1) * 10) / 10 : null);
|
|
19804
|
+
const existingModels = run.manifest.models ?? {};
|
|
19805
|
+
const updatedModels = {
|
|
19806
|
+
...existingModels,
|
|
19807
|
+
...walkerSummary.model ? { walker_reported: walkerSummary.model } : {},
|
|
19808
|
+
...judgeReported ? { judge_reported: judgeReported } : {},
|
|
19809
|
+
...durationSec != null ? { total_duration_sec: durationSec } : {},
|
|
19810
|
+
...velocitySec != null ? { velocity_sec_per_step: velocitySec } : {}
|
|
19811
|
+
};
|
|
19812
|
+
run.manifest = {
|
|
19813
|
+
...run.manifest,
|
|
19814
|
+
models: updatedModels
|
|
19815
|
+
};
|
|
19649
19816
|
const assembled = await assembleRun(
|
|
19650
19817
|
run.projectRoot,
|
|
19651
19818
|
runId,
|
|
@@ -19773,7 +19940,7 @@ async function collectCompletionExtras(run, opts) {
|
|
|
19773
19940
|
parseRunViewport(manifest.viewport)
|
|
19774
19941
|
);
|
|
19775
19942
|
const codeWarning = opts?.force ? null : codeChangedWarning(manifest.build, detectWorktreeState(projectRoot));
|
|
19776
|
-
const cloudInfo = await pushRunToCloud(projectRoot, runId);
|
|
19943
|
+
const cloudInfo = opts?.push === false ? void 0 : await pushRunToCloud(projectRoot, runId);
|
|
19777
19944
|
return { revisit, viewportWarning, codeWarning, cloudInfo };
|
|
19778
19945
|
}
|
|
19779
19946
|
function completeJsonPayload(canonical, extras) {
|
|
@@ -19806,6 +19973,17 @@ function printCompletionSummary(run, completedAt, assembled, extras, provenanceA
|
|
|
19806
19973
|
console.log(` steps ${count(canonical.steps.length)}`);
|
|
19807
19974
|
console.log(` started ${chalk30.dim(manifest.created_at)}`);
|
|
19808
19975
|
console.log(` completed ${chalk30.dim(completedAt)}`);
|
|
19976
|
+
const models = manifest.models;
|
|
19977
|
+
if (models?.velocity_sec_per_step != null) {
|
|
19978
|
+
console.log(
|
|
19979
|
+
` velocity ${chalk30.cyan(`~${models.velocity_sec_per_step}s/step`)} ${chalk30.dim(`(${models.total_duration_sec}s total)`)}`
|
|
19980
|
+
);
|
|
19981
|
+
}
|
|
19982
|
+
if (models && (models.walker || models.judge || models.matcher)) {
|
|
19983
|
+
const w = models.walker_reported ? `${models.walker} (${models.walker_reported})` : models.walker ?? "fast";
|
|
19984
|
+
const j = models.judge_reported ? `${models.judge} (${models.judge_reported})` : models.judge ?? "thorough";
|
|
19985
|
+
console.log(` models ${chalk30.dim(`walker: ${w} \xB7 judge: ${j}`)}`);
|
|
19986
|
+
}
|
|
19809
19987
|
console.log();
|
|
19810
19988
|
console.log(
|
|
19811
19989
|
` ${chalk30.dim("canonical:")} ${chalk30.dim(path26.relative(projectRoot, assembled.canonicalPath))}`
|
|
@@ -19899,9 +20077,11 @@ async function readWalkerSummary(runDirPath) {
|
|
|
19899
20077
|
await fs26.readFile(path26.join(runDirPath, "summary.json"), "utf8")
|
|
19900
20078
|
);
|
|
19901
20079
|
const strings = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string" && x.trim() !== "") : void 0;
|
|
20080
|
+
const model = typeof data.model === "string" && data.model.trim() !== "" ? data.model.trim() : void 0;
|
|
19902
20081
|
return {
|
|
19903
20082
|
what_worked: strings(data.what_worked),
|
|
19904
|
-
open_questions: strings(data.open_questions)
|
|
20083
|
+
open_questions: strings(data.open_questions),
|
|
20084
|
+
model
|
|
19905
20085
|
};
|
|
19906
20086
|
} catch {
|
|
19907
20087
|
return {};
|
|
@@ -20226,9 +20406,28 @@ async function match(runId, opts) {
|
|
|
20226
20406
|
);
|
|
20227
20407
|
const needed = input.current.length > 0 && (input.prior.length > 0 || (input.guidance ?? []).length > 0);
|
|
20228
20408
|
const prompt = needed ? composeMatchPrompt(input) : null;
|
|
20409
|
+
const promptFilePath = opts?.promptFile ?? path27.join(runDirPath, "match_prompt.md");
|
|
20410
|
+
if (prompt) {
|
|
20411
|
+
await fs27.writeFile(promptFilePath, prompt, "utf8");
|
|
20412
|
+
} else {
|
|
20413
|
+
try {
|
|
20414
|
+
await fs27.unlink(promptFilePath);
|
|
20415
|
+
} catch {
|
|
20416
|
+
}
|
|
20417
|
+
}
|
|
20229
20418
|
if (opts?.json) {
|
|
20230
20419
|
console.log(
|
|
20231
|
-
JSON.stringify(
|
|
20420
|
+
JSON.stringify(
|
|
20421
|
+
{
|
|
20422
|
+
...input,
|
|
20423
|
+
needed,
|
|
20424
|
+
prompt_file: prompt ? path27.relative(ctx.projectRoot, promptFilePath) : null,
|
|
20425
|
+
prompt_file_abs: prompt ? path27.resolve(promptFilePath) : null,
|
|
20426
|
+
match_prompt: prompt
|
|
20427
|
+
},
|
|
20428
|
+
null,
|
|
20429
|
+
2
|
|
20430
|
+
)
|
|
20232
20431
|
);
|
|
20233
20432
|
return;
|
|
20234
20433
|
}
|
|
@@ -20664,6 +20863,15 @@ function registerWalkthroughCommands(program) {
|
|
|
20664
20863
|
).option(
|
|
20665
20864
|
"--env <name>",
|
|
20666
20865
|
"Environment the product runs in (default: the target URL's origin)"
|
|
20866
|
+
).option(
|
|
20867
|
+
"--walker <tierOrModel>",
|
|
20868
|
+
"Model tier ('fast', 'thorough', 'inherit') or model ID for driving browser navigation (default: fast)"
|
|
20869
|
+
).option(
|
|
20870
|
+
"--judge <tierOrModel>",
|
|
20871
|
+
"Model tier ('fast', 'thorough', 'inherit') or model ID for human test evaluation (default: thorough)"
|
|
20872
|
+
).option(
|
|
20873
|
+
"--matcher <tierOrModel>",
|
|
20874
|
+
"Model tier ('fast', 'thorough', 'inherit') or model ID for finding matching (default: thorough)"
|
|
20667
20875
|
).option(
|
|
20668
20876
|
"--check",
|
|
20669
20877
|
"Enforce target URL reachability (fail fast if server is offline)"
|
|
@@ -20694,7 +20902,10 @@ function registerWalkthroughCommands(program) {
|
|
|
20694
20902
|
).option(
|
|
20695
20903
|
"--run <run-id>",
|
|
20696
20904
|
"Run ID to size browser (start) or record step into (step)"
|
|
20697
|
-
).option("--action <text>", "Action description (step only)").option("--note <text>", "Persona inner monologue / reasoning (step only)").option("--surface <name>", "Screen or surface name (step only)").option(
|
|
20905
|
+
).option("--action <text>", "Action description (step only)").option("--note <text>", "Persona inner monologue / reasoning (step only)").option("--surface <name>", "Screen or surface name (step only)").option(
|
|
20906
|
+
"--step-file <path>",
|
|
20907
|
+
"Read step payload (action, note, cmd) from a JSON file or '-' for stdin (step only)"
|
|
20908
|
+
).option("--width <px>", "Window width (start)").option("--height <px>", "Window height (start)").option("--headless", "Run without a window (start)").option(
|
|
20698
20909
|
"--port <port>",
|
|
20699
20910
|
"CDP port (start), default 9333",
|
|
20700
20911
|
(value) => parsePortOption(value)
|
|
@@ -20770,9 +20981,9 @@ function registerWalkthroughCommands(program) {
|
|
|
20770
20981
|
).option("-y, --yes", "Skip interactive prompts").option("--no-prompt", "Skip interactive prompt to view results").option("--view", "Open results in browser immediately without prompt").option(
|
|
20771
20982
|
"--strict-provenance",
|
|
20772
20983
|
"Fail if steps show synthetic or programmatic timing anomalies"
|
|
20773
|
-
).addHelpText(
|
|
20984
|
+
).option("--no-push", "Skip pushing the run to Iamu Cloud").addHelpText(
|
|
20774
20985
|
"after",
|
|
20775
|
-
"\nExamples:\n $ iamu complete <run-id>\n $ iamu complete <run-id> --view\n $ iamu complete <run-id> --no-prompt\n $ iamu complete <run-id> --json\n $ iamu complete <run-id> --strict-provenance"
|
|
20986
|
+
"\nExamples:\n $ iamu complete <run-id>\n $ iamu complete <run-id> --view\n $ iamu complete <run-id> --no-prompt\n $ iamu complete <run-id> --no-push\n $ iamu complete <run-id> --json\n $ iamu complete <run-id> --strict-provenance"
|
|
20776
20987
|
).action(
|
|
20777
20988
|
async (runId, opts) => {
|
|
20778
20989
|
const { complete: complete2 } = await Promise.resolve().then(() => (init_complete(), complete_exports));
|
|
@@ -20781,23 +20992,27 @@ function registerWalkthroughCommands(program) {
|
|
|
20781
20992
|
);
|
|
20782
20993
|
program.command("match <run-id>").helpGroup("Walkthrough:").description(
|
|
20783
20994
|
"Compose the prompt that links this run's findings to earlier ones"
|
|
20784
|
-
).option("--json", "Output the match input bundle as JSON").
|
|
20785
|
-
|
|
20786
|
-
|
|
20787
|
-
|
|
20995
|
+
).option("--json", "Output the match input bundle as JSON").option("--prompt-file <path>", "Write the generated prompt to a file").action(
|
|
20996
|
+
async (runId, opts) => {
|
|
20997
|
+
const { match: match2 } = await Promise.resolve().then(() => (init_match(), match_exports));
|
|
20998
|
+
await match2(runId, opts);
|
|
20999
|
+
}
|
|
21000
|
+
);
|
|
20788
21001
|
program.command("match-record <run-id>").helpGroup("Walkthrough:").description(
|
|
20789
21002
|
"Record finding matches and re-derive the run's since-last-run delta"
|
|
20790
21003
|
).requiredOption("--file <path>", "Path to matches JSON file").option("--json", "Output match results as JSON").action(async (runId, opts) => {
|
|
20791
21004
|
const { matchRecord: matchRecord2 } = await Promise.resolve().then(() => (init_match(), match_exports));
|
|
20792
21005
|
await matchRecord2(runId, opts);
|
|
20793
21006
|
});
|
|
20794
|
-
program.command("evaluate <run-id>").helpGroup("Walkthrough:").description("Compose evaluation prompt for human-test questions").option("--json", "Output evaluation input bundle as JSON").
|
|
20795
|
-
|
|
20796
|
-
|
|
20797
|
-
|
|
20798
|
-
|
|
21007
|
+
program.command("evaluate <run-id>").helpGroup("Walkthrough:").description("Compose evaluation prompt for human-test questions").option("--json", "Output evaluation input bundle as JSON").option("--prompt-file <path>", "Write the generated prompt to a file").action(
|
|
21008
|
+
async (runId, opts) => {
|
|
21009
|
+
const { evaluate: evaluate2 } = await Promise.resolve().then(() => (init_evaluate(), evaluate_exports));
|
|
21010
|
+
await evaluate2(runId, opts);
|
|
21011
|
+
}
|
|
21012
|
+
);
|
|
21013
|
+
program.command("evaluate-record <run-id>").helpGroup("Walkthrough:").description("Record evaluation verdicts into the run's verdicts.json").requiredOption("--file <path>", "Path to verdicts JSON file").option("--json", "Output summary as JSON").option("-y, --yes", "Skip interactive prompts").option("--no-prompt", "Skip interactive prompt to view results").option("--no-push", "Skip pushing the updated run to Iamu Cloud").option("--view", "Open results in browser immediately without prompt").addHelpText(
|
|
20799
21014
|
"after",
|
|
20800
|
-
"\nExamples:\n $ iamu evaluate-record <run-id> --file verdicts.json\n $ iamu evaluate-record <run-id> --file verdicts.json --no-prompt\n $ iamu evaluate-record <run-id> --file verdicts.json --json"
|
|
21015
|
+
"\nExamples:\n $ iamu evaluate-record <run-id> --file verdicts.json\n $ iamu evaluate-record <run-id> --file verdicts.json --no-prompt\n $ iamu evaluate-record <run-id> --file verdicts.json --no-push\n $ iamu evaluate-record <run-id> --file verdicts.json --json"
|
|
20801
21016
|
).action(
|
|
20802
21017
|
async (runId, opts) => {
|
|
20803
21018
|
const { evaluateRecord: evaluateRecord2 } = await Promise.resolve().then(() => (init_evaluate(), evaluate_exports));
|
|
@@ -67,47 +67,45 @@ Filter to `triage_status: open`.
|
|
|
67
67
|
|
|
68
68
|
A run that simply did not come across a finding proves nothing — never call a fix verified because it was not seen. After the list, if any fixes await a re-run, say how many and in which scenarios (`iamu status --json` → `rerun`, with `requests.open` for team requests), and offer `iamu-run <scenario>` for the first one.
|
|
69
69
|
|
|
70
|
-
Group by `scenario` field and render
|
|
70
|
+
Group by `scenario` field and render with an executive tally header, followed by a severity-sorted list per scenario group (CRITICAL → MAJOR → MINOR → OBS; hinders and missing first, helps last), including persona quotes, evidence, and clickable file links:
|
|
71
71
|
|
|
72
72
|
```
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
| # | Severity | Type | Finding | Slug |
|
|
87
|
-
|---|----------|-----------|--------------------------------------------------|------------------------------------------------------------|
|
|
88
|
-
| 5 | CRITICAL | incorrect | Cart total recalculates to wrong amount | dev-patel--checkout-abandon--cart-total-wrong-... |
|
|
89
|
-
| 6 | MAJOR | missing | No save-for-later affordance | dev-patel--checkout-abandon--no-save-for-later-... |
|
|
90
|
-
| 7 | MINOR | hinders | Auto-scroll on form error | dev-patel--checkout-abandon--auto-scroll-error-... |
|
|
73
|
+
Findings Tally: <N> Open (<C> Critical · <M> Major · <m> Minor) across <S> Scenarios
|
|
74
|
+
Awaiting verification: <K> addressed findings pending re-run
|
|
75
|
+
|
|
76
|
+
▼ [<scenario>](iamu/scenarios/<scenario>.md) (4 findings)
|
|
77
|
+
1. [CRITICAL · hinders] Handoff list is cluttered ([file](iamu/findings/<slug>.md))
|
|
78
|
+
• Persona: <Persona Name>: "<Persona reaction or consequence quote>"
|
|
79
|
+
• Evidence: Step <N> at <url>
|
|
80
|
+
• Recommendation: Add status filter chips
|
|
81
|
+
|
|
82
|
+
2. [MAJOR · missing] No visible confirmation after shift submit ([file](iamu/findings/<slug>.md))
|
|
83
|
+
• Persona: <Persona Name>: "<Persona reaction or consequence quote>"
|
|
84
|
+
• Evidence: Step <N> at <url>
|
|
85
|
+
• Recommendation: Show inline success toast
|
|
91
86
|
```
|
|
92
87
|
|
|
93
|
-
Ask the user
|
|
88
|
+
Ask the user how they would like to triage:
|
|
94
89
|
|
|
90
|
+
- **"walk through them"** → iterate through all open findings one-by-one in order with full context, code location, and triage choices (`[Fix now] [Skip / Dismiss] [Discuss] [Next]`).
|
|
95
91
|
- **"all critical"** → iterate through all critical findings in order.
|
|
96
92
|
- **Scenario group name** (e.g. "group 1") → iterate through that scenario's findings.
|
|
97
|
-
- **Single number or slug** → single finding.
|
|
93
|
+
- **Single number or slug** → single finding (skip to step 3).
|
|
94
|
+
- **"fix 1 and 2"** → make code fixes and mark addressed immediately.
|
|
95
|
+
- **"skip 3" / "dismiss 3"** → dismiss with reason (`out_of_scope`, `not_relevant`, `walker_error`, `duplicate`).
|
|
98
96
|
|
|
99
97
|
### 3. Direct triage: review one finding
|
|
100
98
|
|
|
101
|
-
Read the finding file at `iamu/findings/<slug>.md`. Parse the YAML frontmatter and markdown body. Render full detail:
|
|
99
|
+
Read the finding file at `iamu/findings/<slug>.md`. Parse the YAML frontmatter and markdown body. Render full detail with clickable links and evidence:
|
|
102
100
|
|
|
103
101
|
```
|
|
104
|
-
Finding: <title>
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
Type: <type> Severity: <severity> Triage: <triage_status>
|
|
102
|
+
Finding: <title> ([file](iamu/findings/<slug>.md))
|
|
103
|
+
Scenario: [<scenario>](iamu/scenarios/<scenario>.md)
|
|
104
|
+
Severity: <severity> · Type: <type> · Status: <triage_status>
|
|
108
105
|
|
|
109
|
-
|
|
110
|
-
|
|
106
|
+
Persona Quote: "<persona_reaction_or_consequence>"
|
|
107
|
+
UI Fact: <ui_fact>
|
|
108
|
+
Evidence: Step <N> at <url> (Screenshot: [view](iamu/.runs/<run>/screenshots/<NNN>.png))
|
|
111
109
|
|
|
112
110
|
Description:
|
|
113
111
|
<markdown body above ## Recommendation>
|
|
@@ -115,7 +113,7 @@ Description:
|
|
|
115
113
|
Recommendation:
|
|
116
114
|
<## Recommendation section, or "none">
|
|
117
115
|
|
|
118
|
-
|
|
116
|
+
Report: [report.md](iamu/.runs/<run>/report.md)
|
|
119
117
|
```
|
|
120
118
|
|
|
121
119
|
If the finding references a run directory, mention the report path for context. If the user wants to see the screenshot, read from `iamu/.runs/<run>/screenshots/`.
|