@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 CHANGED
@@ -12112,15 +12112,15 @@ function resolveApiUrl(configured) {
12112
12112
  assertSecureApiUrl(url, configured ? "API URL" : "IAMU_API_URL");
12113
12113
  return url;
12114
12114
  }
12115
- function parseStore(raw, path31) {
12115
+ function parseStore(raw, path32) {
12116
12116
  let parsed;
12117
12117
  try {
12118
12118
  parsed = JSON.parse(raw);
12119
12119
  } catch (err) {
12120
- throw new CredentialsFileError(path31, "not valid JSON", err);
12120
+ throw new CredentialsFileError(path32, "not valid JSON", err);
12121
12121
  }
12122
12122
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
12123
- throw new CredentialsFileError(path31, "expected a JSON object");
12123
+ throw new CredentialsFileError(path32, "expected a JSON object");
12124
12124
  }
12125
12125
  return parsed;
12126
12126
  }
@@ -12128,26 +12128,26 @@ function isNotFound(err) {
12128
12128
  return typeof err === "object" && err !== null && err.code === "ENOENT";
12129
12129
  }
12130
12130
  async function readCredentialsStore() {
12131
- const path31 = getCredentialsPath();
12131
+ const path32 = getCredentialsPath();
12132
12132
  let raw;
12133
12133
  try {
12134
- raw = await readFile(path31, "utf-8");
12134
+ raw = await readFile(path32, "utf-8");
12135
12135
  } catch (err) {
12136
12136
  if (isNotFound(err)) return {};
12137
12137
  warnUnreadableStoreOnce(err);
12138
12138
  throw err;
12139
12139
  }
12140
12140
  try {
12141
- return parseStore(raw, path31);
12141
+ return parseStore(raw, path32);
12142
12142
  } catch (err) {
12143
12143
  warnUnreadableStoreOnce(err);
12144
12144
  throw err;
12145
12145
  }
12146
12146
  }
12147
12147
  async function writeCredentialsStore(store) {
12148
- const path31 = getCredentialsPath();
12149
- await mkdir(dirname(path31), { recursive: true, mode: 448 });
12150
- await atomicWriteFile(path31, `${JSON.stringify(store, null, 2)}
12148
+ const path32 = getCredentialsPath();
12149
+ await mkdir(dirname(path32), { recursive: true, mode: 448 });
12150
+ await atomicWriteFile(path32, `${JSON.stringify(store, null, 2)}
12151
12151
  `, {
12152
12152
  mode: 384
12153
12153
  });
@@ -12167,16 +12167,16 @@ function resolveApiKey(apiUrl) {
12167
12167
  } catch {
12168
12168
  return null;
12169
12169
  }
12170
- const path31 = getCredentialsPath();
12170
+ const path32 = getCredentialsPath();
12171
12171
  let raw;
12172
12172
  try {
12173
- raw = readFileSync(path31, "utf-8");
12173
+ raw = readFileSync(path32, "utf-8");
12174
12174
  } catch (err) {
12175
12175
  if (!isNotFound(err)) warnUnreadableStoreOnce(err);
12176
12176
  return null;
12177
12177
  }
12178
12178
  try {
12179
- const entry = parseStore(raw, path31)[url];
12179
+ const entry = parseStore(raw, path32)[url];
12180
12180
  if (typeof entry !== "object" || entry === null) return null;
12181
12181
  const token = entry.token;
12182
12182
  return typeof token === "string" && token ? token : null;
@@ -12215,12 +12215,12 @@ var init_credentials = __esm({
12215
12215
  * @param reason - What is wrong with it.
12216
12216
  * @param cause - Underlying parse error, if any.
12217
12217
  */
12218
- constructor(path31, reason, cause) {
12218
+ constructor(path32, reason, cause) {
12219
12219
  super(
12220
- `Credentials file ${path31} is corrupt (${reason}). Fix it by hand, or delete it and run \`iamu auth login\` again.`,
12220
+ `Credentials file ${path32} is corrupt (${reason}). Fix it by hand, or delete it and run \`iamu auth login\` again.`,
12221
12221
  {
12222
12222
  cause,
12223
- suggestedAction: `Delete ${path31} and run \`iamu auth login\`, or set IAMU_TOKEN.`
12223
+ suggestedAction: `Delete ${path32} and run \`iamu auth login\`, or set IAMU_TOKEN.`
12224
12224
  }
12225
12225
  );
12226
12226
  }
@@ -13087,16 +13087,16 @@ async function needsWrite(filePath, content) {
13087
13087
  return true;
13088
13088
  }
13089
13089
  }
13090
- function isCliWrittenSkill(path31) {
13090
+ function isCliWrittenSkill(path32) {
13091
13091
  try {
13092
- return /^generatedBy:/m.test(readFileSync3(path31, "utf-8"));
13092
+ return /^generatedBy:/m.test(readFileSync3(path32, "utf-8"));
13093
13093
  } catch {
13094
13094
  return false;
13095
13095
  }
13096
13096
  }
13097
- function isCliWrittenPrompt(path31) {
13097
+ function isCliWrittenPrompt(path32) {
13098
13098
  try {
13099
- const content = readFileSync3(path31, "utf-8");
13099
+ const content = readFileSync3(path32, "utf-8");
13100
13100
  return content.includes("iamu") || /^generatedBy:/m.test(content);
13101
13101
  } catch {
13102
13102
  return false;
@@ -13108,8 +13108,8 @@ function planSkillFiles(hosts, projectRoot, version2) {
13108
13108
  for (const host of hosts) {
13109
13109
  if (pluginProvidesSkills(host, projectRoot)) {
13110
13110
  for (const name of skillTemplates.keys()) {
13111
- const path31 = join4(host.getSkillsDir(projectRoot), name, "SKILL.md");
13112
- if (isCliWrittenSkill(path31)) files.push({ host, path: path31, content: null });
13111
+ const path32 = join4(host.getSkillsDir(projectRoot), name, "SKILL.md");
13112
+ if (isCliWrittenSkill(path32)) files.push({ host, path: path32, content: null });
13113
13113
  }
13114
13114
  } else if (host.supportsSkills) {
13115
13115
  const skillsDir = host.getSkillsDir(projectRoot);
@@ -19758,7 +19758,7 @@ async function crawlFrom(page, opts, origin, takeBlocked) {
19758
19758
  const byKey = /* @__PURE__ */ new Map();
19759
19759
  const byPlaceView = /* @__PURE__ */ new Map();
19760
19760
  const signatureOf = (s) => exploreSignature(s.headings, s.title);
19761
- function addSurface(state, place, view, path31, depth) {
19761
+ function addSurface(state, place, view, path32, depth) {
19762
19762
  const key = exploreSurfaceKey(place, view);
19763
19763
  const surface = {
19764
19764
  key,
@@ -19770,7 +19770,7 @@ async function crawlFrom(page, opts, origin, takeBlocked) {
19770
19770
  headings: state.headings,
19771
19771
  fields: state.fields,
19772
19772
  depth,
19773
- path: path31,
19773
+ path: path32,
19774
19774
  actions: []
19775
19775
  };
19776
19776
  surfaces.push(surface);
@@ -19788,9 +19788,9 @@ ${surface.signature}`, key);
19788
19788
  });
19789
19789
  await settle(page, settleMs);
19790
19790
  }
19791
- async function replay(path31) {
19792
- if (!("goto" in (path31[0] ?? {}))) await goto(opts.startUrl);
19793
- for (const step of path31) {
19791
+ async function replay(path32) {
19792
+ if (!("goto" in (path32[0] ?? {}))) await goto(opts.startUrl);
19793
+ for (const step of path32) {
19794
19794
  if ("goto" in step) {
19795
19795
  await goto(step.goto);
19796
19796
  } else {
@@ -19914,8 +19914,8 @@ ${sig}`, key);
19914
19914
  if (stats.surfaces >= budget.max_surfaces) {
19915
19915
  stats.truncated = true;
19916
19916
  } else {
19917
- const path31 = !sameDoc && view === null ? [{ goto: after.url }] : [...surface.path, ...via, click];
19918
- addSurface(after, place, view, path31, surface.depth + 1);
19917
+ const path32 = !sameDoc && view === null ? [{ goto: after.url }] : [...surface.path, ...via, click];
19918
+ addSurface(after, place, view, path32, surface.depth + 1);
19919
19919
  }
19920
19920
  }
19921
19921
  }
@@ -20790,15 +20790,15 @@ function composeWalkerPrompt(p) {
20790
20790
  "",
20791
20791
  `Run \`iamu\` as: ${iamu} (from ${p.projectRoot})`,
20792
20792
  `Run directory: ${rd}`,
20793
- `Record steps atomically:`,
20793
+ `Record steps atomically via CLI (do not hand-craft step JSON files or use host Write tools):`,
20794
20794
  ` \u2022 For iamu browse: ${iamu} browse step --run ${p.runId} --action "<action>" --note "<monologue>" <command> [args...]`,
20795
20795
  ` \u2022 For other surfaces: ${iamu} step --run ${p.runId} --action "<action>" --note "<monologue>" [--shot <path>]`,
20796
- `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").`,
20797
- "Never hand-craft raw JSON step files with custom scripts; the CLI handles step numbering, screenshots, and telemetry automatically.",
20796
+ "The CLI handles step numbering, timestamps, screenshot capture/linking, and telemetry automatically.",
20798
20797
  `Screenshots to: ${rd}/screenshots/step-N.png \u2014 absolute paths.`,
20799
- `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.`,
20798
+ `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`,
20800
20799
  "`--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.",
20801
- `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.`,
20800
+ `Before you finish, record synthesis with: ${iamu} summary ${p.runId} --worked "..." --worked "..." --question "..."`,
20801
+ "Execute everything via CLI commands in the shell. Never invoke host Edit or Write tools on files in the workspace.",
20802
20802
  "",
20803
20803
  "Do not read the product's source code, other iamu/ entity files, or earlier runs: you know only what the persona knows.",
20804
20804
  "",
@@ -21209,11 +21209,14 @@ async function newFinding(opts) {
21209
21209
  if (opts.taskImpact) frontmatter.task_impact = opts.taskImpact;
21210
21210
  if (opts.confidence) frontmatter.confidence = opts.confidence;
21211
21211
  if (stepId) frontmatter.walkthrough_step_id = stepId;
21212
- const body = [
21212
+ const desc = opts.description?.trim();
21213
+ const rec = opts.recommendation?.trim();
21214
+ const body = opts.body ?? [
21215
+ desc ?? "",
21213
21216
  "",
21214
21217
  "## Recommendation",
21215
21218
  "",
21216
- "<!-- How should this be fixed? -->",
21219
+ rec ?? "<!-- How should this be fixed? -->",
21217
21220
  ""
21218
21221
  ].join("\n");
21219
21222
  try {
@@ -21269,6 +21272,122 @@ var init_new_finding = __esm({
21269
21272
  }
21270
21273
  });
21271
21274
 
21275
+ // src/commands/summary.ts
21276
+ var summary_exports = {};
21277
+ __export(summary_exports, {
21278
+ summaryCommand: () => summaryCommand
21279
+ });
21280
+ import { promises as fs29 } from "fs";
21281
+ import path28 from "path";
21282
+ import chalk30 from "chalk";
21283
+ async function summaryCommand(runIdArg, opts) {
21284
+ const ctx = await resolveProject(opts);
21285
+ if (!ctx) return;
21286
+ const { projectRoot } = ctx;
21287
+ const runId = runIdArg ?? opts.run;
21288
+ if (!runId) {
21289
+ commandError("Run ID is required. Pass as argument or via --run.", opts);
21290
+ return;
21291
+ }
21292
+ const runDirPath = resolveRunDir(projectRoot, runId, opts);
21293
+ if (!runDirPath) return;
21294
+ let whatWorked = [];
21295
+ let openQuestions = [];
21296
+ let model = opts.model?.trim() || void 0;
21297
+ if (opts.file) {
21298
+ try {
21299
+ let raw;
21300
+ if (opts.file === "-") {
21301
+ const chunks = [];
21302
+ for await (const chunk of process.stdin) {
21303
+ chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
21304
+ }
21305
+ raw = Buffer.concat(chunks).toString("utf8");
21306
+ } else {
21307
+ raw = await fs29.readFile(path28.resolve(projectRoot, opts.file), "utf8");
21308
+ }
21309
+ const parsed = JSON.parse(raw);
21310
+ if (Array.isArray(parsed.what_worked)) {
21311
+ whatWorked = parsed.what_worked.filter(
21312
+ (x) => typeof x === "string" && x.trim() !== ""
21313
+ );
21314
+ }
21315
+ if (Array.isArray(parsed.open_questions)) {
21316
+ openQuestions = parsed.open_questions.filter(
21317
+ (x) => typeof x === "string" && x.trim() !== ""
21318
+ );
21319
+ }
21320
+ if (typeof parsed.model === "string" && parsed.model.trim()) {
21321
+ model = parsed.model.trim();
21322
+ }
21323
+ } catch (err) {
21324
+ commandError(
21325
+ `Failed to parse summary file: ${err instanceof Error ? err.message : String(err)}`,
21326
+ opts
21327
+ );
21328
+ return;
21329
+ }
21330
+ }
21331
+ const toList = (v) => {
21332
+ if (!v) return [];
21333
+ const arr = Array.isArray(v) ? v : [v];
21334
+ return arr.map((s) => s.trim()).filter(Boolean);
21335
+ };
21336
+ const cliWorked = toList(opts.worked);
21337
+ if (cliWorked.length > 0) {
21338
+ whatWorked = [...whatWorked, ...cliWorked];
21339
+ }
21340
+ const cliQuestions = toList(opts.question);
21341
+ if (cliQuestions.length > 0) {
21342
+ openQuestions = [...openQuestions, ...cliQuestions];
21343
+ }
21344
+ const data = {
21345
+ what_worked: whatWorked,
21346
+ open_questions: openQuestions,
21347
+ ...model ? { model } : {}
21348
+ };
21349
+ const summaryPath = path28.join(runDirPath, "summary.json");
21350
+ await atomicWriteFile(summaryPath, `${JSON.stringify(data, null, 2)}
21351
+ `);
21352
+ if (opts.json) {
21353
+ console.log(
21354
+ JSON.stringify(
21355
+ {
21356
+ run_id: runId,
21357
+ path: path28.relative(projectRoot, summaryPath),
21358
+ summary: data
21359
+ },
21360
+ null,
21361
+ 2
21362
+ )
21363
+ );
21364
+ return;
21365
+ }
21366
+ console.log();
21367
+ console.log(
21368
+ ` ${symbols.success} Summary recorded for run ${chalk30.cyan(runId)}`
21369
+ );
21370
+ if (data.what_worked.length > 0) {
21371
+ console.log(
21372
+ ` ${chalk30.dim("what worked:")} ${data.what_worked.length} item(s)`
21373
+ );
21374
+ }
21375
+ if (data.open_questions.length > 0) {
21376
+ console.log(
21377
+ ` ${chalk30.dim("open questions:")} ${data.open_questions.length} item(s)`
21378
+ );
21379
+ }
21380
+ console.log();
21381
+ }
21382
+ var init_summary = __esm({
21383
+ "src/commands/summary.ts"() {
21384
+ "use strict";
21385
+ init_dist2();
21386
+ init_symbols();
21387
+ init_context4();
21388
+ }
21389
+ });
21390
+
21272
21391
  // src/commands/view-results.ts
21273
21392
  var view_results_exports = {};
21274
21393
  __export(view_results_exports, {
@@ -21276,7 +21395,7 @@ __export(view_results_exports, {
21276
21395
  promptViewResults: () => promptViewResults
21277
21396
  });
21278
21397
  import { isCancel as isCancel4, select as select4 } from "@clack/prompts";
21279
- import chalk30 from "chalk";
21398
+ import chalk31 from "chalk";
21280
21399
  function launchCloudDashboard(config, runId) {
21281
21400
  const cloudRunId = runId ? toCloudRunUuid(runId) : void 0;
21282
21401
  const url = getCloudDashboardUrl(
@@ -21285,7 +21404,7 @@ function launchCloudDashboard(config, runId) {
21285
21404
  cloudRunId
21286
21405
  );
21287
21406
  console.log();
21288
- console.log(` ${symbols.success} Opening Iamu Cloud: ${chalk30.cyan(url)}
21407
+ console.log(` ${symbols.success} Opening Iamu Cloud: ${chalk31.cyan(url)}
21289
21408
  `);
21290
21409
  openBrowser(url);
21291
21410
  }
@@ -21353,9 +21472,9 @@ __export(evaluate_exports, {
21353
21472
  evaluate: () => evaluate,
21354
21473
  evaluateRecord: () => evaluateRecord
21355
21474
  });
21356
- import { promises as fs29 } from "fs";
21357
- import path28 from "path";
21358
- import chalk31 from "chalk";
21475
+ import { promises as fs30 } from "fs";
21476
+ import path29 from "path";
21477
+ import chalk32 from "chalk";
21359
21478
  import { z as z25 } from "zod";
21360
21479
  function formatIssues(error) {
21361
21480
  return error.issues.map(
@@ -21385,10 +21504,10 @@ async function evaluate(runId, opts) {
21385
21504
  const resolved = await resolveProjectAndRunDir(runId, opts);
21386
21505
  if (!resolved) return;
21387
21506
  const { projectRoot, runDirPath } = resolved;
21388
- const canonicalPath = path28.join(runDirPath, "canonical.json");
21507
+ const canonicalPath = path29.join(runDirPath, "canonical.json");
21389
21508
  let canonical;
21390
21509
  try {
21391
- const raw = await fs29.readFile(canonicalPath, "utf8");
21510
+ const raw = await fs30.readFile(canonicalPath, "utf8");
21392
21511
  canonical = JSON.parse(raw);
21393
21512
  } catch {
21394
21513
  commandError(
@@ -21444,15 +21563,15 @@ async function evaluate(runId, opts) {
21444
21563
  )
21445
21564
  };
21446
21565
  const prompt = composeJudgePrompt(input);
21447
- const promptFilePath = opts?.promptFile ?? path28.join(runDirPath, "judge_prompt.md");
21448
- await fs29.writeFile(promptFilePath, prompt, "utf8");
21566
+ const promptFilePath = opts?.promptFile ?? path29.join(runDirPath, "judge_prompt.md");
21567
+ await fs30.writeFile(promptFilePath, prompt, "utf8");
21449
21568
  if (opts?.json) {
21450
21569
  console.log(
21451
21570
  JSON.stringify(
21452
21571
  {
21453
21572
  ...input,
21454
- prompt_file: path28.relative(projectRoot, promptFilePath),
21455
- prompt_file_abs: path28.resolve(promptFilePath),
21573
+ prompt_file: path29.relative(projectRoot, promptFilePath),
21574
+ prompt_file_abs: path29.resolve(promptFilePath),
21456
21575
  judge_prompt: prompt
21457
21576
  },
21458
21577
  null,
@@ -21469,21 +21588,39 @@ async function evaluateRecord(runId, opts) {
21469
21588
  const { projectRoot, runDirPath } = resolved;
21470
21589
  sendLivePhase(projectRoot, runId, "judging").catch(() => {
21471
21590
  });
21472
- const canonicalPath = path28.join(runDirPath, "canonical.json");
21591
+ const canonicalPath = path29.join(runDirPath, "canonical.json");
21473
21592
  let canonical;
21474
21593
  try {
21475
- const raw = await fs29.readFile(canonicalPath, "utf8");
21594
+ const raw = await fs30.readFile(canonicalPath, "utf8");
21476
21595
  canonical = JSON.parse(raw);
21477
21596
  } catch {
21478
21597
  commandError(`No canonical.json for run: ${runId}`, opts);
21479
21598
  return;
21480
21599
  }
21600
+ if (!opts.file && !opts.data) {
21601
+ commandError("Either --file <path> or --data <json> is required", opts);
21602
+ return;
21603
+ }
21481
21604
  let parsedFile;
21482
21605
  try {
21483
- const raw = await fs29.readFile(opts.file, "utf8");
21606
+ let raw;
21607
+ if (opts.data) {
21608
+ raw = opts.data;
21609
+ } else if (opts.file === "-") {
21610
+ const chunks = [];
21611
+ for await (const chunk of process.stdin) {
21612
+ chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
21613
+ }
21614
+ raw = Buffer.concat(chunks).toString("utf8");
21615
+ } else {
21616
+ raw = await fs30.readFile(opts.file, "utf8");
21617
+ }
21484
21618
  parsedFile = JSON.parse(raw);
21485
- } catch {
21486
- commandError(`Cannot read verdicts file: ${opts.file}`, opts);
21619
+ } catch (err) {
21620
+ commandError(
21621
+ `Cannot read verdicts: ${err instanceof Error ? err.message : String(err)}`,
21622
+ opts
21623
+ );
21487
21624
  return;
21488
21625
  }
21489
21626
  const fileResult = verdictsFileSchema.safeParse(parsedFile);
@@ -21537,8 +21674,8 @@ ${formatIssues(fileResult.error)}`,
21537
21674
  overall: inputVerdicts.overall
21538
21675
  });
21539
21676
  const reportContent = renderCanonicalMarkdown(canonicalForReport);
21540
- const verdictsPath = path28.join(runDirPath, "verdicts.json");
21541
- const reportPath = path28.join(runDirPath, "report.md");
21677
+ const verdictsPath = path29.join(runDirPath, "verdicts.json");
21678
+ const reportPath = path29.join(runDirPath, "report.md");
21542
21679
  await Promise.all([
21543
21680
  atomicWriteFile(
21544
21681
  verdictsPath,
@@ -21558,8 +21695,8 @@ ${formatIssues(fileResult.error)}`,
21558
21695
  JSON.stringify(
21559
21696
  {
21560
21697
  runId,
21561
- verdictsPath: path28.relative(projectRoot, verdictsPath),
21562
- reportPath: path28.relative(projectRoot, reportPath),
21698
+ verdictsPath: path29.relative(projectRoot, verdictsPath),
21699
+ reportPath: path29.relative(projectRoot, reportPath),
21563
21700
  verdictsCount: validatedVerdicts.length,
21564
21701
  linkedCount: linked.length,
21565
21702
  ...cloudInfo ? { cloud: cloudInfo } : {}
@@ -21572,18 +21709,18 @@ ${formatIssues(fileResult.error)}`,
21572
21709
  }
21573
21710
  console.log();
21574
21711
  console.log(
21575
- ` ${symbols.success} Recorded ${validatedVerdicts.length} verdicts for run ${chalk31.cyan(runId)}`
21712
+ ` ${symbols.success} Recorded ${validatedVerdicts.length} verdicts for run ${chalk32.cyan(runId)}`
21576
21713
  );
21577
21714
  if (linked.length > 0) {
21578
21715
  console.log(
21579
- ` ${chalk31.dim("linked:")} ${linked.length} finding${linked.length === 1 ? "" : "s"} to the human test${linked.length === 1 ? "" : "s"} citing ${linked.length === 1 ? "it" : "them"}`
21716
+ ` ${chalk32.dim("linked:")} ${linked.length} finding${linked.length === 1 ? "" : "s"} to the human test${linked.length === 1 ? "" : "s"} citing ${linked.length === 1 ? "it" : "them"}`
21580
21717
  );
21581
21718
  }
21582
21719
  console.log(
21583
- ` ${chalk31.dim("verdicts:")} ${path28.relative(projectRoot, verdictsPath)}`
21720
+ ` ${chalk32.dim("verdicts:")} ${path29.relative(projectRoot, verdictsPath)}`
21584
21721
  );
21585
21722
  console.log(
21586
- ` ${chalk31.dim("report:")} ${path28.relative(projectRoot, reportPath)}`
21723
+ ` ${chalk32.dim("report:")} ${path29.relative(projectRoot, reportPath)}`
21587
21724
  );
21588
21725
  printCloudRunInfo(cloudInfo);
21589
21726
  await promptViewResults(projectRoot, { ...opts, runId });
@@ -21648,9 +21785,9 @@ __export(complete_exports, {
21648
21785
  screenshotSizeWarning: () => screenshotSizeWarning,
21649
21786
  validateCanonicalWalkthrough: () => validateCanonicalWalkthrough
21650
21787
  });
21651
- import { promises as fs30 } from "fs";
21652
- import path29 from "path";
21653
- import chalk32 from "chalk";
21788
+ import { promises as fs31 } from "fs";
21789
+ import path30 from "path";
21790
+ import chalk33 from "chalk";
21654
21791
  async function complete(runId, opts) {
21655
21792
  const run = await loadRun(runId, opts);
21656
21793
  if (!run) return;
@@ -21662,7 +21799,7 @@ async function complete(runId, opts) {
21662
21799
  }
21663
21800
  if (!opts?.json) {
21664
21801
  console.log();
21665
- console.log(chalk32.bold(" Assembling canonical JSON\u2026"));
21802
+ console.log(chalk33.bold(" Assembling canonical JSON\u2026"));
21666
21803
  }
21667
21804
  const provenanceAudit = auditStepProvenance(
21668
21805
  await readStepsForRun(run.projectRoot, runId)
@@ -21675,8 +21812,8 @@ async function complete(runId, opts) {
21675
21812
  const walkerSummary = await readWalkerSummary(run.runDirPath);
21676
21813
  let judgeReported;
21677
21814
  try {
21678
- const verdictsRaw = await fs30.readFile(
21679
- path29.join(run.runDirPath, "verdicts.json"),
21815
+ const verdictsRaw = await fs31.readFile(
21816
+ path30.join(run.runDirPath, "verdicts.json"),
21680
21817
  "utf8"
21681
21818
  );
21682
21819
  const parsed = JSON.parse(verdictsRaw);
@@ -21728,16 +21865,16 @@ async function loadRun(runId, opts) {
21728
21865
  if (!resolved) return null;
21729
21866
  const { projectRoot, runDirPath } = resolved;
21730
21867
  try {
21731
- await fs30.access(runDirPath);
21868
+ await fs31.access(runDirPath);
21732
21869
  } catch {
21733
21870
  commandError(`Run not found: ${runId}`, opts);
21734
21871
  return null;
21735
21872
  }
21736
- const manifestPath = path29.join(runDirPath, "walkthrough.json");
21873
+ const manifestPath = path30.join(runDirPath, "walkthrough.json");
21737
21874
  let manifest;
21738
21875
  try {
21739
21876
  manifest = JSON.parse(
21740
- await fs30.readFile(manifestPath, "utf8")
21877
+ await fs31.readFile(manifestPath, "utf8")
21741
21878
  );
21742
21879
  } catch {
21743
21880
  commandError(`No walkthrough.json found for run: ${runId}`, opts);
@@ -21749,8 +21886,8 @@ async function reportAlreadyCompleted(run, opts) {
21749
21886
  const { runId, manifest } = run;
21750
21887
  if (opts?.json) {
21751
21888
  try {
21752
- const raw = await fs30.readFile(
21753
- path29.join(run.runDirPath, "canonical.json"),
21889
+ const raw = await fs31.readFile(
21890
+ path30.join(run.runDirPath, "canonical.json"),
21754
21891
  "utf8"
21755
21892
  );
21756
21893
  console.log(raw.trim());
@@ -21770,18 +21907,18 @@ async function reportAlreadyCompleted(run, opts) {
21770
21907
  return;
21771
21908
  }
21772
21909
  console.log();
21773
- console.log(chalk32.bold(" Run: ") + chalk32.cyan(runId));
21774
- console.log(` status ${chalk32.green("completed")}`);
21910
+ console.log(chalk33.bold(" Run: ") + chalk33.cyan(runId));
21911
+ console.log(` status ${chalk33.green("completed")}`);
21775
21912
  if (manifest.completed_at) {
21776
- console.log(` completed ${chalk32.dim(manifest.completed_at)}`);
21913
+ console.log(` completed ${chalk33.dim(manifest.completed_at)}`);
21777
21914
  }
21778
21915
  const viewUrl = await pushedRunUrl(run.projectRoot, runId);
21779
21916
  if (viewUrl) {
21780
- console.log(` view ${chalk32.underline(viewUrl)}`);
21917
+ console.log(` view ${chalk33.underline(viewUrl)}`);
21781
21918
  }
21782
21919
  console.log();
21783
21920
  console.log(
21784
- chalk32.dim(
21921
+ chalk33.dim(
21785
21922
  " Already completed \u2014 nothing to do. Use --force to reassemble the report."
21786
21923
  )
21787
21924
  );
@@ -21797,13 +21934,13 @@ function reportStrictProvenanceFailure(audit, opts) {
21797
21934
  }
21798
21935
  console.error();
21799
21936
  console.error(
21800
- chalk32.red(` ${symbols.error} Strict provenance audit failed:`)
21937
+ chalk33.red(` ${symbols.error} Strict provenance audit failed:`)
21801
21938
  );
21802
21939
  for (const w of audit.warnings) {
21803
- console.error(chalk32.red(` \u2022 ${w}`));
21940
+ console.error(chalk33.red(` \u2022 ${w}`));
21804
21941
  }
21805
21942
  console.error(
21806
- chalk32.dim(
21943
+ chalk33.dim(
21807
21944
  " Run was flagged as synthetic / scripted. Remove --strict-provenance to complete anyway."
21808
21945
  )
21809
21946
  );
@@ -21844,41 +21981,41 @@ function completeJsonPayload(canonical, extras) {
21844
21981
  function printCompletionSummary(run, completedAt, assembled, extras, provenanceAudit) {
21845
21982
  const { runId, projectRoot, manifest } = run;
21846
21983
  const { canonical } = assembled;
21847
- const count = (n) => n === 0 ? chalk32.dim("0") : chalk32.cyan(String(n));
21848
- console.log(chalk32.bold(" Run: ") + chalk32.cyan(runId));
21849
- console.log(` scenario ${chalk32.dim(manifest.scenario_slug)}`);
21850
- console.log(` url ${chalk32.dim(manifest.target_url)}`);
21984
+ const count = (n) => n === 0 ? chalk33.dim("0") : chalk33.cyan(String(n));
21985
+ console.log(chalk33.bold(" Run: ") + chalk33.cyan(runId));
21986
+ console.log(` scenario ${chalk33.dim(manifest.scenario_slug)}`);
21987
+ console.log(` url ${chalk33.dim(manifest.target_url)}`);
21851
21988
  if (manifest.environment) {
21852
- console.log(` env ${chalk32.dim(manifest.environment)}`);
21989
+ console.log(` env ${chalk33.dim(manifest.environment)}`);
21853
21990
  }
21854
21991
  if (manifest.build) {
21855
- console.log(` build ${chalk32.dim(formatBuild(manifest.build))}`);
21992
+ console.log(` build ${chalk33.dim(formatBuild(manifest.build))}`);
21856
21993
  }
21857
21994
  if (manifest.viewport) {
21858
- console.log(` device ${chalk32.dim(formatViewport(manifest.viewport))}`);
21995
+ console.log(` device ${chalk33.dim(formatViewport(manifest.viewport))}`);
21859
21996
  }
21860
- console.log(` status ${chalk32.green("completed")}`);
21997
+ console.log(` status ${chalk33.green("completed")}`);
21861
21998
  console.log(` findings ${count(canonical.findings.length)}`);
21862
21999
  console.log(` steps ${count(canonical.steps.length)}`);
21863
- console.log(` started ${chalk32.dim(manifest.created_at)}`);
21864
- console.log(` completed ${chalk32.dim(completedAt)}`);
22000
+ console.log(` started ${chalk33.dim(manifest.created_at)}`);
22001
+ console.log(` completed ${chalk33.dim(completedAt)}`);
21865
22002
  const models = manifest.models;
21866
22003
  if (models?.velocity_sec_per_step != null) {
21867
22004
  console.log(
21868
- ` velocity ${chalk32.cyan(`~${models.velocity_sec_per_step}s/step`)} ${chalk32.dim(`(${models.total_duration_sec}s total)`)}`
22005
+ ` velocity ${chalk33.cyan(`~${models.velocity_sec_per_step}s/step`)} ${chalk33.dim(`(${models.total_duration_sec}s total)`)}`
21869
22006
  );
21870
22007
  }
21871
22008
  if (models && (models.walker || models.judge || models.matcher)) {
21872
22009
  const w = models.walker_reported ? `${models.walker} (${models.walker_reported})` : models.walker ?? "fast";
21873
22010
  const j = models.judge_reported ? `${models.judge} (${models.judge_reported})` : models.judge ?? "thorough";
21874
- console.log(` models ${chalk32.dim(`walker: ${w} \xB7 judge: ${j}`)}`);
22011
+ console.log(` models ${chalk33.dim(`walker: ${w} \xB7 judge: ${j}`)}`);
21875
22012
  }
21876
22013
  console.log();
21877
22014
  console.log(
21878
- ` ${chalk32.dim("canonical:")} ${chalk32.dim(path29.relative(projectRoot, assembled.canonicalPath))}`
22015
+ ` ${chalk33.dim("canonical:")} ${chalk33.dim(path30.relative(projectRoot, assembled.canonicalPath))}`
21879
22016
  );
21880
22017
  console.log(
21881
- ` ${chalk32.dim("report:")} ${chalk32.dim(path29.relative(projectRoot, assembled.reportPath))}`
22018
+ ` ${chalk33.dim("report:")} ${chalk33.dim(path30.relative(projectRoot, assembled.reportPath))}`
21882
22019
  );
21883
22020
  if (extras.viewportWarning) {
21884
22021
  console.log();
@@ -21886,7 +22023,7 @@ function printCompletionSummary(run, completedAt, assembled, extras, provenanceA
21886
22023
  }
21887
22024
  if (extras.codeWarning) {
21888
22025
  console.log();
21889
- console.log(` ${chalk32.yellow("!")} ${extras.codeWarning}`);
22026
+ console.log(` ${chalk33.yellow("!")} ${extras.codeWarning}`);
21890
22027
  }
21891
22028
  if (extras.revisit) printRevisit(extras.revisit);
21892
22029
  if (assembled.verification) printVerification(assembled.verification);
@@ -21898,13 +22035,13 @@ function printProvenanceWarnings(audit) {
21898
22035
  if (audit.warnings.length === 0) return;
21899
22036
  console.log();
21900
22037
  console.log(
21901
- ` ${symbols.warning} ${chalk32.yellow("Step provenance audit flagged suspicious timing:")}`
22038
+ ` ${symbols.warning} ${chalk33.yellow("Step provenance audit flagged suspicious timing:")}`
21902
22039
  );
21903
22040
  for (const w of audit.warnings) {
21904
- console.log(` ${chalk32.dim("\u2022")} ${w}`);
22041
+ console.log(` ${chalk33.dim("\u2022")} ${w}`);
21905
22042
  }
21906
22043
  console.log(
21907
- chalk32.dim(
22044
+ chalk33.dim(
21908
22045
  " Run appears to be programmatically batch-generated rather than an interactive walkthrough."
21909
22046
  )
21910
22047
  );
@@ -21936,17 +22073,17 @@ function screenshotFitsViewport(width, expected) {
21936
22073
  }
21937
22074
  async function screenshotSizeWarning(runDirPath, viewport) {
21938
22075
  if (!viewport) return null;
21939
- const dir = path29.join(runDirPath, "screenshots");
22076
+ const dir = path30.join(runDirPath, "screenshots");
21940
22077
  let first;
21941
22078
  try {
21942
- first = (await fs30.readdir(dir)).filter((f) => f.toLowerCase().endsWith(".png")).sort((a, b) => a.localeCompare(b, void 0, { numeric: true }))[0];
22079
+ first = (await fs31.readdir(dir)).filter((f) => f.toLowerCase().endsWith(".png")).sort((a, b) => a.localeCompare(b, void 0, { numeric: true }))[0];
21943
22080
  } catch {
21944
22081
  return null;
21945
22082
  }
21946
22083
  if (!first) return null;
21947
22084
  let size = null;
21948
22085
  try {
21949
- const handle = await fs30.open(path29.join(dir, first), "r");
22086
+ const handle = await fs31.open(path30.join(dir, first), "r");
21950
22087
  try {
21951
22088
  const head = Buffer.alloc(24);
21952
22089
  const { bytesRead } = await handle.read(head, 0, 24, 0);
@@ -21963,7 +22100,7 @@ async function screenshotSizeWarning(runDirPath, viewport) {
21963
22100
  async function readWalkerSummary(runDirPath) {
21964
22101
  try {
21965
22102
  const data = JSON.parse(
21966
- await fs30.readFile(path29.join(runDirPath, "summary.json"), "utf8")
22103
+ await fs31.readFile(path30.join(runDirPath, "summary.json"), "utf8")
21967
22104
  );
21968
22105
  const strings = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string" && x.trim() !== "") : void 0;
21969
22106
  const model = typeof data.model === "string" && data.model.trim() !== "" ? data.model.trim() : void 0;
@@ -22072,7 +22209,7 @@ function validateCanonicalWalkthrough(canonical) {
22072
22209
  async function applyRecordedVerdicts(canonical, runDirPath) {
22073
22210
  try {
22074
22211
  const recorded = JSON.parse(
22075
- await fs30.readFile(path29.join(runDirPath, "verdicts.json"), "utf8")
22212
+ await fs31.readFile(path30.join(runDirPath, "verdicts.json"), "utf8")
22076
22213
  );
22077
22214
  if (Array.isArray(recorded.verdicts)) {
22078
22215
  return applyVerdicts(canonical, recorded);
@@ -22082,7 +22219,7 @@ async function applyRecordedVerdicts(canonical, runDirPath) {
22082
22219
  return canonical;
22083
22220
  }
22084
22221
  async function persistCanonical(runDirPath, canonical) {
22085
- const canonicalPath = path29.join(runDirPath, "canonical.json");
22222
+ const canonicalPath = path30.join(runDirPath, "canonical.json");
22086
22223
  await atomicWriteFile(
22087
22224
  canonicalPath,
22088
22225
  `${JSON.stringify(canonical, null, 2)}
@@ -22091,7 +22228,7 @@ async function persistCanonical(runDirPath, canonical) {
22091
22228
  return canonicalPath;
22092
22229
  }
22093
22230
  async function persistReport(runDirPath, canonical) {
22094
- const reportPath = path29.join(runDirPath, "report.md");
22231
+ const reportPath = path30.join(runDirPath, "report.md");
22095
22232
  await atomicWriteFile(reportPath, renderCanonicalMarkdown(canonical));
22096
22233
  return reportPath;
22097
22234
  }
@@ -22191,17 +22328,17 @@ function placeLabel(p) {
22191
22328
  function printRevisit(r) {
22192
22329
  const missed = r.places.filter((p) => !p.reached);
22193
22330
  console.log();
22194
- const head = ` ${chalk32.bold("Re-check places:")} reached ${r.reached} of ${r.requested}`;
22331
+ const head = ` ${chalk33.bold("Re-check places:")} reached ${r.reached} of ${r.requested}`;
22195
22332
  if (missed.length === 0) {
22196
- console.log(`${head} ${chalk32.green("\u2713")}`);
22333
+ console.log(`${head} ${chalk33.green("\u2713")}`);
22197
22334
  return;
22198
22335
  }
22199
22336
  console.log(`${head} \u2014 not reached:`);
22200
22337
  for (const p of missed) {
22201
- console.log(` ${chalk32.yellow("\u25CB")} ${placeLabel(p)}`);
22338
+ console.log(` ${chalk33.yellow("\u25CB")} ${placeLabel(p)}`);
22202
22339
  }
22203
22340
  console.log(
22204
- chalk32.dim(
22341
+ chalk33.dim(
22205
22342
  " Fixes there stay awaiting a re-run until a walk gets there."
22206
22343
  )
22207
22344
  );
@@ -22209,7 +22346,7 @@ function printRevisit(r) {
22209
22346
  function printVerification(verification) {
22210
22347
  console.log();
22211
22348
  console.log(
22212
- chalk32.bold(" Since this scenario's last run") + chalk32.dim(` (${verification.baseline_run_id.slice(0, 30)}\u2026)`) + chalk32.bold(":")
22349
+ chalk33.bold(" Since this scenario's last run") + chalk33.dim(` (${verification.baseline_run_id.slice(0, 30)}\u2026)`) + chalk33.bold(":")
22213
22350
  );
22214
22351
  if (verification.resolved.length > 0) {
22215
22352
  console.log(
@@ -22218,7 +22355,7 @@ function printVerification(verification) {
22218
22355
  }
22219
22356
  if (verification.not_observed.length > 0) {
22220
22357
  console.log(
22221
- ` ${symbols.pending} ${verification.not_observed.length} not seen this run ${chalk32.dim("(not evidence of a fix)")}`
22358
+ ` ${symbols.pending} ${verification.not_observed.length} not seen this run ${chalk33.dim("(not evidence of a fix)")}`
22222
22359
  );
22223
22360
  }
22224
22361
  if (verification.persists.length > 0) {
@@ -22260,14 +22397,14 @@ __export(match_exports, {
22260
22397
  match: () => match,
22261
22398
  matchRecord: () => matchRecord
22262
22399
  });
22263
- import { promises as fs31 } from "fs";
22264
- import path30 from "path";
22265
- import chalk33 from "chalk";
22400
+ import { promises as fs32 } from "fs";
22401
+ import path31 from "path";
22402
+ import chalk34 from "chalk";
22266
22403
  async function completedManifest2(runDirPath, runId, opts) {
22267
22404
  let manifest;
22268
22405
  try {
22269
22406
  manifest = JSON.parse(
22270
- await fs31.readFile(path30.join(runDirPath, "walkthrough.json"), "utf8")
22407
+ await fs32.readFile(path31.join(runDirPath, "walkthrough.json"), "utf8")
22271
22408
  );
22272
22409
  } catch {
22273
22410
  commandError(`Run not found: ${runId}`, opts);
@@ -22296,12 +22433,12 @@ async function match(runId, opts) {
22296
22433
  );
22297
22434
  const needed = input.current.length > 0 && (input.prior.length > 0 || (input.guidance ?? []).length > 0);
22298
22435
  const prompt = needed ? composeMatchPrompt(input) : null;
22299
- const promptFilePath = opts?.promptFile ?? path30.join(runDirPath, "match_prompt.md");
22436
+ const promptFilePath = opts?.promptFile ?? path31.join(runDirPath, "match_prompt.md");
22300
22437
  if (prompt) {
22301
- await fs31.writeFile(promptFilePath, prompt, "utf8");
22438
+ await fs32.writeFile(promptFilePath, prompt, "utf8");
22302
22439
  } else {
22303
22440
  try {
22304
- await fs31.unlink(promptFilePath);
22441
+ await fs32.unlink(promptFilePath);
22305
22442
  } catch {
22306
22443
  }
22307
22444
  }
@@ -22311,8 +22448,8 @@ async function match(runId, opts) {
22311
22448
  {
22312
22449
  ...input,
22313
22450
  needed,
22314
- prompt_file: prompt ? path30.relative(ctx.projectRoot, promptFilePath) : null,
22315
- prompt_file_abs: prompt ? path30.resolve(promptFilePath) : null,
22451
+ prompt_file: prompt ? path31.relative(ctx.projectRoot, promptFilePath) : null,
22452
+ prompt_file_abs: prompt ? path31.resolve(promptFilePath) : null,
22316
22453
  match_prompt: prompt
22317
22454
  },
22318
22455
  null,
@@ -22323,7 +22460,7 @@ async function match(runId, opts) {
22323
22460
  }
22324
22461
  if (!prompt) {
22325
22462
  console.log(
22326
- chalk33.dim(
22463
+ chalk34.dim(
22327
22464
  " No earlier findings or tester guidance for this scenario to match against \u2014 nothing to do."
22328
22465
  )
22329
22466
  );
@@ -22339,11 +22476,30 @@ async function matchRecord(runId, opts) {
22339
22476
  });
22340
22477
  const manifest = await completedManifest2(runDirPath, runId, opts);
22341
22478
  if (!manifest) return;
22479
+ if (!opts.file && !opts.data) {
22480
+ commandError("Either --file <path> or --data <json> is required", opts);
22481
+ return;
22482
+ }
22342
22483
  let answer;
22343
22484
  try {
22344
- answer = JSON.parse(await fs31.readFile(opts.file, "utf8"));
22345
- } catch {
22346
- commandError(`Cannot read matches file: ${opts.file}`, opts);
22485
+ let raw;
22486
+ if (opts.data) {
22487
+ raw = opts.data;
22488
+ } else if (opts.file === "-") {
22489
+ const chunks = [];
22490
+ for await (const chunk of process.stdin) {
22491
+ chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
22492
+ }
22493
+ raw = Buffer.concat(chunks).toString("utf8");
22494
+ } else {
22495
+ raw = await fs32.readFile(opts.file, "utf8");
22496
+ }
22497
+ answer = JSON.parse(raw);
22498
+ } catch (err) {
22499
+ commandError(
22500
+ `Cannot read matches: ${err instanceof Error ? err.message : String(err)}`,
22501
+ opts
22502
+ );
22347
22503
  return;
22348
22504
  }
22349
22505
  let result;
@@ -22382,17 +22538,17 @@ async function matchRecord(runId, opts) {
22382
22538
  }
22383
22539
  console.log();
22384
22540
  console.log(
22385
- ` ${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`
22541
+ ` ${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`
22386
22542
  );
22387
22543
  if (result.suppressed > 0) {
22388
22544
  console.log(
22389
- ` ${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)`
22545
+ ` ${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)`
22390
22546
  );
22391
22547
  }
22392
22548
  if (verification) printVerification(verification);
22393
22549
  console.log();
22394
22550
  console.log(
22395
- chalk33.dim(" Run `iamu sync` to push the linked findings to the cloud.")
22551
+ chalk34.dim(" Run `iamu sync` to push the linked findings to the cloud.")
22396
22552
  );
22397
22553
  console.log();
22398
22554
  }
@@ -22414,7 +22570,7 @@ __export(prune_exports, {
22414
22570
  prune: () => prune
22415
22571
  });
22416
22572
  import { confirm, isCancel as isCancel5 } from "@clack/prompts";
22417
- import chalk34 from "chalk";
22573
+ import chalk35 from "chalk";
22418
22574
  function formatBytes(bytes) {
22419
22575
  if (bytes <= 0) return "0 B";
22420
22576
  if (bytes < 1024) return `${bytes} B`;
@@ -22460,7 +22616,7 @@ async function prune(opts = {}) {
22460
22616
  });
22461
22617
  if (preview.prunedRuns.length === 0) {
22462
22618
  console.log(
22463
- chalk34.dim(" No runs match the pruning criteria. Nothing to prune.")
22619
+ chalk35.dim(" No runs match the pruning criteria. Nothing to prune.")
22464
22620
  );
22465
22621
  return preview;
22466
22622
  }
@@ -22469,7 +22625,7 @@ async function prune(opts = {}) {
22469
22625
  message: `Prune ${mode} ${preview.prunedRuns.length} run(s) and reclaim ${formatBytes(preview.totalBytesReclaimed)}?`
22470
22626
  });
22471
22627
  if (isCancel5(confirmed) || !confirmed) {
22472
- console.log(chalk34.yellow(" Pruning cancelled."));
22628
+ console.log(chalk35.yellow(" Pruning cancelled."));
22473
22629
  return;
22474
22630
  }
22475
22631
  }
@@ -22480,14 +22636,14 @@ async function prune(opts = {}) {
22480
22636
  }
22481
22637
  if (result.dryRun) {
22482
22638
  console.log(
22483
- chalk34.cyan(
22639
+ chalk35.cyan(
22484
22640
  `
22485
22641
  [DRY RUN] Would prune ${result.prunedRuns.length} of ${result.totalRunsScanned} run(s):`
22486
22642
  )
22487
22643
  );
22488
22644
  } else {
22489
22645
  console.log(
22490
- chalk34.bold(
22646
+ chalk35.bold(
22491
22647
  `
22492
22648
  ${symbols.success} Pruned ${result.prunedRuns.length} of ${result.totalRunsScanned} run(s):`
22493
22649
  )
@@ -22495,18 +22651,18 @@ async function prune(opts = {}) {
22495
22651
  }
22496
22652
  if (result.prunedRuns.length === 0) {
22497
22653
  console.log(
22498
- chalk34.dim(" No runs matched the pruning criteria. Nothing pruned.")
22654
+ chalk35.dim(" No runs matched the pruning criteria. Nothing pruned.")
22499
22655
  );
22500
22656
  } else {
22501
22657
  for (const run of result.prunedRuns) {
22502
22658
  const mode = run.fullyDeleted ? "removed" : "screenshots purged";
22503
22659
  const count = run.screenshotsDeleted > 0 ? `${run.screenshotsDeleted} screenshots, ` : "";
22504
22660
  console.log(
22505
- ` ${chalk34.dim("\u2022")} ${chalk34.bold(run.runId)} ${chalk34.dim(`(${run.scenarioSlug ?? "unknown scenario"})`)}: ${chalk34.yellow(formatBytes(run.bytesReclaimed))} (${count}${mode})`
22661
+ ` ${chalk35.dim("\u2022")} ${chalk35.bold(run.runId)} ${chalk35.dim(`(${run.scenarioSlug ?? "unknown scenario"})`)}: ${chalk35.yellow(formatBytes(run.bytesReclaimed))} (${count}${mode})`
22506
22662
  );
22507
22663
  }
22508
22664
  console.log(
22509
- chalk34.dim(
22665
+ chalk35.dim(
22510
22666
  `
22511
22667
  Total reclaimed: ${formatBytes(result.totalBytesReclaimed)} (${result.totalScreenshotsDeleted} screenshots deleted)
22512
22668
  `
@@ -22529,7 +22685,7 @@ init_context4();
22529
22685
  import { existsSync as existsSync13, readFileSync as readFileSync9 } from "fs";
22530
22686
  import { dirname as dirname5, resolve as resolve3 } from "path";
22531
22687
  import { fileURLToPath as fileURLToPath3 } from "url";
22532
- import chalk37 from "chalk";
22688
+ import chalk38 from "chalk";
22533
22689
  import { Command } from "commander";
22534
22690
 
22535
22691
  // src/commands/register-cloud.ts
@@ -22703,9 +22859,9 @@ function registerProjectCommands(program2) {
22703
22859
  "after",
22704
22860
  "\nExamples:\n $ iamu validate --all\n $ iamu validate --all --fix --dry-run\n $ iamu validate iamu/personas/maria.md"
22705
22861
  ).action(
22706
- async (path31, opts) => {
22862
+ async (path32, opts) => {
22707
22863
  const { validate: validate2 } = await Promise.resolve().then(() => (init_validate(), validate_exports));
22708
- await validate2(path31, opts);
22864
+ await validate2(path32, opts);
22709
22865
  }
22710
22866
  );
22711
22867
  program2.command("update").helpGroup("Project:").description("Update and repair agent skills and prompts to latest version").option(
@@ -22930,6 +23086,15 @@ function registerWalkthroughCommands(program2) {
22930
23086
  ).option(
22931
23087
  "--step <n>",
22932
23088
  "Step whose screenshot shows the finding \u2014 its evidence (step must be recorded first)"
23089
+ ).option(
23090
+ "--description <text>",
23091
+ "Finding narrative description below frontmatter"
23092
+ ).option(
23093
+ "--recommendation <text>",
23094
+ "Concrete recommendation for how to fix or address the finding"
23095
+ ).option(
23096
+ "--body <markdown>",
23097
+ "Complete markdown body (overrides --description and --recommendation)"
22933
23098
  ).option("--json", "Output as JSON").addHelpText(
22934
23099
  "after",
22935
23100
  '\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'
@@ -22940,6 +23105,20 @@ function registerWalkthroughCommands(program2) {
22940
23105
  await newFinding2({ ...opts, run });
22941
23106
  }
22942
23107
  );
23108
+ program2.command("summary [run-id]").helpGroup("Walkthrough:").description(
23109
+ "Record walker summary (what worked, open questions) atomically"
23110
+ ).option("--run <run-id>", "Run ID (or pass as positional argument)").option(
23111
+ "--worked <note...>",
23112
+ "Things that worked well (can be specified multiple times)"
23113
+ ).option(
23114
+ "--question <note...>",
23115
+ "Open questions that arose (can be specified multiple times)"
23116
+ ).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(
23117
+ async (runIdArg, opts) => {
23118
+ const { summaryCommand: summaryCommand2 } = await Promise.resolve().then(() => (init_summary(), summary_exports));
23119
+ await summaryCommand2(runIdArg, opts);
23120
+ }
23121
+ );
22943
23122
  program2.command("complete <run-id>").helpGroup("Walkthrough:").description("Assemble canonical JSON + report from a walkthrough run").option("--json", "Output canonical JSON to stdout").option(
22944
23123
  "--force",
22945
23124
  "Reassemble canonical.json and report.md for an already completed run"
@@ -22965,17 +23144,19 @@ function registerWalkthroughCommands(program2) {
22965
23144
  );
22966
23145
  program2.command("match-record <run-id>").helpGroup("Walkthrough:").description(
22967
23146
  "Record finding matches and re-derive the run's since-last-run delta"
22968
- ).requiredOption("--file <path>", "Path to matches JSON file").option("--json", "Output match results as JSON").action(async (runId, opts) => {
22969
- const { matchRecord: matchRecord2 } = await Promise.resolve().then(() => (init_match(), match_exports));
22970
- await matchRecord2(runId, opts);
22971
- });
23147
+ ).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(
23148
+ async (runId, opts) => {
23149
+ const { matchRecord: matchRecord2 } = await Promise.resolve().then(() => (init_match(), match_exports));
23150
+ await matchRecord2(runId, opts);
23151
+ }
23152
+ );
22972
23153
  program2.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(
22973
23154
  async (runId, opts) => {
22974
23155
  const { evaluate: evaluate2 } = await Promise.resolve().then(() => (init_evaluate(), evaluate_exports));
22975
23156
  await evaluate2(runId, opts);
22976
23157
  }
22977
23158
  );
22978
- program2.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(
23159
+ program2.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(
22979
23160
  "after",
22980
23161
  "\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"
22981
23162
  ).action(
@@ -23006,23 +23187,23 @@ function registerAllCommands(program2) {
23006
23187
 
23007
23188
  // src/ui/panic.ts
23008
23189
  init_logger();
23009
- import chalk35 from "chalk";
23190
+ import chalk36 from "chalk";
23010
23191
  var handlersRegistered = false;
23011
23192
  var customSigintHandler = null;
23012
23193
  function logFatalAndExit(title, errOrReason) {
23013
23194
  process.stderr.write("\x1B[?25h");
23014
23195
  if (isVerbose()) {
23015
- console.error(chalk35.red(`
23196
+ console.error(chalk36.red(`
23016
23197
  ${title}:`));
23017
23198
  console.error(
23018
23199
  errOrReason instanceof Error ? errOrReason.stack || errOrReason : errOrReason
23019
23200
  );
23020
23201
  } else {
23021
23202
  const msg = errOrReason instanceof Error ? errOrReason.message : String(errOrReason);
23022
- console.error(chalk35.red(`
23203
+ console.error(chalk36.red(`
23023
23204
  Fatal Error: ${msg}`));
23024
23205
  console.error(
23025
- chalk35.dim(
23206
+ chalk36.dim(
23026
23207
  " Run with --verbose or DEBUG=1 to view the full stack trace.\n"
23027
23208
  )
23028
23209
  );
@@ -23041,7 +23222,7 @@ function setupProcessHandlers() {
23041
23222
  }
23042
23223
  process.exit(130);
23043
23224
  } else {
23044
- console.error(chalk35.dim("\n Operation cancelled."));
23225
+ console.error(chalk36.dim("\n Operation cancelled."));
23045
23226
  process.exit(130);
23046
23227
  }
23047
23228
  });
@@ -23067,7 +23248,7 @@ function setupProcessHandlers() {
23067
23248
  import { mkdir as mkdir7, readFile as readFile8, writeFile as writeFile5 } from "fs/promises";
23068
23249
  import { homedir as homedir4 } from "os";
23069
23250
  import { join as join10 } from "path";
23070
- import chalk36 from "chalk";
23251
+ import chalk37 from "chalk";
23071
23252
  var CACHE_FILE = join10(homedir4(), ".iamu", "update-check.json");
23072
23253
  var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
23073
23254
  function isSemverGreater(latest, current) {
@@ -23120,7 +23301,7 @@ async function checkForUpdate(currentVersion) {
23120
23301
  function printUpdateNotice(currentVersion, latestVersion) {
23121
23302
  console.error();
23122
23303
  console.error(
23123
- ` ${chalk36.yellow("Update available:")} ${chalk36.dim(currentVersion)} \u2192 ${chalk36.green(latestVersion)} ${chalk36.dim("(run: npm i -g @iamu-dev/iamu)")}`
23304
+ ` ${chalk37.yellow("Update available:")} ${chalk37.dim(currentVersion)} \u2192 ${chalk37.green(latestVersion)} ${chalk37.dim("(run: npm i -g @iamu-dev/iamu)")}`
23124
23305
  );
23125
23306
  }
23126
23307
 
@@ -23165,7 +23346,7 @@ var program = new Command().name("iamu").description(
23165
23346
  }
23166
23347
  }
23167
23348
  if (root.color === false || process.env.NO_COLOR) {
23168
- chalk37.level = 0;
23349
+ chalk38.level = 0;
23169
23350
  }
23170
23351
  if (root.verbose) {
23171
23352
  process.env.IAMU_VERBOSE = "1";