@iamu-dev/iamu 0.2.0-beta.11 → 0.2.0-beta.13

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/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, path31) {
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(path31, "not valid JSON", err);
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(path31, "expected a JSON object");
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 path31 = getCredentialsPath();
12203
+ const path32 = getCredentialsPath();
12204
12204
  let raw;
12205
12205
  try {
12206
- raw = await readFile(path31, "utf-8");
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, path31);
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 path31 = getCredentialsPath();
12221
- await mkdir(dirname(path31), { recursive: true, mode: 448 });
12222
- await atomicWriteFile(path31, `${JSON.stringify(store, null, 2)}
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 path31 = getCredentialsPath();
12242
+ const path32 = getCredentialsPath();
12243
12243
  let raw;
12244
12244
  try {
12245
- raw = readFileSync(path31, "utf-8");
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, path31)[url];
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(path31, reason, cause) {
12290
+ constructor(path32, reason, cause) {
12291
12291
  super(
12292
- `Credentials file ${path31} is corrupt (${reason}). Fix it by hand, or delete it and run \`iamu auth login\` again.`,
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 ${path31} and run \`iamu auth login\`, or set IAMU_TOKEN.`
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(path31) {
13069
+ function isCliWrittenSkill(path32) {
13070
13070
  try {
13071
- return /^generatedBy:/m.test(readFileSync3(path31, "utf-8"));
13071
+ return /^generatedBy:/m.test(readFileSync3(path32, "utf-8"));
13072
13072
  } catch {
13073
13073
  return false;
13074
13074
  }
13075
13075
  }
13076
- function isCliWrittenPrompt(path31) {
13076
+ function isCliWrittenPrompt(path32) {
13077
13077
  try {
13078
- const content = readFileSync3(path31, "utf-8");
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 path31 = join4(host.getSkillsDir(projectRoot), name, "SKILL.md");
13091
- if (isCliWrittenSkill(path31)) files.push({ host, path: path31, content: null });
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, path31, depth) {
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: path31,
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(path31) {
19771
- if (!("goto" in (path31[0] ?? {}))) await goto(opts.startUrl);
19772
- for (const step of path31) {
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 path31 = !sameDoc && view === null ? [{ goto: after.url }] : [...surface.path, ...via, click];
19897
- addSurface(after, place, view, path31, surface.depth + 1);
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
- `Record steps to: ${rd}/steps/NNN.json (one file per step, 001.json upward): step_number, timestamp (ISO), action, note (first-person, in the persona's language), screenshot ("screenshots/step-N.png"), url (the page you are on when the step ends, copied from the driving surface), surface (optional short screen name, e.g. "search results").`,
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> --json \u2014 then add the body (description + "## Recommendation") below the frontmatter of the file it returns.`,
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, 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.`,
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 body = [
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 chalk30 from "chalk";
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: ${chalk30.cyan(url)}
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 fs29 } from "fs";
21336
- import path28 from "path";
21337
- import chalk31 from "chalk";
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 = path28.join(runDirPath, "canonical.json");
21486
+ const canonicalPath = path29.join(runDirPath, "canonical.json");
21368
21487
  let canonical;
21369
21488
  try {
21370
- const raw = await fs29.readFile(canonicalPath, "utf8");
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 ?? path28.join(runDirPath, "judge_prompt.md");
21427
- await fs29.writeFile(promptFilePath, prompt, "utf8");
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: path28.relative(projectRoot, promptFilePath),
21434
- prompt_file_abs: path28.resolve(promptFilePath),
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 = path28.join(runDirPath, "canonical.json");
21570
+ const canonicalPath = path29.join(runDirPath, "canonical.json");
21452
21571
  let canonical;
21453
21572
  try {
21454
- const raw = await fs29.readFile(canonicalPath, "utf8");
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
- const raw = await fs29.readFile(opts.file, "utf8");
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(`Cannot read verdicts file: ${opts.file}`, opts);
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 = path28.join(runDirPath, "verdicts.json");
21520
- const reportPath = path28.join(runDirPath, "report.md");
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: path28.relative(projectRoot, verdictsPath),
21541
- reportPath: path28.relative(projectRoot, 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 ${chalk31.cyan(runId)}`
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
- ` ${chalk31.dim("linked:")} ${linked.length} finding${linked.length === 1 ? "" : "s"} to the human test${linked.length === 1 ? "" : "s"} citing ${linked.length === 1 ? "it" : "them"}`
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
- ` ${chalk31.dim("verdicts:")} ${path28.relative(projectRoot, verdictsPath)}`
21699
+ ` ${chalk32.dim("verdicts:")} ${path29.relative(projectRoot, verdictsPath)}`
21563
21700
  );
21564
21701
  console.log(
21565
- ` ${chalk31.dim("report:")} ${path28.relative(projectRoot, reportPath)}`
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 fs30 } from "fs";
21631
- import path29 from "path";
21632
- import chalk32 from "chalk";
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(chalk32.bold(" Assembling canonical JSON\u2026"));
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 fs30.readFile(
21658
- path29.join(run.runDirPath, "verdicts.json"),
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 fs30.access(runDirPath);
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 = path29.join(runDirPath, "walkthrough.json");
21852
+ const manifestPath = path30.join(runDirPath, "walkthrough.json");
21716
21853
  let manifest;
21717
21854
  try {
21718
21855
  manifest = JSON.parse(
21719
- await fs30.readFile(manifestPath, "utf8")
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 fs30.readFile(
21732
- path29.join(run.runDirPath, "canonical.json"),
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(chalk32.bold(" Run: ") + chalk32.cyan(runId));
21753
- console.log(` status ${chalk32.green("completed")}`);
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 ${chalk32.dim(manifest.completed_at)}`);
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 ${chalk32.underline(viewUrl)}`);
21896
+ console.log(` view ${chalk33.underline(viewUrl)}`);
21760
21897
  }
21761
21898
  console.log();
21762
21899
  console.log(
21763
- chalk32.dim(
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
- chalk32.red(` ${symbols.error} Strict provenance audit failed:`)
21916
+ chalk33.red(` ${symbols.error} Strict provenance audit failed:`)
21780
21917
  );
21781
21918
  for (const w of audit.warnings) {
21782
- console.error(chalk32.red(` \u2022 ${w}`));
21919
+ console.error(chalk33.red(` \u2022 ${w}`));
21783
21920
  }
21784
21921
  console.error(
21785
- chalk32.dim(
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 ? chalk32.dim("0") : chalk32.cyan(String(n));
21827
- console.log(chalk32.bold(" Run: ") + chalk32.cyan(runId));
21828
- console.log(` scenario ${chalk32.dim(manifest.scenario_slug)}`);
21829
- console.log(` url ${chalk32.dim(manifest.target_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 ${chalk32.dim(manifest.environment)}`);
21968
+ console.log(` env ${chalk33.dim(manifest.environment)}`);
21832
21969
  }
21833
21970
  if (manifest.build) {
21834
- console.log(` build ${chalk32.dim(formatBuild(manifest.build))}`);
21971
+ console.log(` build ${chalk33.dim(formatBuild(manifest.build))}`);
21835
21972
  }
21836
21973
  if (manifest.viewport) {
21837
- console.log(` device ${chalk32.dim(formatViewport(manifest.viewport))}`);
21974
+ console.log(` device ${chalk33.dim(formatViewport(manifest.viewport))}`);
21838
21975
  }
21839
- console.log(` status ${chalk32.green("completed")}`);
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 ${chalk32.dim(manifest.created_at)}`);
21843
- console.log(` completed ${chalk32.dim(completedAt)}`);
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 ${chalk32.cyan(`~${models.velocity_sec_per_step}s/step`)} ${chalk32.dim(`(${models.total_duration_sec}s total)`)}`
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 ${chalk32.dim(`walker: ${w} \xB7 judge: ${j}`)}`);
21990
+ console.log(` models ${chalk33.dim(`walker: ${w} \xB7 judge: ${j}`)}`);
21854
21991
  }
21855
21992
  console.log();
21856
21993
  console.log(
21857
- ` ${chalk32.dim("canonical:")} ${chalk32.dim(path29.relative(projectRoot, assembled.canonicalPath))}`
21994
+ ` ${chalk33.dim("canonical:")} ${chalk33.dim(path30.relative(projectRoot, assembled.canonicalPath))}`
21858
21995
  );
21859
21996
  console.log(
21860
- ` ${chalk32.dim("report:")} ${chalk32.dim(path29.relative(projectRoot, assembled.reportPath))}`
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(` ${chalk32.yellow("!")} ${extras.codeWarning}`);
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} ${chalk32.yellow("Step provenance audit flagged suspicious timing:")}`
22017
+ ` ${symbols.warning} ${chalk33.yellow("Step provenance audit flagged suspicious timing:")}`
21881
22018
  );
21882
22019
  for (const w of audit.warnings) {
21883
- console.log(` ${chalk32.dim("\u2022")} ${w}`);
22020
+ console.log(` ${chalk33.dim("\u2022")} ${w}`);
21884
22021
  }
21885
22022
  console.log(
21886
- chalk32.dim(
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 = path29.join(runDirPath, "screenshots");
22055
+ const dir = path30.join(runDirPath, "screenshots");
21919
22056
  let first;
21920
22057
  try {
21921
- first = (await fs30.readdir(dir)).filter((f) => f.toLowerCase().endsWith(".png")).sort((a, b) => a.localeCompare(b, void 0, { numeric: true }))[0];
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 fs30.open(path29.join(dir, first), "r");
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 fs30.readFile(path29.join(runDirPath, "summary.json"), "utf8")
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 fs30.readFile(path29.join(runDirPath, "verdicts.json"), "utf8")
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 = path29.join(runDirPath, "canonical.json");
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 = path29.join(runDirPath, "report.md");
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 = ` ${chalk32.bold("Re-check places:")} reached ${r.reached} of ${r.requested}`;
22310
+ const head = ` ${chalk33.bold("Re-check places:")} reached ${r.reached} of ${r.requested}`;
22174
22311
  if (missed.length === 0) {
22175
- console.log(`${head} ${chalk32.green("\u2713")}`);
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(` ${chalk32.yellow("\u25CB")} ${placeLabel(p)}`);
22317
+ console.log(` ${chalk33.yellow("\u25CB")} ${placeLabel(p)}`);
22181
22318
  }
22182
22319
  console.log(
22183
- chalk32.dim(
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
- chalk32.bold(" Since this scenario's last run") + chalk32.dim(` (${verification.baseline_run_id.slice(0, 30)}\u2026)`) + chalk32.bold(":")
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 ${chalk32.dim("(not evidence of a fix)")}`
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 fs31 } from "fs";
22243
- import path30 from "path";
22244
- import chalk33 from "chalk";
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 fs31.readFile(path30.join(runDirPath, "walkthrough.json"), "utf8")
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 ?? path30.join(runDirPath, "match_prompt.md");
22415
+ const promptFilePath = opts?.promptFile ?? path31.join(runDirPath, "match_prompt.md");
22279
22416
  if (prompt) {
22280
- await fs31.writeFile(promptFilePath, prompt, "utf8");
22417
+ await fs32.writeFile(promptFilePath, prompt, "utf8");
22281
22418
  } else {
22282
22419
  try {
22283
- await fs31.unlink(promptFilePath);
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 ? path30.relative(ctx.projectRoot, promptFilePath) : null,
22294
- prompt_file_abs: prompt ? path30.resolve(promptFilePath) : null,
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
- chalk33.dim(
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
- answer = JSON.parse(await fs31.readFile(opts.file, "utf8"));
22324
- } catch {
22325
- commandError(`Cannot read matches file: ${opts.file}`, opts);
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
- ` ${chalk33.green("\u2713")} Matched ${result.linked} finding${result.linked === 1 ? "" : "s"} to earlier ones, ${result.new} first seen, ${result.rechecked_fixed} earlier re-checked fixed`
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
- ` ${chalk33.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)`
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
- chalk33.dim(" Run `iamu sync` to push the linked findings to the cloud.")
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 chalk34 from "chalk";
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
- chalk34.dim(" No runs match the pruning criteria. Nothing to prune.")
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(chalk34.yellow(" Pruning cancelled."));
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
- chalk34.cyan(
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
- chalk34.bold(
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
- chalk34.dim(" No runs matched the pruning criteria. Nothing pruned.")
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
- ` ${chalk34.dim("\u2022")} ${chalk34.bold(run.runId)} ${chalk34.dim(`(${run.scenarioSlug ?? "unknown scenario"})`)}: ${chalk34.yellow(formatBytes(run.bytesReclaimed))} (${count}${mode})`
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
- chalk34.dim(
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 (path31, opts) => {
22833
+ async (path32, opts) => {
22678
22834
  const { validate: validate2 } = await Promise.resolve().then(() => (init_validate(), validate_exports));
22679
- await validate2(path31, opts);
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
- ).requiredOption("--file <path>", "Path to matches JSON file").option("--json", "Output match results as JSON").action(async (runId, opts) => {
22940
- const { matchRecord: matchRecord2 } = await Promise.resolve().then(() => (init_match(), match_exports));
22941
- await matchRecord2(runId, opts);
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").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(
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 chalk35 from "chalk";
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(chalk35.red(`
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(chalk35.red(`
23180
+ console.error(chalk36.red(`
23000
23181
  Fatal Error: ${msg}`));
23001
23182
  console.error(
23002
- chalk35.dim(
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(chalk35.dim("\n Operation cancelled."));
23202
+ console.error(chalk36.dim("\n Operation cancelled."));
23022
23203
  process.exit(130);
23023
23204
  }
23024
23205
  });