@iamu-dev/iamu 0.2.0-beta.12 → 0.2.0-beta.14
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 +330 -149
- package/dist/commands/evaluate.d.ts +5 -3
- package/dist/commands/match.d.ts +6 -4
- package/dist/commands/new-finding.d.ts +6 -0
- package/dist/commands/summary.d.ts +14 -0
- package/dist/index.js +326 -145
- package/dist/templates/prompts/walk.md +32 -34
- package/dist/templates/skills/iamu-findings.md +13 -28
- package/dist/templates/skills/iamu-run.md +56 -76
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -12184,15 +12184,15 @@ function resolveApiUrl(configured) {
|
|
|
12184
12184
|
assertSecureApiUrl(url, configured ? "API URL" : "IAMU_API_URL");
|
|
12185
12185
|
return url;
|
|
12186
12186
|
}
|
|
12187
|
-
function parseStore(raw,
|
|
12187
|
+
function parseStore(raw, path32) {
|
|
12188
12188
|
let parsed;
|
|
12189
12189
|
try {
|
|
12190
12190
|
parsed = JSON.parse(raw);
|
|
12191
12191
|
} catch (err) {
|
|
12192
|
-
throw new CredentialsFileError(
|
|
12192
|
+
throw new CredentialsFileError(path32, "not valid JSON", err);
|
|
12193
12193
|
}
|
|
12194
12194
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
12195
|
-
throw new CredentialsFileError(
|
|
12195
|
+
throw new CredentialsFileError(path32, "expected a JSON object");
|
|
12196
12196
|
}
|
|
12197
12197
|
return parsed;
|
|
12198
12198
|
}
|
|
@@ -12200,26 +12200,26 @@ function isNotFound(err) {
|
|
|
12200
12200
|
return typeof err === "object" && err !== null && err.code === "ENOENT";
|
|
12201
12201
|
}
|
|
12202
12202
|
async function readCredentialsStore() {
|
|
12203
|
-
const
|
|
12203
|
+
const path32 = getCredentialsPath();
|
|
12204
12204
|
let raw;
|
|
12205
12205
|
try {
|
|
12206
|
-
raw = await readFile(
|
|
12206
|
+
raw = await readFile(path32, "utf-8");
|
|
12207
12207
|
} catch (err) {
|
|
12208
12208
|
if (isNotFound(err)) return {};
|
|
12209
12209
|
warnUnreadableStoreOnce(err);
|
|
12210
12210
|
throw err;
|
|
12211
12211
|
}
|
|
12212
12212
|
try {
|
|
12213
|
-
return parseStore(raw,
|
|
12213
|
+
return parseStore(raw, path32);
|
|
12214
12214
|
} catch (err) {
|
|
12215
12215
|
warnUnreadableStoreOnce(err);
|
|
12216
12216
|
throw err;
|
|
12217
12217
|
}
|
|
12218
12218
|
}
|
|
12219
12219
|
async function writeCredentialsStore(store) {
|
|
12220
|
-
const
|
|
12221
|
-
await mkdir(dirname(
|
|
12222
|
-
await atomicWriteFile(
|
|
12220
|
+
const path32 = getCredentialsPath();
|
|
12221
|
+
await mkdir(dirname(path32), { recursive: true, mode: 448 });
|
|
12222
|
+
await atomicWriteFile(path32, `${JSON.stringify(store, null, 2)}
|
|
12223
12223
|
`, {
|
|
12224
12224
|
mode: 384
|
|
12225
12225
|
});
|
|
@@ -12239,16 +12239,16 @@ function resolveApiKey(apiUrl) {
|
|
|
12239
12239
|
} catch {
|
|
12240
12240
|
return null;
|
|
12241
12241
|
}
|
|
12242
|
-
const
|
|
12242
|
+
const path32 = getCredentialsPath();
|
|
12243
12243
|
let raw;
|
|
12244
12244
|
try {
|
|
12245
|
-
raw = readFileSync(
|
|
12245
|
+
raw = readFileSync(path32, "utf-8");
|
|
12246
12246
|
} catch (err) {
|
|
12247
12247
|
if (!isNotFound(err)) warnUnreadableStoreOnce(err);
|
|
12248
12248
|
return null;
|
|
12249
12249
|
}
|
|
12250
12250
|
try {
|
|
12251
|
-
const entry = parseStore(raw,
|
|
12251
|
+
const entry = parseStore(raw, path32)[url];
|
|
12252
12252
|
if (typeof entry !== "object" || entry === null) return null;
|
|
12253
12253
|
const token = entry.token;
|
|
12254
12254
|
return typeof token === "string" && token ? token : null;
|
|
@@ -12287,12 +12287,12 @@ var init_credentials = __esm({
|
|
|
12287
12287
|
* @param reason - What is wrong with it.
|
|
12288
12288
|
* @param cause - Underlying parse error, if any.
|
|
12289
12289
|
*/
|
|
12290
|
-
constructor(
|
|
12290
|
+
constructor(path32, reason, cause) {
|
|
12291
12291
|
super(
|
|
12292
|
-
`Credentials file ${
|
|
12292
|
+
`Credentials file ${path32} is corrupt (${reason}). Fix it by hand, or delete it and run \`iamu auth login\` again.`,
|
|
12293
12293
|
{
|
|
12294
12294
|
cause,
|
|
12295
|
-
suggestedAction: `Delete ${
|
|
12295
|
+
suggestedAction: `Delete ${path32} and run \`iamu auth login\`, or set IAMU_TOKEN.`
|
|
12296
12296
|
}
|
|
12297
12297
|
);
|
|
12298
12298
|
}
|
|
@@ -13066,16 +13066,16 @@ async function needsWrite(filePath, content) {
|
|
|
13066
13066
|
return true;
|
|
13067
13067
|
}
|
|
13068
13068
|
}
|
|
13069
|
-
function isCliWrittenSkill(
|
|
13069
|
+
function isCliWrittenSkill(path32) {
|
|
13070
13070
|
try {
|
|
13071
|
-
return /^generatedBy:/m.test(readFileSync3(
|
|
13071
|
+
return /^generatedBy:/m.test(readFileSync3(path32, "utf-8"));
|
|
13072
13072
|
} catch {
|
|
13073
13073
|
return false;
|
|
13074
13074
|
}
|
|
13075
13075
|
}
|
|
13076
|
-
function isCliWrittenPrompt(
|
|
13076
|
+
function isCliWrittenPrompt(path32) {
|
|
13077
13077
|
try {
|
|
13078
|
-
const content = readFileSync3(
|
|
13078
|
+
const content = readFileSync3(path32, "utf-8");
|
|
13079
13079
|
return content.includes("iamu") || /^generatedBy:/m.test(content);
|
|
13080
13080
|
} catch {
|
|
13081
13081
|
return false;
|
|
@@ -13087,8 +13087,8 @@ function planSkillFiles(hosts, projectRoot, version) {
|
|
|
13087
13087
|
for (const host of hosts) {
|
|
13088
13088
|
if (pluginProvidesSkills(host, projectRoot)) {
|
|
13089
13089
|
for (const name of skillTemplates.keys()) {
|
|
13090
|
-
const
|
|
13091
|
-
if (isCliWrittenSkill(
|
|
13090
|
+
const path32 = join4(host.getSkillsDir(projectRoot), name, "SKILL.md");
|
|
13091
|
+
if (isCliWrittenSkill(path32)) files.push({ host, path: path32, content: null });
|
|
13092
13092
|
}
|
|
13093
13093
|
} else if (host.supportsSkills) {
|
|
13094
13094
|
const skillsDir = host.getSkillsDir(projectRoot);
|
|
@@ -19737,7 +19737,7 @@ async function crawlFrom(page, opts, origin, takeBlocked) {
|
|
|
19737
19737
|
const byKey = /* @__PURE__ */ new Map();
|
|
19738
19738
|
const byPlaceView = /* @__PURE__ */ new Map();
|
|
19739
19739
|
const signatureOf = (s) => exploreSignature(s.headings, s.title);
|
|
19740
|
-
function addSurface(state, place, view,
|
|
19740
|
+
function addSurface(state, place, view, path32, depth) {
|
|
19741
19741
|
const key = exploreSurfaceKey(place, view);
|
|
19742
19742
|
const surface = {
|
|
19743
19743
|
key,
|
|
@@ -19749,7 +19749,7 @@ async function crawlFrom(page, opts, origin, takeBlocked) {
|
|
|
19749
19749
|
headings: state.headings,
|
|
19750
19750
|
fields: state.fields,
|
|
19751
19751
|
depth,
|
|
19752
|
-
path:
|
|
19752
|
+
path: path32,
|
|
19753
19753
|
actions: []
|
|
19754
19754
|
};
|
|
19755
19755
|
surfaces.push(surface);
|
|
@@ -19767,9 +19767,9 @@ ${surface.signature}`, key);
|
|
|
19767
19767
|
});
|
|
19768
19768
|
await settle(page, settleMs);
|
|
19769
19769
|
}
|
|
19770
|
-
async function replay(
|
|
19771
|
-
if (!("goto" in (
|
|
19772
|
-
for (const step of
|
|
19770
|
+
async function replay(path32) {
|
|
19771
|
+
if (!("goto" in (path32[0] ?? {}))) await goto(opts.startUrl);
|
|
19772
|
+
for (const step of path32) {
|
|
19773
19773
|
if ("goto" in step) {
|
|
19774
19774
|
await goto(step.goto);
|
|
19775
19775
|
} else {
|
|
@@ -19893,8 +19893,8 @@ ${sig}`, key);
|
|
|
19893
19893
|
if (stats.surfaces >= budget.max_surfaces) {
|
|
19894
19894
|
stats.truncated = true;
|
|
19895
19895
|
} else {
|
|
19896
|
-
const
|
|
19897
|
-
addSurface(after, place, view,
|
|
19896
|
+
const path32 = !sameDoc && view === null ? [{ goto: after.url }] : [...surface.path, ...via, click];
|
|
19897
|
+
addSurface(after, place, view, path32, surface.depth + 1);
|
|
19898
19898
|
}
|
|
19899
19899
|
}
|
|
19900
19900
|
}
|
|
@@ -20769,15 +20769,15 @@ function composeWalkerPrompt(p) {
|
|
|
20769
20769
|
"",
|
|
20770
20770
|
`Run \`iamu\` as: ${iamu} (from ${p.projectRoot})`,
|
|
20771
20771
|
`Run directory: ${rd}`,
|
|
20772
|
-
`Record steps atomically:`,
|
|
20772
|
+
`Record steps atomically via CLI (do not hand-craft step JSON files or use host Write tools):`,
|
|
20773
20773
|
` \u2022 For iamu browse: ${iamu} browse step --run ${p.runId} --action "<action>" --note "<monologue>" <command> [args...]`,
|
|
20774
20774
|
` \u2022 For other surfaces: ${iamu} step --run ${p.runId} --action "<action>" --note "<monologue>" [--shot <path>]`,
|
|
20775
|
-
|
|
20776
|
-
"Never hand-craft raw JSON step files with custom scripts; the CLI handles step numbering, screenshots, and telemetry automatically.",
|
|
20775
|
+
"The CLI handles step numbering, timestamps, screenshot capture/linking, and telemetry automatically.",
|
|
20777
20776
|
`Screenshots to: ${rd}/screenshots/step-N.png \u2014 absolute paths.`,
|
|
20778
|
-
`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> --
|
|
20777
|
+
`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> --description "..." --recommendation "..." --json`,
|
|
20779
20778
|
"`--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.",
|
|
20780
|
-
`Before you finish,
|
|
20779
|
+
`Before you finish, record synthesis with: ${iamu} summary ${p.runId} --worked "..." --worked "..." --question "..."`,
|
|
20780
|
+
"Execute everything via CLI commands in the shell. Never invoke host Edit or Write tools on files in the workspace.",
|
|
20781
20781
|
"",
|
|
20782
20782
|
"Do not read the product's source code, other iamu/ entity files, or earlier runs: you know only what the persona knows.",
|
|
20783
20783
|
"",
|
|
@@ -21188,11 +21188,14 @@ async function newFinding(opts) {
|
|
|
21188
21188
|
if (opts.taskImpact) frontmatter.task_impact = opts.taskImpact;
|
|
21189
21189
|
if (opts.confidence) frontmatter.confidence = opts.confidence;
|
|
21190
21190
|
if (stepId) frontmatter.walkthrough_step_id = stepId;
|
|
21191
|
-
const
|
|
21191
|
+
const desc = opts.description?.trim();
|
|
21192
|
+
const rec = opts.recommendation?.trim();
|
|
21193
|
+
const body = opts.body ?? [
|
|
21194
|
+
desc ?? "",
|
|
21192
21195
|
"",
|
|
21193
21196
|
"## Recommendation",
|
|
21194
21197
|
"",
|
|
21195
|
-
"<!-- How should this be fixed? -->",
|
|
21198
|
+
rec ?? "<!-- How should this be fixed? -->",
|
|
21196
21199
|
""
|
|
21197
21200
|
].join("\n");
|
|
21198
21201
|
try {
|
|
@@ -21248,6 +21251,122 @@ var init_new_finding = __esm({
|
|
|
21248
21251
|
}
|
|
21249
21252
|
});
|
|
21250
21253
|
|
|
21254
|
+
// src/commands/summary.ts
|
|
21255
|
+
var summary_exports = {};
|
|
21256
|
+
__export(summary_exports, {
|
|
21257
|
+
summaryCommand: () => summaryCommand
|
|
21258
|
+
});
|
|
21259
|
+
import { promises as fs29 } from "fs";
|
|
21260
|
+
import path28 from "path";
|
|
21261
|
+
import chalk30 from "chalk";
|
|
21262
|
+
async function summaryCommand(runIdArg, opts) {
|
|
21263
|
+
const ctx = await resolveProject(opts);
|
|
21264
|
+
if (!ctx) return;
|
|
21265
|
+
const { projectRoot } = ctx;
|
|
21266
|
+
const runId = runIdArg ?? opts.run;
|
|
21267
|
+
if (!runId) {
|
|
21268
|
+
commandError("Run ID is required. Pass as argument or via --run.", opts);
|
|
21269
|
+
return;
|
|
21270
|
+
}
|
|
21271
|
+
const runDirPath = resolveRunDir(projectRoot, runId, opts);
|
|
21272
|
+
if (!runDirPath) return;
|
|
21273
|
+
let whatWorked = [];
|
|
21274
|
+
let openQuestions = [];
|
|
21275
|
+
let model = opts.model?.trim() || void 0;
|
|
21276
|
+
if (opts.file) {
|
|
21277
|
+
try {
|
|
21278
|
+
let raw;
|
|
21279
|
+
if (opts.file === "-") {
|
|
21280
|
+
const chunks = [];
|
|
21281
|
+
for await (const chunk of process.stdin) {
|
|
21282
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
21283
|
+
}
|
|
21284
|
+
raw = Buffer.concat(chunks).toString("utf8");
|
|
21285
|
+
} else {
|
|
21286
|
+
raw = await fs29.readFile(path28.resolve(projectRoot, opts.file), "utf8");
|
|
21287
|
+
}
|
|
21288
|
+
const parsed = JSON.parse(raw);
|
|
21289
|
+
if (Array.isArray(parsed.what_worked)) {
|
|
21290
|
+
whatWorked = parsed.what_worked.filter(
|
|
21291
|
+
(x) => typeof x === "string" && x.trim() !== ""
|
|
21292
|
+
);
|
|
21293
|
+
}
|
|
21294
|
+
if (Array.isArray(parsed.open_questions)) {
|
|
21295
|
+
openQuestions = parsed.open_questions.filter(
|
|
21296
|
+
(x) => typeof x === "string" && x.trim() !== ""
|
|
21297
|
+
);
|
|
21298
|
+
}
|
|
21299
|
+
if (typeof parsed.model === "string" && parsed.model.trim()) {
|
|
21300
|
+
model = parsed.model.trim();
|
|
21301
|
+
}
|
|
21302
|
+
} catch (err) {
|
|
21303
|
+
commandError(
|
|
21304
|
+
`Failed to parse summary file: ${err instanceof Error ? err.message : String(err)}`,
|
|
21305
|
+
opts
|
|
21306
|
+
);
|
|
21307
|
+
return;
|
|
21308
|
+
}
|
|
21309
|
+
}
|
|
21310
|
+
const toList = (v) => {
|
|
21311
|
+
if (!v) return [];
|
|
21312
|
+
const arr = Array.isArray(v) ? v : [v];
|
|
21313
|
+
return arr.map((s) => s.trim()).filter(Boolean);
|
|
21314
|
+
};
|
|
21315
|
+
const cliWorked = toList(opts.worked);
|
|
21316
|
+
if (cliWorked.length > 0) {
|
|
21317
|
+
whatWorked = [...whatWorked, ...cliWorked];
|
|
21318
|
+
}
|
|
21319
|
+
const cliQuestions = toList(opts.question);
|
|
21320
|
+
if (cliQuestions.length > 0) {
|
|
21321
|
+
openQuestions = [...openQuestions, ...cliQuestions];
|
|
21322
|
+
}
|
|
21323
|
+
const data = {
|
|
21324
|
+
what_worked: whatWorked,
|
|
21325
|
+
open_questions: openQuestions,
|
|
21326
|
+
...model ? { model } : {}
|
|
21327
|
+
};
|
|
21328
|
+
const summaryPath = path28.join(runDirPath, "summary.json");
|
|
21329
|
+
await atomicWriteFile(summaryPath, `${JSON.stringify(data, null, 2)}
|
|
21330
|
+
`);
|
|
21331
|
+
if (opts.json) {
|
|
21332
|
+
console.log(
|
|
21333
|
+
JSON.stringify(
|
|
21334
|
+
{
|
|
21335
|
+
run_id: runId,
|
|
21336
|
+
path: path28.relative(projectRoot, summaryPath),
|
|
21337
|
+
summary: data
|
|
21338
|
+
},
|
|
21339
|
+
null,
|
|
21340
|
+
2
|
|
21341
|
+
)
|
|
21342
|
+
);
|
|
21343
|
+
return;
|
|
21344
|
+
}
|
|
21345
|
+
console.log();
|
|
21346
|
+
console.log(
|
|
21347
|
+
` ${symbols.success} Summary recorded for run ${chalk30.cyan(runId)}`
|
|
21348
|
+
);
|
|
21349
|
+
if (data.what_worked.length > 0) {
|
|
21350
|
+
console.log(
|
|
21351
|
+
` ${chalk30.dim("what worked:")} ${data.what_worked.length} item(s)`
|
|
21352
|
+
);
|
|
21353
|
+
}
|
|
21354
|
+
if (data.open_questions.length > 0) {
|
|
21355
|
+
console.log(
|
|
21356
|
+
` ${chalk30.dim("open questions:")} ${data.open_questions.length} item(s)`
|
|
21357
|
+
);
|
|
21358
|
+
}
|
|
21359
|
+
console.log();
|
|
21360
|
+
}
|
|
21361
|
+
var init_summary = __esm({
|
|
21362
|
+
"src/commands/summary.ts"() {
|
|
21363
|
+
"use strict";
|
|
21364
|
+
init_dist2();
|
|
21365
|
+
init_symbols();
|
|
21366
|
+
init_context4();
|
|
21367
|
+
}
|
|
21368
|
+
});
|
|
21369
|
+
|
|
21251
21370
|
// src/commands/view-results.ts
|
|
21252
21371
|
var view_results_exports = {};
|
|
21253
21372
|
__export(view_results_exports, {
|
|
@@ -21255,7 +21374,7 @@ __export(view_results_exports, {
|
|
|
21255
21374
|
promptViewResults: () => promptViewResults
|
|
21256
21375
|
});
|
|
21257
21376
|
import { isCancel as isCancel4, select as select4 } from "@clack/prompts";
|
|
21258
|
-
import
|
|
21377
|
+
import chalk31 from "chalk";
|
|
21259
21378
|
function launchCloudDashboard(config, runId) {
|
|
21260
21379
|
const cloudRunId = runId ? toCloudRunUuid(runId) : void 0;
|
|
21261
21380
|
const url = getCloudDashboardUrl(
|
|
@@ -21264,7 +21383,7 @@ function launchCloudDashboard(config, runId) {
|
|
|
21264
21383
|
cloudRunId
|
|
21265
21384
|
);
|
|
21266
21385
|
console.log();
|
|
21267
|
-
console.log(` ${symbols.success} Opening Iamu Cloud: ${
|
|
21386
|
+
console.log(` ${symbols.success} Opening Iamu Cloud: ${chalk31.cyan(url)}
|
|
21268
21387
|
`);
|
|
21269
21388
|
openBrowser(url);
|
|
21270
21389
|
}
|
|
@@ -21332,9 +21451,9 @@ __export(evaluate_exports, {
|
|
|
21332
21451
|
evaluate: () => evaluate,
|
|
21333
21452
|
evaluateRecord: () => evaluateRecord
|
|
21334
21453
|
});
|
|
21335
|
-
import { promises as
|
|
21336
|
-
import
|
|
21337
|
-
import
|
|
21454
|
+
import { promises as fs30 } from "fs";
|
|
21455
|
+
import path29 from "path";
|
|
21456
|
+
import chalk32 from "chalk";
|
|
21338
21457
|
import { z as z25 } from "zod";
|
|
21339
21458
|
function formatIssues(error) {
|
|
21340
21459
|
return error.issues.map(
|
|
@@ -21364,10 +21483,10 @@ async function evaluate(runId, opts) {
|
|
|
21364
21483
|
const resolved = await resolveProjectAndRunDir(runId, opts);
|
|
21365
21484
|
if (!resolved) return;
|
|
21366
21485
|
const { projectRoot, runDirPath } = resolved;
|
|
21367
|
-
const canonicalPath =
|
|
21486
|
+
const canonicalPath = path29.join(runDirPath, "canonical.json");
|
|
21368
21487
|
let canonical;
|
|
21369
21488
|
try {
|
|
21370
|
-
const raw = await
|
|
21489
|
+
const raw = await fs30.readFile(canonicalPath, "utf8");
|
|
21371
21490
|
canonical = JSON.parse(raw);
|
|
21372
21491
|
} catch {
|
|
21373
21492
|
commandError(
|
|
@@ -21423,15 +21542,15 @@ async function evaluate(runId, opts) {
|
|
|
21423
21542
|
)
|
|
21424
21543
|
};
|
|
21425
21544
|
const prompt = composeJudgePrompt(input);
|
|
21426
|
-
const promptFilePath = opts?.promptFile ??
|
|
21427
|
-
await
|
|
21545
|
+
const promptFilePath = opts?.promptFile ?? path29.join(runDirPath, "judge_prompt.md");
|
|
21546
|
+
await fs30.writeFile(promptFilePath, prompt, "utf8");
|
|
21428
21547
|
if (opts?.json) {
|
|
21429
21548
|
console.log(
|
|
21430
21549
|
JSON.stringify(
|
|
21431
21550
|
{
|
|
21432
21551
|
...input,
|
|
21433
|
-
prompt_file:
|
|
21434
|
-
prompt_file_abs:
|
|
21552
|
+
prompt_file: path29.relative(projectRoot, promptFilePath),
|
|
21553
|
+
prompt_file_abs: path29.resolve(promptFilePath),
|
|
21435
21554
|
judge_prompt: prompt
|
|
21436
21555
|
},
|
|
21437
21556
|
null,
|
|
@@ -21448,21 +21567,39 @@ async function evaluateRecord(runId, opts) {
|
|
|
21448
21567
|
const { projectRoot, runDirPath } = resolved;
|
|
21449
21568
|
sendLivePhase(projectRoot, runId, "judging").catch(() => {
|
|
21450
21569
|
});
|
|
21451
|
-
const canonicalPath =
|
|
21570
|
+
const canonicalPath = path29.join(runDirPath, "canonical.json");
|
|
21452
21571
|
let canonical;
|
|
21453
21572
|
try {
|
|
21454
|
-
const raw = await
|
|
21573
|
+
const raw = await fs30.readFile(canonicalPath, "utf8");
|
|
21455
21574
|
canonical = JSON.parse(raw);
|
|
21456
21575
|
} catch {
|
|
21457
21576
|
commandError(`No canonical.json for run: ${runId}`, opts);
|
|
21458
21577
|
return;
|
|
21459
21578
|
}
|
|
21579
|
+
if (!opts.file && !opts.data) {
|
|
21580
|
+
commandError("Either --file <path> or --data <json> is required", opts);
|
|
21581
|
+
return;
|
|
21582
|
+
}
|
|
21460
21583
|
let parsedFile;
|
|
21461
21584
|
try {
|
|
21462
|
-
|
|
21585
|
+
let raw;
|
|
21586
|
+
if (opts.data) {
|
|
21587
|
+
raw = opts.data;
|
|
21588
|
+
} else if (opts.file === "-") {
|
|
21589
|
+
const chunks = [];
|
|
21590
|
+
for await (const chunk of process.stdin) {
|
|
21591
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
21592
|
+
}
|
|
21593
|
+
raw = Buffer.concat(chunks).toString("utf8");
|
|
21594
|
+
} else {
|
|
21595
|
+
raw = await fs30.readFile(opts.file, "utf8");
|
|
21596
|
+
}
|
|
21463
21597
|
parsedFile = JSON.parse(raw);
|
|
21464
|
-
} catch {
|
|
21465
|
-
commandError(
|
|
21598
|
+
} catch (err) {
|
|
21599
|
+
commandError(
|
|
21600
|
+
`Cannot read verdicts: ${err instanceof Error ? err.message : String(err)}`,
|
|
21601
|
+
opts
|
|
21602
|
+
);
|
|
21466
21603
|
return;
|
|
21467
21604
|
}
|
|
21468
21605
|
const fileResult = verdictsFileSchema.safeParse(parsedFile);
|
|
@@ -21516,8 +21653,8 @@ ${formatIssues(fileResult.error)}`,
|
|
|
21516
21653
|
overall: inputVerdicts.overall
|
|
21517
21654
|
});
|
|
21518
21655
|
const reportContent = renderCanonicalMarkdown(canonicalForReport);
|
|
21519
|
-
const verdictsPath =
|
|
21520
|
-
const reportPath =
|
|
21656
|
+
const verdictsPath = path29.join(runDirPath, "verdicts.json");
|
|
21657
|
+
const reportPath = path29.join(runDirPath, "report.md");
|
|
21521
21658
|
await Promise.all([
|
|
21522
21659
|
atomicWriteFile(
|
|
21523
21660
|
verdictsPath,
|
|
@@ -21537,8 +21674,8 @@ ${formatIssues(fileResult.error)}`,
|
|
|
21537
21674
|
JSON.stringify(
|
|
21538
21675
|
{
|
|
21539
21676
|
runId,
|
|
21540
|
-
verdictsPath:
|
|
21541
|
-
reportPath:
|
|
21677
|
+
verdictsPath: path29.relative(projectRoot, verdictsPath),
|
|
21678
|
+
reportPath: path29.relative(projectRoot, reportPath),
|
|
21542
21679
|
verdictsCount: validatedVerdicts.length,
|
|
21543
21680
|
linkedCount: linked.length,
|
|
21544
21681
|
...cloudInfo ? { cloud: cloudInfo } : {}
|
|
@@ -21551,18 +21688,18 @@ ${formatIssues(fileResult.error)}`,
|
|
|
21551
21688
|
}
|
|
21552
21689
|
console.log();
|
|
21553
21690
|
console.log(
|
|
21554
|
-
` ${symbols.success} Recorded ${validatedVerdicts.length} verdicts for run ${
|
|
21691
|
+
` ${symbols.success} Recorded ${validatedVerdicts.length} verdicts for run ${chalk32.cyan(runId)}`
|
|
21555
21692
|
);
|
|
21556
21693
|
if (linked.length > 0) {
|
|
21557
21694
|
console.log(
|
|
21558
|
-
` ${
|
|
21695
|
+
` ${chalk32.dim("linked:")} ${linked.length} finding${linked.length === 1 ? "" : "s"} to the human test${linked.length === 1 ? "" : "s"} citing ${linked.length === 1 ? "it" : "them"}`
|
|
21559
21696
|
);
|
|
21560
21697
|
}
|
|
21561
21698
|
console.log(
|
|
21562
|
-
` ${
|
|
21699
|
+
` ${chalk32.dim("verdicts:")} ${path29.relative(projectRoot, verdictsPath)}`
|
|
21563
21700
|
);
|
|
21564
21701
|
console.log(
|
|
21565
|
-
` ${
|
|
21702
|
+
` ${chalk32.dim("report:")} ${path29.relative(projectRoot, reportPath)}`
|
|
21566
21703
|
);
|
|
21567
21704
|
printCloudRunInfo(cloudInfo);
|
|
21568
21705
|
await promptViewResults(projectRoot, { ...opts, runId });
|
|
@@ -21627,9 +21764,9 @@ __export(complete_exports, {
|
|
|
21627
21764
|
screenshotSizeWarning: () => screenshotSizeWarning,
|
|
21628
21765
|
validateCanonicalWalkthrough: () => validateCanonicalWalkthrough
|
|
21629
21766
|
});
|
|
21630
|
-
import { promises as
|
|
21631
|
-
import
|
|
21632
|
-
import
|
|
21767
|
+
import { promises as fs31 } from "fs";
|
|
21768
|
+
import path30 from "path";
|
|
21769
|
+
import chalk33 from "chalk";
|
|
21633
21770
|
async function complete(runId, opts) {
|
|
21634
21771
|
const run = await loadRun(runId, opts);
|
|
21635
21772
|
if (!run) return;
|
|
@@ -21641,7 +21778,7 @@ async function complete(runId, opts) {
|
|
|
21641
21778
|
}
|
|
21642
21779
|
if (!opts?.json) {
|
|
21643
21780
|
console.log();
|
|
21644
|
-
console.log(
|
|
21781
|
+
console.log(chalk33.bold(" Assembling canonical JSON\u2026"));
|
|
21645
21782
|
}
|
|
21646
21783
|
const provenanceAudit = auditStepProvenance(
|
|
21647
21784
|
await readStepsForRun(run.projectRoot, runId)
|
|
@@ -21654,8 +21791,8 @@ async function complete(runId, opts) {
|
|
|
21654
21791
|
const walkerSummary = await readWalkerSummary(run.runDirPath);
|
|
21655
21792
|
let judgeReported;
|
|
21656
21793
|
try {
|
|
21657
|
-
const verdictsRaw = await
|
|
21658
|
-
|
|
21794
|
+
const verdictsRaw = await fs31.readFile(
|
|
21795
|
+
path30.join(run.runDirPath, "verdicts.json"),
|
|
21659
21796
|
"utf8"
|
|
21660
21797
|
);
|
|
21661
21798
|
const parsed = JSON.parse(verdictsRaw);
|
|
@@ -21707,16 +21844,16 @@ async function loadRun(runId, opts) {
|
|
|
21707
21844
|
if (!resolved) return null;
|
|
21708
21845
|
const { projectRoot, runDirPath } = resolved;
|
|
21709
21846
|
try {
|
|
21710
|
-
await
|
|
21847
|
+
await fs31.access(runDirPath);
|
|
21711
21848
|
} catch {
|
|
21712
21849
|
commandError(`Run not found: ${runId}`, opts);
|
|
21713
21850
|
return null;
|
|
21714
21851
|
}
|
|
21715
|
-
const manifestPath =
|
|
21852
|
+
const manifestPath = path30.join(runDirPath, "walkthrough.json");
|
|
21716
21853
|
let manifest;
|
|
21717
21854
|
try {
|
|
21718
21855
|
manifest = JSON.parse(
|
|
21719
|
-
await
|
|
21856
|
+
await fs31.readFile(manifestPath, "utf8")
|
|
21720
21857
|
);
|
|
21721
21858
|
} catch {
|
|
21722
21859
|
commandError(`No walkthrough.json found for run: ${runId}`, opts);
|
|
@@ -21728,8 +21865,8 @@ async function reportAlreadyCompleted(run, opts) {
|
|
|
21728
21865
|
const { runId, manifest } = run;
|
|
21729
21866
|
if (opts?.json) {
|
|
21730
21867
|
try {
|
|
21731
|
-
const raw = await
|
|
21732
|
-
|
|
21868
|
+
const raw = await fs31.readFile(
|
|
21869
|
+
path30.join(run.runDirPath, "canonical.json"),
|
|
21733
21870
|
"utf8"
|
|
21734
21871
|
);
|
|
21735
21872
|
console.log(raw.trim());
|
|
@@ -21749,18 +21886,18 @@ async function reportAlreadyCompleted(run, opts) {
|
|
|
21749
21886
|
return;
|
|
21750
21887
|
}
|
|
21751
21888
|
console.log();
|
|
21752
|
-
console.log(
|
|
21753
|
-
console.log(` status ${
|
|
21889
|
+
console.log(chalk33.bold(" Run: ") + chalk33.cyan(runId));
|
|
21890
|
+
console.log(` status ${chalk33.green("completed")}`);
|
|
21754
21891
|
if (manifest.completed_at) {
|
|
21755
|
-
console.log(` completed ${
|
|
21892
|
+
console.log(` completed ${chalk33.dim(manifest.completed_at)}`);
|
|
21756
21893
|
}
|
|
21757
21894
|
const viewUrl = await pushedRunUrl(run.projectRoot, runId);
|
|
21758
21895
|
if (viewUrl) {
|
|
21759
|
-
console.log(` view ${
|
|
21896
|
+
console.log(` view ${chalk33.underline(viewUrl)}`);
|
|
21760
21897
|
}
|
|
21761
21898
|
console.log();
|
|
21762
21899
|
console.log(
|
|
21763
|
-
|
|
21900
|
+
chalk33.dim(
|
|
21764
21901
|
" Already completed \u2014 nothing to do. Use --force to reassemble the report."
|
|
21765
21902
|
)
|
|
21766
21903
|
);
|
|
@@ -21776,13 +21913,13 @@ function reportStrictProvenanceFailure(audit, opts) {
|
|
|
21776
21913
|
}
|
|
21777
21914
|
console.error();
|
|
21778
21915
|
console.error(
|
|
21779
|
-
|
|
21916
|
+
chalk33.red(` ${symbols.error} Strict provenance audit failed:`)
|
|
21780
21917
|
);
|
|
21781
21918
|
for (const w of audit.warnings) {
|
|
21782
|
-
console.error(
|
|
21919
|
+
console.error(chalk33.red(` \u2022 ${w}`));
|
|
21783
21920
|
}
|
|
21784
21921
|
console.error(
|
|
21785
|
-
|
|
21922
|
+
chalk33.dim(
|
|
21786
21923
|
" Run was flagged as synthetic / scripted. Remove --strict-provenance to complete anyway."
|
|
21787
21924
|
)
|
|
21788
21925
|
);
|
|
@@ -21823,41 +21960,41 @@ function completeJsonPayload(canonical, extras) {
|
|
|
21823
21960
|
function printCompletionSummary(run, completedAt, assembled, extras, provenanceAudit) {
|
|
21824
21961
|
const { runId, projectRoot, manifest } = run;
|
|
21825
21962
|
const { canonical } = assembled;
|
|
21826
|
-
const count = (n) => n === 0 ?
|
|
21827
|
-
console.log(
|
|
21828
|
-
console.log(` scenario ${
|
|
21829
|
-
console.log(` url ${
|
|
21963
|
+
const count = (n) => n === 0 ? chalk33.dim("0") : chalk33.cyan(String(n));
|
|
21964
|
+
console.log(chalk33.bold(" Run: ") + chalk33.cyan(runId));
|
|
21965
|
+
console.log(` scenario ${chalk33.dim(manifest.scenario_slug)}`);
|
|
21966
|
+
console.log(` url ${chalk33.dim(manifest.target_url)}`);
|
|
21830
21967
|
if (manifest.environment) {
|
|
21831
|
-
console.log(` env ${
|
|
21968
|
+
console.log(` env ${chalk33.dim(manifest.environment)}`);
|
|
21832
21969
|
}
|
|
21833
21970
|
if (manifest.build) {
|
|
21834
|
-
console.log(` build ${
|
|
21971
|
+
console.log(` build ${chalk33.dim(formatBuild(manifest.build))}`);
|
|
21835
21972
|
}
|
|
21836
21973
|
if (manifest.viewport) {
|
|
21837
|
-
console.log(` device ${
|
|
21974
|
+
console.log(` device ${chalk33.dim(formatViewport(manifest.viewport))}`);
|
|
21838
21975
|
}
|
|
21839
|
-
console.log(` status ${
|
|
21976
|
+
console.log(` status ${chalk33.green("completed")}`);
|
|
21840
21977
|
console.log(` findings ${count(canonical.findings.length)}`);
|
|
21841
21978
|
console.log(` steps ${count(canonical.steps.length)}`);
|
|
21842
|
-
console.log(` started ${
|
|
21843
|
-
console.log(` completed ${
|
|
21979
|
+
console.log(` started ${chalk33.dim(manifest.created_at)}`);
|
|
21980
|
+
console.log(` completed ${chalk33.dim(completedAt)}`);
|
|
21844
21981
|
const models = manifest.models;
|
|
21845
21982
|
if (models?.velocity_sec_per_step != null) {
|
|
21846
21983
|
console.log(
|
|
21847
|
-
` velocity ${
|
|
21984
|
+
` velocity ${chalk33.cyan(`~${models.velocity_sec_per_step}s/step`)} ${chalk33.dim(`(${models.total_duration_sec}s total)`)}`
|
|
21848
21985
|
);
|
|
21849
21986
|
}
|
|
21850
21987
|
if (models && (models.walker || models.judge || models.matcher)) {
|
|
21851
21988
|
const w = models.walker_reported ? `${models.walker} (${models.walker_reported})` : models.walker ?? "fast";
|
|
21852
21989
|
const j = models.judge_reported ? `${models.judge} (${models.judge_reported})` : models.judge ?? "thorough";
|
|
21853
|
-
console.log(` models ${
|
|
21990
|
+
console.log(` models ${chalk33.dim(`walker: ${w} \xB7 judge: ${j}`)}`);
|
|
21854
21991
|
}
|
|
21855
21992
|
console.log();
|
|
21856
21993
|
console.log(
|
|
21857
|
-
` ${
|
|
21994
|
+
` ${chalk33.dim("canonical:")} ${chalk33.dim(path30.relative(projectRoot, assembled.canonicalPath))}`
|
|
21858
21995
|
);
|
|
21859
21996
|
console.log(
|
|
21860
|
-
` ${
|
|
21997
|
+
` ${chalk33.dim("report:")} ${chalk33.dim(path30.relative(projectRoot, assembled.reportPath))}`
|
|
21861
21998
|
);
|
|
21862
21999
|
if (extras.viewportWarning) {
|
|
21863
22000
|
console.log();
|
|
@@ -21865,7 +22002,7 @@ function printCompletionSummary(run, completedAt, assembled, extras, provenanceA
|
|
|
21865
22002
|
}
|
|
21866
22003
|
if (extras.codeWarning) {
|
|
21867
22004
|
console.log();
|
|
21868
|
-
console.log(` ${
|
|
22005
|
+
console.log(` ${chalk33.yellow("!")} ${extras.codeWarning}`);
|
|
21869
22006
|
}
|
|
21870
22007
|
if (extras.revisit) printRevisit(extras.revisit);
|
|
21871
22008
|
if (assembled.verification) printVerification(assembled.verification);
|
|
@@ -21877,13 +22014,13 @@ function printProvenanceWarnings(audit) {
|
|
|
21877
22014
|
if (audit.warnings.length === 0) return;
|
|
21878
22015
|
console.log();
|
|
21879
22016
|
console.log(
|
|
21880
|
-
` ${symbols.warning} ${
|
|
22017
|
+
` ${symbols.warning} ${chalk33.yellow("Step provenance audit flagged suspicious timing:")}`
|
|
21881
22018
|
);
|
|
21882
22019
|
for (const w of audit.warnings) {
|
|
21883
|
-
console.log(` ${
|
|
22020
|
+
console.log(` ${chalk33.dim("\u2022")} ${w}`);
|
|
21884
22021
|
}
|
|
21885
22022
|
console.log(
|
|
21886
|
-
|
|
22023
|
+
chalk33.dim(
|
|
21887
22024
|
" Run appears to be programmatically batch-generated rather than an interactive walkthrough."
|
|
21888
22025
|
)
|
|
21889
22026
|
);
|
|
@@ -21915,17 +22052,17 @@ function screenshotFitsViewport(width, expected) {
|
|
|
21915
22052
|
}
|
|
21916
22053
|
async function screenshotSizeWarning(runDirPath, viewport) {
|
|
21917
22054
|
if (!viewport) return null;
|
|
21918
|
-
const dir =
|
|
22055
|
+
const dir = path30.join(runDirPath, "screenshots");
|
|
21919
22056
|
let first;
|
|
21920
22057
|
try {
|
|
21921
|
-
first = (await
|
|
22058
|
+
first = (await fs31.readdir(dir)).filter((f) => f.toLowerCase().endsWith(".png")).sort((a, b) => a.localeCompare(b, void 0, { numeric: true }))[0];
|
|
21922
22059
|
} catch {
|
|
21923
22060
|
return null;
|
|
21924
22061
|
}
|
|
21925
22062
|
if (!first) return null;
|
|
21926
22063
|
let size = null;
|
|
21927
22064
|
try {
|
|
21928
|
-
const handle = await
|
|
22065
|
+
const handle = await fs31.open(path30.join(dir, first), "r");
|
|
21929
22066
|
try {
|
|
21930
22067
|
const head = Buffer.alloc(24);
|
|
21931
22068
|
const { bytesRead } = await handle.read(head, 0, 24, 0);
|
|
@@ -21942,7 +22079,7 @@ async function screenshotSizeWarning(runDirPath, viewport) {
|
|
|
21942
22079
|
async function readWalkerSummary(runDirPath) {
|
|
21943
22080
|
try {
|
|
21944
22081
|
const data = JSON.parse(
|
|
21945
|
-
await
|
|
22082
|
+
await fs31.readFile(path30.join(runDirPath, "summary.json"), "utf8")
|
|
21946
22083
|
);
|
|
21947
22084
|
const strings = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string" && x.trim() !== "") : void 0;
|
|
21948
22085
|
const model = typeof data.model === "string" && data.model.trim() !== "" ? data.model.trim() : void 0;
|
|
@@ -22051,7 +22188,7 @@ function validateCanonicalWalkthrough(canonical) {
|
|
|
22051
22188
|
async function applyRecordedVerdicts(canonical, runDirPath) {
|
|
22052
22189
|
try {
|
|
22053
22190
|
const recorded = JSON.parse(
|
|
22054
|
-
await
|
|
22191
|
+
await fs31.readFile(path30.join(runDirPath, "verdicts.json"), "utf8")
|
|
22055
22192
|
);
|
|
22056
22193
|
if (Array.isArray(recorded.verdicts)) {
|
|
22057
22194
|
return applyVerdicts(canonical, recorded);
|
|
@@ -22061,7 +22198,7 @@ async function applyRecordedVerdicts(canonical, runDirPath) {
|
|
|
22061
22198
|
return canonical;
|
|
22062
22199
|
}
|
|
22063
22200
|
async function persistCanonical(runDirPath, canonical) {
|
|
22064
|
-
const canonicalPath =
|
|
22201
|
+
const canonicalPath = path30.join(runDirPath, "canonical.json");
|
|
22065
22202
|
await atomicWriteFile(
|
|
22066
22203
|
canonicalPath,
|
|
22067
22204
|
`${JSON.stringify(canonical, null, 2)}
|
|
@@ -22070,7 +22207,7 @@ async function persistCanonical(runDirPath, canonical) {
|
|
|
22070
22207
|
return canonicalPath;
|
|
22071
22208
|
}
|
|
22072
22209
|
async function persistReport(runDirPath, canonical) {
|
|
22073
|
-
const reportPath =
|
|
22210
|
+
const reportPath = path30.join(runDirPath, "report.md");
|
|
22074
22211
|
await atomicWriteFile(reportPath, renderCanonicalMarkdown(canonical));
|
|
22075
22212
|
return reportPath;
|
|
22076
22213
|
}
|
|
@@ -22170,17 +22307,17 @@ function placeLabel(p) {
|
|
|
22170
22307
|
function printRevisit(r) {
|
|
22171
22308
|
const missed = r.places.filter((p) => !p.reached);
|
|
22172
22309
|
console.log();
|
|
22173
|
-
const head = ` ${
|
|
22310
|
+
const head = ` ${chalk33.bold("Re-check places:")} reached ${r.reached} of ${r.requested}`;
|
|
22174
22311
|
if (missed.length === 0) {
|
|
22175
|
-
console.log(`${head} ${
|
|
22312
|
+
console.log(`${head} ${chalk33.green("\u2713")}`);
|
|
22176
22313
|
return;
|
|
22177
22314
|
}
|
|
22178
22315
|
console.log(`${head} \u2014 not reached:`);
|
|
22179
22316
|
for (const p of missed) {
|
|
22180
|
-
console.log(` ${
|
|
22317
|
+
console.log(` ${chalk33.yellow("\u25CB")} ${placeLabel(p)}`);
|
|
22181
22318
|
}
|
|
22182
22319
|
console.log(
|
|
22183
|
-
|
|
22320
|
+
chalk33.dim(
|
|
22184
22321
|
" Fixes there stay awaiting a re-run until a walk gets there."
|
|
22185
22322
|
)
|
|
22186
22323
|
);
|
|
@@ -22188,7 +22325,7 @@ function printRevisit(r) {
|
|
|
22188
22325
|
function printVerification(verification) {
|
|
22189
22326
|
console.log();
|
|
22190
22327
|
console.log(
|
|
22191
|
-
|
|
22328
|
+
chalk33.bold(" Since this scenario's last run") + chalk33.dim(` (${verification.baseline_run_id.slice(0, 30)}\u2026)`) + chalk33.bold(":")
|
|
22192
22329
|
);
|
|
22193
22330
|
if (verification.resolved.length > 0) {
|
|
22194
22331
|
console.log(
|
|
@@ -22197,7 +22334,7 @@ function printVerification(verification) {
|
|
|
22197
22334
|
}
|
|
22198
22335
|
if (verification.not_observed.length > 0) {
|
|
22199
22336
|
console.log(
|
|
22200
|
-
` ${symbols.pending} ${verification.not_observed.length} not seen this run ${
|
|
22337
|
+
` ${symbols.pending} ${verification.not_observed.length} not seen this run ${chalk33.dim("(not evidence of a fix)")}`
|
|
22201
22338
|
);
|
|
22202
22339
|
}
|
|
22203
22340
|
if (verification.persists.length > 0) {
|
|
@@ -22239,14 +22376,14 @@ __export(match_exports, {
|
|
|
22239
22376
|
match: () => match,
|
|
22240
22377
|
matchRecord: () => matchRecord
|
|
22241
22378
|
});
|
|
22242
|
-
import { promises as
|
|
22243
|
-
import
|
|
22244
|
-
import
|
|
22379
|
+
import { promises as fs32 } from "fs";
|
|
22380
|
+
import path31 from "path";
|
|
22381
|
+
import chalk34 from "chalk";
|
|
22245
22382
|
async function completedManifest2(runDirPath, runId, opts) {
|
|
22246
22383
|
let manifest;
|
|
22247
22384
|
try {
|
|
22248
22385
|
manifest = JSON.parse(
|
|
22249
|
-
await
|
|
22386
|
+
await fs32.readFile(path31.join(runDirPath, "walkthrough.json"), "utf8")
|
|
22250
22387
|
);
|
|
22251
22388
|
} catch {
|
|
22252
22389
|
commandError(`Run not found: ${runId}`, opts);
|
|
@@ -22275,12 +22412,12 @@ async function match(runId, opts) {
|
|
|
22275
22412
|
);
|
|
22276
22413
|
const needed = input.current.length > 0 && (input.prior.length > 0 || (input.guidance ?? []).length > 0);
|
|
22277
22414
|
const prompt = needed ? composeMatchPrompt(input) : null;
|
|
22278
|
-
const promptFilePath = opts?.promptFile ??
|
|
22415
|
+
const promptFilePath = opts?.promptFile ?? path31.join(runDirPath, "match_prompt.md");
|
|
22279
22416
|
if (prompt) {
|
|
22280
|
-
await
|
|
22417
|
+
await fs32.writeFile(promptFilePath, prompt, "utf8");
|
|
22281
22418
|
} else {
|
|
22282
22419
|
try {
|
|
22283
|
-
await
|
|
22420
|
+
await fs32.unlink(promptFilePath);
|
|
22284
22421
|
} catch {
|
|
22285
22422
|
}
|
|
22286
22423
|
}
|
|
@@ -22290,8 +22427,8 @@ async function match(runId, opts) {
|
|
|
22290
22427
|
{
|
|
22291
22428
|
...input,
|
|
22292
22429
|
needed,
|
|
22293
|
-
prompt_file: prompt ?
|
|
22294
|
-
prompt_file_abs: prompt ?
|
|
22430
|
+
prompt_file: prompt ? path31.relative(ctx.projectRoot, promptFilePath) : null,
|
|
22431
|
+
prompt_file_abs: prompt ? path31.resolve(promptFilePath) : null,
|
|
22295
22432
|
match_prompt: prompt
|
|
22296
22433
|
},
|
|
22297
22434
|
null,
|
|
@@ -22302,7 +22439,7 @@ async function match(runId, opts) {
|
|
|
22302
22439
|
}
|
|
22303
22440
|
if (!prompt) {
|
|
22304
22441
|
console.log(
|
|
22305
|
-
|
|
22442
|
+
chalk34.dim(
|
|
22306
22443
|
" No earlier findings or tester guidance for this scenario to match against \u2014 nothing to do."
|
|
22307
22444
|
)
|
|
22308
22445
|
);
|
|
@@ -22318,11 +22455,30 @@ async function matchRecord(runId, opts) {
|
|
|
22318
22455
|
});
|
|
22319
22456
|
const manifest = await completedManifest2(runDirPath, runId, opts);
|
|
22320
22457
|
if (!manifest) return;
|
|
22458
|
+
if (!opts.file && !opts.data) {
|
|
22459
|
+
commandError("Either --file <path> or --data <json> is required", opts);
|
|
22460
|
+
return;
|
|
22461
|
+
}
|
|
22321
22462
|
let answer;
|
|
22322
22463
|
try {
|
|
22323
|
-
|
|
22324
|
-
|
|
22325
|
-
|
|
22464
|
+
let raw;
|
|
22465
|
+
if (opts.data) {
|
|
22466
|
+
raw = opts.data;
|
|
22467
|
+
} else if (opts.file === "-") {
|
|
22468
|
+
const chunks = [];
|
|
22469
|
+
for await (const chunk of process.stdin) {
|
|
22470
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
22471
|
+
}
|
|
22472
|
+
raw = Buffer.concat(chunks).toString("utf8");
|
|
22473
|
+
} else {
|
|
22474
|
+
raw = await fs32.readFile(opts.file, "utf8");
|
|
22475
|
+
}
|
|
22476
|
+
answer = JSON.parse(raw);
|
|
22477
|
+
} catch (err) {
|
|
22478
|
+
commandError(
|
|
22479
|
+
`Cannot read matches: ${err instanceof Error ? err.message : String(err)}`,
|
|
22480
|
+
opts
|
|
22481
|
+
);
|
|
22326
22482
|
return;
|
|
22327
22483
|
}
|
|
22328
22484
|
let result;
|
|
@@ -22361,17 +22517,17 @@ async function matchRecord(runId, opts) {
|
|
|
22361
22517
|
}
|
|
22362
22518
|
console.log();
|
|
22363
22519
|
console.log(
|
|
22364
|
-
` ${
|
|
22520
|
+
` ${chalk34.green("\u2713")} Matched ${result.linked} finding${result.linked === 1 ? "" : "s"} to earlier ones, ${result.new} first seen, ${result.rechecked_fixed} earlier re-checked fixed`
|
|
22365
22521
|
);
|
|
22366
22522
|
if (result.suppressed > 0) {
|
|
22367
22523
|
console.log(
|
|
22368
|
-
` ${
|
|
22524
|
+
` ${chalk34.yellow("!")} ${result.suppressed} finding${result.suppressed === 1 ? "" : "s"} match tester guidance (flagged with suppressed_by_guidance \u2014 dismiss them in triage if the rule holds)`
|
|
22369
22525
|
);
|
|
22370
22526
|
}
|
|
22371
22527
|
if (verification) printVerification(verification);
|
|
22372
22528
|
console.log();
|
|
22373
22529
|
console.log(
|
|
22374
|
-
|
|
22530
|
+
chalk34.dim(" Run `iamu sync` to push the linked findings to the cloud.")
|
|
22375
22531
|
);
|
|
22376
22532
|
console.log();
|
|
22377
22533
|
}
|
|
@@ -22393,7 +22549,7 @@ __export(prune_exports, {
|
|
|
22393
22549
|
prune: () => prune
|
|
22394
22550
|
});
|
|
22395
22551
|
import { confirm, isCancel as isCancel5 } from "@clack/prompts";
|
|
22396
|
-
import
|
|
22552
|
+
import chalk35 from "chalk";
|
|
22397
22553
|
function formatBytes(bytes) {
|
|
22398
22554
|
if (bytes <= 0) return "0 B";
|
|
22399
22555
|
if (bytes < 1024) return `${bytes} B`;
|
|
@@ -22439,7 +22595,7 @@ async function prune(opts = {}) {
|
|
|
22439
22595
|
});
|
|
22440
22596
|
if (preview.prunedRuns.length === 0) {
|
|
22441
22597
|
console.log(
|
|
22442
|
-
|
|
22598
|
+
chalk35.dim(" No runs match the pruning criteria. Nothing to prune.")
|
|
22443
22599
|
);
|
|
22444
22600
|
return preview;
|
|
22445
22601
|
}
|
|
@@ -22448,7 +22604,7 @@ async function prune(opts = {}) {
|
|
|
22448
22604
|
message: `Prune ${mode} ${preview.prunedRuns.length} run(s) and reclaim ${formatBytes(preview.totalBytesReclaimed)}?`
|
|
22449
22605
|
});
|
|
22450
22606
|
if (isCancel5(confirmed) || !confirmed) {
|
|
22451
|
-
console.log(
|
|
22607
|
+
console.log(chalk35.yellow(" Pruning cancelled."));
|
|
22452
22608
|
return;
|
|
22453
22609
|
}
|
|
22454
22610
|
}
|
|
@@ -22459,14 +22615,14 @@ async function prune(opts = {}) {
|
|
|
22459
22615
|
}
|
|
22460
22616
|
if (result.dryRun) {
|
|
22461
22617
|
console.log(
|
|
22462
|
-
|
|
22618
|
+
chalk35.cyan(
|
|
22463
22619
|
`
|
|
22464
22620
|
[DRY RUN] Would prune ${result.prunedRuns.length} of ${result.totalRunsScanned} run(s):`
|
|
22465
22621
|
)
|
|
22466
22622
|
);
|
|
22467
22623
|
} else {
|
|
22468
22624
|
console.log(
|
|
22469
|
-
|
|
22625
|
+
chalk35.bold(
|
|
22470
22626
|
`
|
|
22471
22627
|
${symbols.success} Pruned ${result.prunedRuns.length} of ${result.totalRunsScanned} run(s):`
|
|
22472
22628
|
)
|
|
@@ -22474,18 +22630,18 @@ async function prune(opts = {}) {
|
|
|
22474
22630
|
}
|
|
22475
22631
|
if (result.prunedRuns.length === 0) {
|
|
22476
22632
|
console.log(
|
|
22477
|
-
|
|
22633
|
+
chalk35.dim(" No runs matched the pruning criteria. Nothing pruned.")
|
|
22478
22634
|
);
|
|
22479
22635
|
} else {
|
|
22480
22636
|
for (const run of result.prunedRuns) {
|
|
22481
22637
|
const mode = run.fullyDeleted ? "removed" : "screenshots purged";
|
|
22482
22638
|
const count = run.screenshotsDeleted > 0 ? `${run.screenshotsDeleted} screenshots, ` : "";
|
|
22483
22639
|
console.log(
|
|
22484
|
-
` ${
|
|
22640
|
+
` ${chalk35.dim("\u2022")} ${chalk35.bold(run.runId)} ${chalk35.dim(`(${run.scenarioSlug ?? "unknown scenario"})`)}: ${chalk35.yellow(formatBytes(run.bytesReclaimed))} (${count}${mode})`
|
|
22485
22641
|
);
|
|
22486
22642
|
}
|
|
22487
22643
|
console.log(
|
|
22488
|
-
|
|
22644
|
+
chalk35.dim(
|
|
22489
22645
|
`
|
|
22490
22646
|
Total reclaimed: ${formatBytes(result.totalBytesReclaimed)} (${result.totalScreenshotsDeleted} screenshots deleted)
|
|
22491
22647
|
`
|
|
@@ -22674,9 +22830,9 @@ function registerProjectCommands(program) {
|
|
|
22674
22830
|
"after",
|
|
22675
22831
|
"\nExamples:\n $ iamu validate --all\n $ iamu validate --all --fix --dry-run\n $ iamu validate iamu/personas/maria.md"
|
|
22676
22832
|
).action(
|
|
22677
|
-
async (
|
|
22833
|
+
async (path32, opts) => {
|
|
22678
22834
|
const { validate: validate2 } = await Promise.resolve().then(() => (init_validate(), validate_exports));
|
|
22679
|
-
await validate2(
|
|
22835
|
+
await validate2(path32, opts);
|
|
22680
22836
|
}
|
|
22681
22837
|
);
|
|
22682
22838
|
program.command("update").helpGroup("Project:").description("Update and repair agent skills and prompts to latest version").option(
|
|
@@ -22901,6 +23057,15 @@ function registerWalkthroughCommands(program) {
|
|
|
22901
23057
|
).option(
|
|
22902
23058
|
"--step <n>",
|
|
22903
23059
|
"Step whose screenshot shows the finding \u2014 its evidence (step must be recorded first)"
|
|
23060
|
+
).option(
|
|
23061
|
+
"--description <text>",
|
|
23062
|
+
"Finding narrative description below frontmatter"
|
|
23063
|
+
).option(
|
|
23064
|
+
"--recommendation <text>",
|
|
23065
|
+
"Concrete recommendation for how to fix or address the finding"
|
|
23066
|
+
).option(
|
|
23067
|
+
"--body <markdown>",
|
|
23068
|
+
"Complete markdown body (overrides --description and --recommendation)"
|
|
22904
23069
|
).option("--json", "Output as JSON").addHelpText(
|
|
22905
23070
|
"after",
|
|
22906
23071
|
'\nExamples:\n $ iamu new-finding run-123 --title "Submit fails" --type hinders --severity critical\n $ iamu new-finding --run run-123 --scenario login --title "Submit fails" --type hinders --severity critical'
|
|
@@ -22911,6 +23076,20 @@ function registerWalkthroughCommands(program) {
|
|
|
22911
23076
|
await newFinding2({ ...opts, run });
|
|
22912
23077
|
}
|
|
22913
23078
|
);
|
|
23079
|
+
program.command("summary [run-id]").helpGroup("Walkthrough:").description(
|
|
23080
|
+
"Record walker summary (what worked, open questions) atomically"
|
|
23081
|
+
).option("--run <run-id>", "Run ID (or pass as positional argument)").option(
|
|
23082
|
+
"--worked <note...>",
|
|
23083
|
+
"Things that worked well (can be specified multiple times)"
|
|
23084
|
+
).option(
|
|
23085
|
+
"--question <note...>",
|
|
23086
|
+
"Open questions that arose (can be specified multiple times)"
|
|
23087
|
+
).option("--model <name>", "Model identifier (e.g. flash, haiku)").option("--file <path>", "Path to summary JSON file (or - for stdin)").option("--json", "Output as JSON").action(
|
|
23088
|
+
async (runIdArg, opts) => {
|
|
23089
|
+
const { summaryCommand: summaryCommand2 } = await Promise.resolve().then(() => (init_summary(), summary_exports));
|
|
23090
|
+
await summaryCommand2(runIdArg, opts);
|
|
23091
|
+
}
|
|
23092
|
+
);
|
|
22914
23093
|
program.command("complete <run-id>").helpGroup("Walkthrough:").description("Assemble canonical JSON + report from a walkthrough run").option("--json", "Output canonical JSON to stdout").option(
|
|
22915
23094
|
"--force",
|
|
22916
23095
|
"Reassemble canonical.json and report.md for an already completed run"
|
|
@@ -22936,17 +23115,19 @@ function registerWalkthroughCommands(program) {
|
|
|
22936
23115
|
);
|
|
22937
23116
|
program.command("match-record <run-id>").helpGroup("Walkthrough:").description(
|
|
22938
23117
|
"Record finding matches and re-derive the run's since-last-run delta"
|
|
22939
|
-
).
|
|
22940
|
-
|
|
22941
|
-
|
|
22942
|
-
|
|
23118
|
+
).option("--file <path>", "Path to matches JSON file (or - for stdin)").option("--data <json>", "Inline matches JSON string").option("--json", "Output match results as JSON").action(
|
|
23119
|
+
async (runId, opts) => {
|
|
23120
|
+
const { matchRecord: matchRecord2 } = await Promise.resolve().then(() => (init_match(), match_exports));
|
|
23121
|
+
await matchRecord2(runId, opts);
|
|
23122
|
+
}
|
|
23123
|
+
);
|
|
22943
23124
|
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(
|
|
22944
23125
|
async (runId, opts) => {
|
|
22945
23126
|
const { evaluate: evaluate2 } = await Promise.resolve().then(() => (init_evaluate(), evaluate_exports));
|
|
22946
23127
|
await evaluate2(runId, opts);
|
|
22947
23128
|
}
|
|
22948
23129
|
);
|
|
22949
|
-
program.command("evaluate-record <run-id>").helpGroup("Walkthrough:").description("Record evaluation verdicts into the run's verdicts.json").
|
|
23130
|
+
program.command("evaluate-record <run-id>").helpGroup("Walkthrough:").description("Record evaluation verdicts into the run's verdicts.json").option("--file <path>", "Path to verdicts JSON file (or - for stdin)").option("--data <json>", "Inline verdicts JSON string").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(
|
|
22950
23131
|
"after",
|
|
22951
23132
|
"\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"
|
|
22952
23133
|
).action(
|
|
@@ -22977,7 +23158,7 @@ function registerAllCommands(program) {
|
|
|
22977
23158
|
|
|
22978
23159
|
// src/ui/panic.ts
|
|
22979
23160
|
init_logger();
|
|
22980
|
-
import
|
|
23161
|
+
import chalk36 from "chalk";
|
|
22981
23162
|
var handlersRegistered = false;
|
|
22982
23163
|
var customSigintHandler = null;
|
|
22983
23164
|
function registerSigintCleanup(handler) {
|
|
@@ -22989,17 +23170,17 @@ function registerSigintCleanup(handler) {
|
|
|
22989
23170
|
function logFatalAndExit(title, errOrReason) {
|
|
22990
23171
|
process.stderr.write("\x1B[?25h");
|
|
22991
23172
|
if (isVerbose()) {
|
|
22992
|
-
console.error(
|
|
23173
|
+
console.error(chalk36.red(`
|
|
22993
23174
|
${title}:`));
|
|
22994
23175
|
console.error(
|
|
22995
23176
|
errOrReason instanceof Error ? errOrReason.stack || errOrReason : errOrReason
|
|
22996
23177
|
);
|
|
22997
23178
|
} else {
|
|
22998
23179
|
const msg = errOrReason instanceof Error ? errOrReason.message : String(errOrReason);
|
|
22999
|
-
console.error(
|
|
23180
|
+
console.error(chalk36.red(`
|
|
23000
23181
|
Fatal Error: ${msg}`));
|
|
23001
23182
|
console.error(
|
|
23002
|
-
|
|
23183
|
+
chalk36.dim(
|
|
23003
23184
|
" Run with --verbose or DEBUG=1 to view the full stack trace.\n"
|
|
23004
23185
|
)
|
|
23005
23186
|
);
|
|
@@ -23018,7 +23199,7 @@ function setupProcessHandlers() {
|
|
|
23018
23199
|
}
|
|
23019
23200
|
process.exit(130);
|
|
23020
23201
|
} else {
|
|
23021
|
-
console.error(
|
|
23202
|
+
console.error(chalk36.dim("\n Operation cancelled."));
|
|
23022
23203
|
process.exit(130);
|
|
23023
23204
|
}
|
|
23024
23205
|
});
|