@iamu-dev/iamu 0.2.0-beta.17 → 0.2.0-beta.19

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
@@ -386,7 +386,7 @@ var init_context = __esm({
386
386
  jurisdiction: z7.string().max(1024).nullish().describe("Country / region the regulations and policies apply in. Example: 'Sweden', 'EU', 'United States'."),
387
387
  architecture: z7.string().max(8192).nullish().describe("High-level architecture notes for the software. Brief is fine \u2014 this is orientation, not spec."),
388
388
  notes: z7.string().max(16384).nullish().describe("Additional orientation context that doesn't fit the structured fields. Extended multi-paragraph notes belong in the file body BELOW the frontmatter, not here."),
389
- access: z7.string().max(4096).nullish().describe(`How a walker becomes a given user in this product: the switching mechanism, written once for the project. Example: 'No login in dev. In the page, run localStorage.setItem("dev_persona", "<persona access>") and reload.' Each persona's own \`access\` names which user they are. Never put passwords or tokens here \u2014 name where they live instead.`),
389
+ access: z7.string().max(4096).nullish().describe("Deprecated: belongs in `iamu/testability.md` `access` \u2014 how to switch user is test setup, not domain. Still read as a fallback when testability.md has none."),
390
390
  governed_by: z7.array(contextGovernedBySchema).max(64).optional().describe("Regulations / standards / policies the domain is governed by, ordered roughly by authority."),
391
391
  actors: z7.array(contextActorSchema).max(64).optional().describe("Key domain roles + their responsibilities."),
392
392
  glossary: z7.array(contextGlossaryEntrySchema).max(512).optional().describe("Domain-specific terms the project uses. Flattened into one file locally; the cloud handler splits into the terminology table on push.")
@@ -927,6 +927,7 @@ var init_testability = __esm({
927
927
  target_url: z17.string().url().describe("Target URL where the product is hosted or served locally."),
928
928
  dev_command: z17.string().max(1e3).optional().describe("Dev script or startup command to boot the app locally."),
929
929
  auth_strategy: authStrategyEnum.or(z17.string().min(1).max(100)).optional().describe("How personas authenticate in test environments."),
930
+ access: z17.string().max(4096).optional().describe(`How a walker becomes a given user in this product: the switching mechanism, written once for the project. Example: 'No login in dev. In the page, run localStorage.setItem("dev_persona", "<persona access>") and reload.' Each persona's own \`access\` names which user they are. Never put passwords or tokens here \u2014 name where they live instead.`),
930
931
  harnesses: z17.array(z17.string().min(1).max(500)).max(64).optional().describe("Mocking frameworks, seed scripts, or test harnesses present."),
931
932
  untestable_boundaries: z17.array(z17.string().min(1).max(1024)).max(128).optional().describe("External services, third-party APIs, or surfaces forbidden to autonomous walkers.")
932
933
  });
@@ -1198,6 +1199,9 @@ var init_walkthrough_bundle = __esm({
1198
1199
  what_worked: z20.array(z20.string().max(4096)).max(64).optional(),
1199
1200
  open_questions: z20.array(z20.string().max(4096)).max(64).optional(),
1200
1201
  verification: bundleVerificationSchema.optional().describe("Since-last-run delta from canonical.json \u2014 which earlier findings persist, were re-checked fixed, were not seen, and which are new. Persists to walkthroughs.verification."),
1202
+ blocked: z20.object({
1203
+ reason: z20.string().min(1).max(2048)
1204
+ }).optional().describe("Set when the walk could not start as set up (missing preconditions)."),
1201
1205
  build: buildMetadataSchema.optional().describe("Build of the product under test (git SHA, branch, CI run). Absent for runs started before `start-run` recorded it."),
1202
1206
  environment: z20.string().min(1).max(ENVIRONMENT_MAX_LENGTH).optional().describe("Where the product ran: `--env <name>` or, by default, the target URL's origin (e.g. `http://localhost:3000`)."),
1203
1207
  /** Holistic UX quality score from UX review pass. Present only when `lenses` includes "ux". */
@@ -1471,7 +1475,12 @@ var init_walkthrough_canonical = __esm({
1471
1475
  human_tests: z21.array(canonicalHumanTestSchema),
1472
1476
  human_test_overall: canonicalHumanTestOverallSchema,
1473
1477
  errors: z21.array(canonicalErrorSchema),
1474
- cloud_mirror_state: cloudMirrorStateSchema
1478
+ cloud_mirror_state: cloudMirrorStateSchema,
1479
+ /**
1480
+ * The walk could not start as set up (the walker's `iamu summary
1481
+ * --blocked`). Its findings say nothing about earlier ones.
1482
+ */
1483
+ blocked: z21.object({ reason: z21.string() }).passthrough().optional()
1475
1484
  }).passthrough();
1476
1485
  }
1477
1486
  });
@@ -6571,6 +6580,8 @@ async function readRunEvidence(projectRoot) {
6571
6580
  continue;
6572
6581
  if (manifest.status !== "completed")
6573
6582
  continue;
6583
+ if (manifest.blocked)
6584
+ continue;
6574
6585
  const canonical = await readJson(path8.join(dir, "canonical.json"));
6575
6586
  const matches = await readMatches(projectRoot, runId);
6576
6587
  const declared = typeof manifest.started_at === "string" ? manifest.started_at : typeof manifest.created_at === "string" ? manifest.created_at : null;
@@ -7386,7 +7397,8 @@ async function buildCanonical(projectRoot, manifest, completedAt, inputs) {
7386
7397
  // renderers + judge agents can surface them without losing signal.
7387
7398
  // Consumers MUST preserve unknown keys (per design D3).
7388
7399
  ...inputs.what_worked && inputs.what_worked.length > 0 ? { what_worked: inputs.what_worked } : {},
7389
- ...inputs.open_questions && inputs.open_questions.length > 0 ? { open_questions: inputs.open_questions } : {}
7400
+ ...inputs.open_questions && inputs.open_questions.length > 0 ? { open_questions: inputs.open_questions } : {},
7401
+ ...manifest.blocked ? { blocked: manifest.blocked } : {}
7390
7402
  };
7391
7403
  }
7392
7404
  async function compareWithBaseline(projectRoot, scenarioSlug, currentRunId, currentFindings) {
@@ -7401,7 +7413,7 @@ async function compareWithBaseline(projectRoot, scenarioSlug, currentRunId, curr
7401
7413
  const mPath = path9.join(runDir(projectRoot, rid), "walkthrough.json");
7402
7414
  const raw = await fs12.readFile(mPath, "utf8");
7403
7415
  const m = JSON.parse(raw);
7404
- if (m.scenario_slug === scenarioSlug && m.status === "completed") {
7416
+ if (m.scenario_slug === scenarioSlug && m.status === "completed" && !m.blocked) {
7405
7417
  baselineRunId = rid;
7406
7418
  baselineManifest = m;
7407
7419
  break;
@@ -7510,6 +7522,13 @@ function renderCanonicalMarkdown(canonical) {
7510
7522
  "",
7511
7523
  `# Walkthrough report: ${canonical.scenario.slug}`,
7512
7524
  "",
7525
+ ...canonical.blocked ? [
7526
+ "> **Blocked \u2014 the walk could not start as it was set up.**",
7527
+ `> ${canonical.blocked.reason}`,
7528
+ ">",
7529
+ "> This run is not compared with earlier runs and proves nothing about earlier findings. Fix the setup (the scenario's starting state or the persona's access) and run it again.",
7530
+ ""
7531
+ ] : [],
7513
7532
  `- Run ID: ${canonical.run_id}`,
7514
7533
  `- Target URL: ${canonical.target.url}`
7515
7534
  ];
@@ -7619,7 +7638,11 @@ function renderCanonicalMarkdown(canonical) {
7619
7638
  for (const ht of canonical.human_tests) {
7620
7639
  lines.push("", `- **${ht.question}**`);
7621
7640
  if (ht.verdict === null) {
7622
- lines.push(` - Verdict: _pending judge_ (walker does not self-grade)`);
7641
+ if (canonical.blocked) {
7642
+ lines.push(` - Verdict: _skipped (walk blocked)_`);
7643
+ } else {
7644
+ lines.push(` - Verdict: _pending judge_ (walker does not self-grade)`);
7645
+ }
7623
7646
  } else {
7624
7647
  lines.push(` - Verdict: **${ht.verdict}**`);
7625
7648
  if (ht.reasoning)
@@ -7629,7 +7652,11 @@ function renderCanonicalMarkdown(canonical) {
7629
7652
  }
7630
7653
  lines.push("", "## Human test overall", "", `> ${canonical.human_test_overall.question}`);
7631
7654
  if (canonical.human_test_overall.answer === null) {
7632
- lines.push("", "_Pending judge stage._");
7655
+ if (canonical.blocked) {
7656
+ lines.push("", "_Skipped (walk blocked)._");
7657
+ } else {
7658
+ lines.push("", "_Pending judge stage._");
7659
+ }
7633
7660
  } else {
7634
7661
  lines.push("", canonical.human_test_overall.answer);
7635
7662
  if (canonical.human_test_overall.reasoning)
@@ -7800,10 +7827,15 @@ async function readCanonicalExtras(projectRoot, runId) {
7800
7827
  const data = JSON.parse(await fs13.readFile(path10.join(runDir(projectRoot, runId), "canonical.json"), "utf-8"));
7801
7828
  const strings = (v) => Array.isArray(v) && v.every((x) => typeof x === "string") ? v : void 0;
7802
7829
  const verification = bundleVerificationSchema.safeParse(data.verification);
7830
+ let blocked;
7831
+ if (typeof data.blocked === "object" && data.blocked !== null && "reason" in data.blocked && typeof data.blocked.reason === "string" && data.blocked.reason.trim().length > 0) {
7832
+ blocked = { reason: data.blocked.reason.trim() };
7833
+ }
7803
7834
  return {
7804
7835
  what_worked: strings(data.what_worked),
7805
7836
  open_questions: strings(data.open_questions),
7806
- verification: verification.success ? verification.data : void 0
7837
+ verification: verification.success ? verification.data : void 0,
7838
+ ...blocked ? { blocked } : {}
7807
7839
  };
7808
7840
  } catch {
7809
7841
  return {};
@@ -7900,6 +7932,10 @@ async function assembleWalkthroughBundle(projectRoot, runId) {
7900
7932
  }
7901
7933
  } : {}
7902
7934
  } : void 0;
7935
+ const canonicalExtras = await readCanonicalExtras(projectRoot, runId);
7936
+ const rawBlocked = manifest.blocked;
7937
+ const manifestBlocked = typeof rawBlocked === "object" && rawBlocked !== null && "reason" in rawBlocked && typeof rawBlocked.reason === "string" && rawBlocked.reason.trim().length > 0 ? { reason: rawBlocked.reason.trim() } : void 0;
7938
+ const blocked = canonicalExtras.blocked ?? manifestBlocked;
7903
7939
  const bundle = {
7904
7940
  manifest: {
7905
7941
  run_id: cloudRunUuid,
@@ -7917,7 +7953,8 @@ async function assembleWalkthroughBundle(projectRoot, runId) {
7917
7953
  judge_meta: judgeMeta,
7918
7954
  ...manifest.models ? { models: manifest.models } : {},
7919
7955
  ...buildFields(manifest),
7920
- ...await readCanonicalExtras(projectRoot, runId)
7956
+ ...canonicalExtras,
7957
+ ...blocked ? { blocked } : {}
7921
7958
  },
7922
7959
  steps: bundleSteps,
7923
7960
  findings: bundleFindings,
@@ -10897,6 +10934,26 @@ function summarizeCapabilityCoverage(surfaces, capabilities, outlines = []) {
10897
10934
  unassigned_surfaces: unassigned
10898
10935
  };
10899
10936
  }
10937
+ function groupUnassignedByPrefix(surfaces) {
10938
+ const groups = /* @__PURE__ */ new Map();
10939
+ for (const s of surfaces) {
10940
+ const pathOnly = s.place.split(/[?#]/)[0] ?? "";
10941
+ const first = pathOnly.split("/").filter(Boolean)[0];
10942
+ const prefix = first ? `/${first}` : "/";
10943
+ let g = groups.get(prefix);
10944
+ if (!g) {
10945
+ g = { prefix, surfaces: 0, reached: 0, examples: [] };
10946
+ groups.set(prefix, g);
10947
+ }
10948
+ g.surfaces += 1;
10949
+ if (s.status === "reached")
10950
+ g.reached += 1;
10951
+ if (g.examples.length < 3 && !g.examples.includes(pathOnly)) {
10952
+ g.examples.push(pathOnly);
10953
+ }
10954
+ }
10955
+ return [...groups.values()].sort((a, b) => b.surfaces - a.surfaces || a.prefix.localeCompare(b.prefix));
10956
+ }
10900
10957
  async function readCapabilityCoverage(projectRoot, coverage) {
10901
10958
  const [capsResult, outlinesResult] = await Promise.all([
10902
10959
  listEntities(projectRoot, "capability"),
@@ -11434,7 +11491,7 @@ function buildPersonaFrontmatter(row, slug) {
11434
11491
  };
11435
11492
  return { frontmatter, body: resolveBody(row, "") };
11436
11493
  }
11437
- function buildScenarioFrontmatter(row, personaSlug, outlineSlug) {
11494
+ function buildScenarioFrontmatter(row, personaSlug, outlineSlug, localFrontmatter) {
11438
11495
  const slug = `${personaSlug}--${outlineSlug}`;
11439
11496
  const frontmatter = {
11440
11497
  id: row.id,
@@ -11445,6 +11502,8 @@ function buildScenarioFrontmatter(row, personaSlug, outlineSlug) {
11445
11502
  // writing null wiped the local value on every pull.
11446
11503
  ..."targetUrl" in row ? { target_url: row.targetUrl ?? null } : {},
11447
11504
  ..."entryPath" in row ? { entry_path: row.entryPath ?? null } : {},
11505
+ ..."reviews" in row && Array.isArray(row.reviews) ? { reviews: row.reviews } : localFrontmatter?.reviews !== void 0 ? { reviews: localFrontmatter.reviews } : {},
11506
+ ..."state" in row && row.state !== void 0 ? { state: row.state ?? null } : localFrontmatter?.state !== void 0 ? { state: localFrontmatter.state } : {},
11448
11507
  baseline_score: row.baselineScore ?? null,
11449
11508
  current_score: row.currentScore ?? null,
11450
11509
  target_score: row.targetScore ?? 95,
@@ -11536,6 +11595,7 @@ async function loadLocalIndex(projectRoot, type2) {
11536
11595
  const byId = /* @__PURE__ */ new Map();
11537
11596
  const bySlug = /* @__PURE__ */ new Map();
11538
11597
  const bodyById = /* @__PURE__ */ new Map();
11598
+ const frontmatterById = /* @__PURE__ */ new Map();
11539
11599
  const [result, baseById, lastPushById] = await Promise.all([
11540
11600
  listEntities(projectRoot, type2),
11541
11601
  readBaseHashes(projectRoot),
@@ -11547,9 +11607,10 @@ async function loadLocalIndex(projectRoot, type2) {
11547
11607
  byId.set(fm.id, fm.slug);
11548
11608
  bySlug.set(fm.slug, fm.id);
11549
11609
  bodyById.set(fm.id, entity.body);
11610
+ frontmatterById.set(fm.id, entity.frontmatter);
11550
11611
  }
11551
11612
  }
11552
- return { byId, bySlug, bodyById, baseById, lastPushById };
11613
+ return { byId, bySlug, bodyById, frontmatterById, baseById, lastPushById };
11553
11614
  }
11554
11615
  async function classifyExisting(projectRoot, type2, cloudId, localSlug, targetSlug, cloudVersion, localIndex) {
11555
11616
  const base = localIndex.baseById[cloudId];
@@ -11890,7 +11951,8 @@ async function pullScenarios(projectRoot, cloud, projectId, outlineIdToSlug, per
11890
11951
  });
11891
11952
  continue;
11892
11953
  }
11893
- const { frontmatter, body, slug } = buildScenarioFrontmatter(row, personaSlug, outlineSlug);
11954
+ const localFm = localIndex.frontmatterById.get(row.id);
11955
+ const { frontmatter, body, slug } = buildScenarioFrontmatter(row, personaSlug, outlineSlug, localFm);
11894
11956
  await applyEntityPullOutcome(projectRoot, "scenario", row, slug, frontmatter, body, localIndex, mode, result.pulls.scenario, batch);
11895
11957
  }
11896
11958
  await finalizePullBatch(projectRoot, batch);
@@ -12329,15 +12391,15 @@ function resolveApiUrl(configured) {
12329
12391
  assertSecureApiUrl(url, configured ? "API URL" : "IAMU_API_URL");
12330
12392
  return url;
12331
12393
  }
12332
- function parseStore(raw, path32) {
12394
+ function parseStore(raw, path34) {
12333
12395
  let parsed;
12334
12396
  try {
12335
12397
  parsed = JSON.parse(raw);
12336
12398
  } catch (err) {
12337
- throw new CredentialsFileError(path32, "not valid JSON", err);
12399
+ throw new CredentialsFileError(path34, "not valid JSON", err);
12338
12400
  }
12339
12401
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
12340
- throw new CredentialsFileError(path32, "expected a JSON object");
12402
+ throw new CredentialsFileError(path34, "expected a JSON object");
12341
12403
  }
12342
12404
  return parsed;
12343
12405
  }
@@ -12345,26 +12407,26 @@ function isNotFound(err) {
12345
12407
  return typeof err === "object" && err !== null && err.code === "ENOENT";
12346
12408
  }
12347
12409
  async function readCredentialsStore() {
12348
- const path32 = getCredentialsPath();
12410
+ const path34 = getCredentialsPath();
12349
12411
  let raw;
12350
12412
  try {
12351
- raw = await readFile(path32, "utf-8");
12413
+ raw = await readFile(path34, "utf-8");
12352
12414
  } catch (err) {
12353
12415
  if (isNotFound(err)) return {};
12354
12416
  warnUnreadableStoreOnce(err);
12355
12417
  throw err;
12356
12418
  }
12357
12419
  try {
12358
- return parseStore(raw, path32);
12420
+ return parseStore(raw, path34);
12359
12421
  } catch (err) {
12360
12422
  warnUnreadableStoreOnce(err);
12361
12423
  throw err;
12362
12424
  }
12363
12425
  }
12364
12426
  async function writeCredentialsStore(store) {
12365
- const path32 = getCredentialsPath();
12366
- await mkdir(dirname(path32), { recursive: true, mode: 448 });
12367
- await atomicWriteFile(path32, `${JSON.stringify(store, null, 2)}
12427
+ const path34 = getCredentialsPath();
12428
+ await mkdir(dirname(path34), { recursive: true, mode: 448 });
12429
+ await atomicWriteFile(path34, `${JSON.stringify(store, null, 2)}
12368
12430
  `, {
12369
12431
  mode: 384
12370
12432
  });
@@ -12384,16 +12446,16 @@ function resolveApiKey(apiUrl) {
12384
12446
  } catch {
12385
12447
  return null;
12386
12448
  }
12387
- const path32 = getCredentialsPath();
12449
+ const path34 = getCredentialsPath();
12388
12450
  let raw;
12389
12451
  try {
12390
- raw = readFileSync(path32, "utf-8");
12452
+ raw = readFileSync(path34, "utf-8");
12391
12453
  } catch (err) {
12392
12454
  if (!isNotFound(err)) warnUnreadableStoreOnce(err);
12393
12455
  return null;
12394
12456
  }
12395
12457
  try {
12396
- const entry = parseStore(raw, path32)[url];
12458
+ const entry = parseStore(raw, path34)[url];
12397
12459
  if (typeof entry !== "object" || entry === null) return null;
12398
12460
  const token = entry.token;
12399
12461
  return typeof token === "string" && token ? token : null;
@@ -12432,12 +12494,12 @@ var init_credentials = __esm({
12432
12494
  * @param reason - What is wrong with it.
12433
12495
  * @param cause - Underlying parse error, if any.
12434
12496
  */
12435
- constructor(path32, reason, cause) {
12497
+ constructor(path34, reason, cause) {
12436
12498
  super(
12437
- `Credentials file ${path32} is corrupt (${reason}). Fix it by hand, or delete it and run \`iamu auth login\` again.`,
12499
+ `Credentials file ${path34} is corrupt (${reason}). Fix it by hand, or delete it and run \`iamu auth login\` again.`,
12438
12500
  {
12439
12501
  cause,
12440
- suggestedAction: `Delete ${path32} and run \`iamu auth login\`, or set IAMU_TOKEN.`
12502
+ suggestedAction: `Delete ${path34} and run \`iamu auth login\`, or set IAMU_TOKEN.`
12441
12503
  }
12442
12504
  );
12443
12505
  }
@@ -13210,8 +13272,8 @@ import { dirname as dirname3, join as join4, resolve as resolve2 } from "path";
13210
13272
  import { fileURLToPath as fileURLToPath2 } from "url";
13211
13273
  import chalk6 from "chalk";
13212
13274
  function getVersion() {
13213
- if ("0.2.0-beta.17") {
13214
- return "0.2.0-beta.17";
13275
+ if ("0.2.0-beta.19") {
13276
+ return "0.2.0-beta.19";
13215
13277
  }
13216
13278
  try {
13217
13279
  const thisDir = dirname3(fileURLToPath2(import.meta.url));
@@ -13240,6 +13302,7 @@ function loadSkillTemplates(version2) {
13240
13302
  "iamu-setup.md",
13241
13303
  "iamu-propose.md",
13242
13304
  "iamu-new.md",
13305
+ "iamu-stage.md",
13243
13306
  "iamu-run.md",
13244
13307
  "iamu-findings.md",
13245
13308
  "iamu-status.md"
@@ -13317,16 +13380,16 @@ async function needsWrite(filePath, content) {
13317
13380
  return true;
13318
13381
  }
13319
13382
  }
13320
- function isCliWrittenSkill(path32) {
13383
+ function isCliWrittenSkill(path34) {
13321
13384
  try {
13322
- return /^generatedBy:/m.test(readFileSync3(path32, "utf-8"));
13385
+ return /^generatedBy:/m.test(readFileSync3(path34, "utf-8"));
13323
13386
  } catch {
13324
13387
  return false;
13325
13388
  }
13326
13389
  }
13327
- function isCliWrittenPrompt(path32) {
13390
+ function isCliWrittenPrompt(path34) {
13328
13391
  try {
13329
- const content = readFileSync3(path32, "utf-8");
13392
+ const content = readFileSync3(path34, "utf-8");
13330
13393
  return content.includes("iamu") || /^generatedBy:/m.test(content);
13331
13394
  } catch {
13332
13395
  return false;
@@ -13338,8 +13401,8 @@ function planSkillFiles(hosts, projectRoot, version2) {
13338
13401
  for (const host of hosts) {
13339
13402
  if (pluginProvidesSkills(host, projectRoot)) {
13340
13403
  for (const name of skillTemplates.keys()) {
13341
- const path32 = join4(host.getSkillsDir(projectRoot), name, "SKILL.md");
13342
- if (isCliWrittenSkill(path32)) files.push({ host, path: path32, content: null });
13404
+ const path34 = join4(host.getSkillsDir(projectRoot), name, "SKILL.md");
13405
+ if (isCliWrittenSkill(path34)) files.push({ host, path: path34, content: null });
13343
13406
  }
13344
13407
  } else if (host.supportsSkills) {
13345
13408
  const skillsDir = host.getSkillsDir(projectRoot);
@@ -18830,6 +18893,22 @@ async function checkProjectTestabilityContract(projectRoot) {
18830
18893
  status: "ok",
18831
18894
  message: `Configured (${details.join(" \xB7 ") || "present"})`
18832
18895
  });
18896
+ if (!frontmatter.access) {
18897
+ let contextAccess = null;
18898
+ try {
18899
+ contextAccess = (await readContext(projectRoot))?.frontmatter.access ?? null;
18900
+ } catch {
18901
+ }
18902
+ if (contextAccess) {
18903
+ checks.push({
18904
+ category: "target",
18905
+ name: "User switching (access)",
18906
+ status: "warn",
18907
+ message: "How to switch user is in iamu/context.yml, which holds the domain. Walks still read it there.",
18908
+ remedy: "Move the `access:` field from iamu/context.yml to the iamu/testability.md frontmatter."
18909
+ });
18910
+ }
18911
+ }
18833
18912
  return { checks, targetUrl };
18834
18913
  } catch (err) {
18835
18914
  checks.push({
@@ -19382,6 +19461,7 @@ var init_list = __esm({
19382
19461
  // src/commands/validate.ts
19383
19462
  var validate_exports = {};
19384
19463
  __export(validate_exports, {
19464
+ schemaByType: () => schemaByType,
19385
19465
  validate: () => validate
19386
19466
  });
19387
19467
  import { randomUUID as randomUUID8 } from "crypto";
@@ -20056,6 +20136,413 @@ var init_new_entity = __esm({
20056
20136
  }
20057
20137
  });
20058
20138
 
20139
+ // src/commands/set-entity.ts
20140
+ var set_entity_exports = {};
20141
+ __export(set_entity_exports, {
20142
+ setEntity: () => setEntity
20143
+ });
20144
+ import { promises as fs28 } from "fs";
20145
+ import path26 from "path";
20146
+ import chalk27 from "chalk";
20147
+ import { ZodError as ZodError2 } from "zod";
20148
+ function given(opts, key) {
20149
+ const v = opts[key];
20150
+ if (Array.isArray(v)) return v.length > 0;
20151
+ if (key === "state") return v === false;
20152
+ return v !== void 0;
20153
+ }
20154
+ function splitList(raw) {
20155
+ return raw.split(",").map((s) => s.trim()).filter(Boolean);
20156
+ }
20157
+ function applyChanges(type2, fm, opts) {
20158
+ const changed = [];
20159
+ const put = (key, value) => {
20160
+ if (value === null) delete fm[key];
20161
+ else fm[key] = value;
20162
+ changed.push(key);
20163
+ };
20164
+ if (type2 === "scenario") {
20165
+ if (opts.entry !== void 0) put("entry_path", opts.entry || null);
20166
+ if (opts.target !== void 0) put("target_url", opts.target || null);
20167
+ if (opts.state === false) {
20168
+ put("state", null);
20169
+ } else if (opts.stateDescription !== void 0 || opts.stateApproximation !== void 0) {
20170
+ const prev = fm.state ?? {};
20171
+ const next = { ...prev };
20172
+ if (opts.stateDescription !== void 0) {
20173
+ if (opts.stateDescription.trim() === "") delete next.description;
20174
+ else next.description = opts.stateDescription;
20175
+ }
20176
+ if (opts.stateApproximation !== void 0) {
20177
+ if (opts.stateApproximation.trim() === "") delete next.approximation;
20178
+ else next.approximation = opts.stateApproximation;
20179
+ }
20180
+ if (Object.keys(next).length === 0) {
20181
+ put("state", null);
20182
+ } else {
20183
+ put("state", next);
20184
+ }
20185
+ }
20186
+ } else if (type2 === "persona") {
20187
+ if (opts.access !== void 0) put("access", opts.access || null);
20188
+ } else if (type2 === "capability") {
20189
+ if (opts.paths === false) {
20190
+ put("paths", []);
20191
+ } else if (opts.paths !== void 0 || given(opts, "addPath") || given(opts, "removePath")) {
20192
+ let paths = typeof opts.paths === "string" ? splitList(opts.paths) : Array.isArray(fm.paths) ? fm.paths : [];
20193
+ for (const p of opts.addPath ?? []) {
20194
+ if (!paths.includes(p)) paths = [...paths, p];
20195
+ }
20196
+ const drop = new Set(opts.removePath ?? []);
20197
+ paths = paths.filter((p) => !drop.has(p));
20198
+ put("paths", paths);
20199
+ }
20200
+ if (opts.area !== void 0) put("area", opts.area || null);
20201
+ }
20202
+ return changed;
20203
+ }
20204
+ async function setEntity(type2, slug, opts) {
20205
+ const ctx = await resolveProject(opts);
20206
+ if (!ctx) return;
20207
+ const { projectRoot } = ctx;
20208
+ const allowed = SETTABLE[type2];
20209
+ if (!allowed) {
20210
+ commandError(
20211
+ `Cannot set fields on "${type2}". Supported: ${Object.keys(SETTABLE).join(", ")}. What a scenario is about (narrative, tension, human tests, brief) is edited in its file, not here.`,
20212
+ opts
20213
+ );
20214
+ return;
20215
+ }
20216
+ const foreign = Object.keys(FLAG_NAMES).filter(
20217
+ (k) => !allowed.includes(k) && given(opts, k)
20218
+ );
20219
+ if (foreign.length > 0) {
20220
+ commandError(
20221
+ `${foreign.map((k) => FLAG_NAMES[k]).join(", ")} ${foreign.length === 1 ? "does" : "do"} not apply to a ${type2}. ${type2} takes: ${allowed.map((k) => FLAG_NAMES[k]).join(", ")}.`,
20222
+ opts
20223
+ );
20224
+ return;
20225
+ }
20226
+ const effectiveOpts = { ...opts };
20227
+ if (opts.file) {
20228
+ let raw;
20229
+ try {
20230
+ if (opts.file === "-") {
20231
+ const chunks = [];
20232
+ for await (const chunk of process.stdin) {
20233
+ chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
20234
+ }
20235
+ raw = Buffer.concat(chunks).toString("utf8");
20236
+ } else {
20237
+ raw = await fs28.readFile(path26.resolve(projectRoot, opts.file), "utf8");
20238
+ }
20239
+ } catch (err) {
20240
+ commandError(
20241
+ `Could not read patch file: ${err instanceof Error ? err.message : String(err)}`,
20242
+ opts
20243
+ );
20244
+ return;
20245
+ }
20246
+ let parsed;
20247
+ try {
20248
+ parsed = JSON.parse(raw);
20249
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
20250
+ throw new Error("Patch must be a JSON object");
20251
+ }
20252
+ } catch (err) {
20253
+ commandError(
20254
+ `Failed to parse patch JSON: ${err instanceof Error ? err.message : String(err)}`,
20255
+ opts
20256
+ );
20257
+ return;
20258
+ }
20259
+ const allowedPatchKeys = {
20260
+ scenario: ["entry", "entry_path", "target", "target_url", "state"],
20261
+ persona: ["access"],
20262
+ capability: ["paths", "addPath", "removePath", "area"]
20263
+ };
20264
+ const foreignPatchKeys = Object.keys(parsed).filter(
20265
+ (k) => !allowedPatchKeys[type2]?.includes(k)
20266
+ );
20267
+ if (foreignPatchKeys.length > 0) {
20268
+ commandError(
20269
+ `Cannot set authoring fields on ${type2} via staging patch: ${foreignPatchKeys.join(", ")}. What an entity is about (narrative, tension, human tests, brief) is edited in its file, not through staging.`,
20270
+ opts
20271
+ );
20272
+ return;
20273
+ }
20274
+ if (type2 === "scenario") {
20275
+ if (effectiveOpts.entry === void 0 && (parsed.entry !== void 0 || parsed.entry_path !== void 0)) {
20276
+ const v = parsed.entry ?? parsed.entry_path;
20277
+ effectiveOpts.entry = v === null || v === false ? false : String(v);
20278
+ }
20279
+ if (effectiveOpts.target === void 0 && (parsed.target !== void 0 || parsed.target_url !== void 0)) {
20280
+ const v = parsed.target ?? parsed.target_url;
20281
+ effectiveOpts.target = v === null || v === false ? false : String(v);
20282
+ }
20283
+ if (parsed.state !== void 0) {
20284
+ if (parsed.state === null || parsed.state === false) {
20285
+ if (effectiveOpts.state === void 0) effectiveOpts.state = false;
20286
+ } else if (typeof parsed.state === "object") {
20287
+ const s = parsed.state;
20288
+ if (effectiveOpts.stateDescription === void 0 && s.description !== void 0) {
20289
+ effectiveOpts.stateDescription = s.description === null ? "" : String(s.description);
20290
+ }
20291
+ if (effectiveOpts.stateApproximation === void 0 && s.approximation !== void 0) {
20292
+ effectiveOpts.stateApproximation = s.approximation === null ? "" : String(s.approximation);
20293
+ }
20294
+ }
20295
+ }
20296
+ } else if (type2 === "persona") {
20297
+ if (effectiveOpts.access === void 0 && parsed.access !== void 0) {
20298
+ effectiveOpts.access = parsed.access === null || parsed.access === false ? false : String(parsed.access);
20299
+ }
20300
+ } else if (type2 === "capability") {
20301
+ if (effectiveOpts.paths === void 0 && parsed.paths !== void 0) {
20302
+ if (parsed.paths === null || parsed.paths === false) {
20303
+ effectiveOpts.paths = false;
20304
+ } else if (Array.isArray(parsed.paths)) {
20305
+ effectiveOpts.paths = parsed.paths.length === 0 ? false : parsed.paths.join(",");
20306
+ }
20307
+ }
20308
+ if (effectiveOpts.addPath === void 0 && Array.isArray(parsed.addPath)) {
20309
+ effectiveOpts.addPath = parsed.addPath.map(String);
20310
+ }
20311
+ if (effectiveOpts.removePath === void 0 && Array.isArray(parsed.removePath)) {
20312
+ effectiveOpts.removePath = parsed.removePath.map(String);
20313
+ }
20314
+ if (effectiveOpts.area === void 0 && parsed.area !== void 0) {
20315
+ effectiveOpts.area = parsed.area === null || parsed.area === false ? false : String(parsed.area);
20316
+ }
20317
+ }
20318
+ }
20319
+ if (!allowed.some((k) => given(effectiveOpts, k))) {
20320
+ commandError(
20321
+ `Nothing to set. ${type2} takes: ${allowed.map((k) => FLAG_NAMES[k]).join(", ")}, or --file <path>.`,
20322
+ opts
20323
+ );
20324
+ return;
20325
+ }
20326
+ if (type2 === "capability") {
20327
+ const candidatePaths = [
20328
+ ...typeof effectiveOpts.paths === "string" ? splitList(effectiveOpts.paths) : [],
20329
+ ...effectiveOpts.addPath ?? []
20330
+ ];
20331
+ const invalid = candidatePaths.filter((p) => !p.startsWith("/"));
20332
+ if (invalid.length > 0) {
20333
+ commandError(
20334
+ `Route prefixes must start with "/": ${invalid.join(", ")}`,
20335
+ opts
20336
+ );
20337
+ return;
20338
+ }
20339
+ }
20340
+ const entity = await safeReadEntity(
20341
+ projectRoot,
20342
+ type2,
20343
+ slug,
20344
+ opts
20345
+ );
20346
+ if (!entity) return;
20347
+ const fm = { ...entity.frontmatter };
20348
+ const changed = applyChanges(type2, fm, effectiveOpts);
20349
+ let writtenPath;
20350
+ try {
20351
+ const record = await writeEntity(
20352
+ projectRoot,
20353
+ type2,
20354
+ fm,
20355
+ entity.body
20356
+ );
20357
+ writtenPath = record.path;
20358
+ } catch (err) {
20359
+ const message = err instanceof ZodError2 ? err.issues.map((i) => `${i.path.join(".") || "root"}: ${i.message}`).join("; ") : err instanceof Error ? err.message : String(err);
20360
+ commandError(`Could not set ${type2} "${slug}": ${message}`, opts);
20361
+ return;
20362
+ }
20363
+ const entitySlug = String(fm.slug);
20364
+ if (opts.json) {
20365
+ console.log(
20366
+ JSON.stringify({ slug: entitySlug, type: type2, path: writtenPath, changed })
20367
+ );
20368
+ return;
20369
+ }
20370
+ console.log();
20371
+ console.log(
20372
+ ` ${symbols.success} Updated ${type2} ${chalk27.cyan(entitySlug)}: ${changed.join(", ")}`
20373
+ );
20374
+ console.log(` ${chalk27.dim("path:")} ${writtenPath}`);
20375
+ console.log();
20376
+ }
20377
+ var SETTABLE, FLAG_NAMES;
20378
+ var init_set_entity = __esm({
20379
+ "src/commands/set-entity.ts"() {
20380
+ "use strict";
20381
+ init_dist2();
20382
+ init_symbols();
20383
+ init_context4();
20384
+ SETTABLE = {
20385
+ scenario: [
20386
+ "entry",
20387
+ "target",
20388
+ "stateDescription",
20389
+ "stateApproximation",
20390
+ "state"
20391
+ ],
20392
+ persona: ["access"],
20393
+ capability: ["paths", "addPath", "removePath", "area"]
20394
+ };
20395
+ FLAG_NAMES = {
20396
+ entry: "--entry",
20397
+ target: "--target",
20398
+ stateDescription: "--state-description",
20399
+ stateApproximation: "--state-approximation",
20400
+ state: "--no-state",
20401
+ access: "--access",
20402
+ paths: "--paths",
20403
+ addPath: "--add-path",
20404
+ removePath: "--remove-path",
20405
+ area: "--area"
20406
+ };
20407
+ }
20408
+ });
20409
+
20410
+ // src/commands/edit-entity.ts
20411
+ var edit_entity_exports = {};
20412
+ __export(edit_entity_exports, {
20413
+ editEntity: () => editEntity
20414
+ });
20415
+ import { spawn as spawn2 } from "child_process";
20416
+ import { promises as fs29 } from "fs";
20417
+ import path27 from "path";
20418
+ import chalk28 from "chalk";
20419
+ async function editEntity(type2, slug, opts = {}) {
20420
+ const ctx = await resolveProject(opts);
20421
+ if (!ctx) return;
20422
+ const { projectRoot } = ctx;
20423
+ if (!ENTITY_TYPES.includes(type2)) {
20424
+ commandError(
20425
+ `Unknown entity type "${type2}". Supported: ${ENTITY_TYPES.join(", ")}.`,
20426
+ opts
20427
+ );
20428
+ return;
20429
+ }
20430
+ const dir = ENTITY_DIRS[type2];
20431
+ const filePath = path27.join(projectRoot, "iamu", dir, `${slug}.md`);
20432
+ try {
20433
+ await fs29.access(filePath);
20434
+ } catch {
20435
+ commandError(
20436
+ `${type2} "${slug}" not found at ${path27.relative(projectRoot, filePath)}.`,
20437
+ opts
20438
+ );
20439
+ return;
20440
+ }
20441
+ if (!process.stdin.isTTY || process.env.CI) {
20442
+ commandError(
20443
+ `Cannot open editor in a non-interactive environment. Edit the file directly: ${path27.relative(projectRoot, filePath)}`,
20444
+ opts
20445
+ );
20446
+ return;
20447
+ }
20448
+ const rawEditor = process.env.VISUAL || process.env.EDITOR || (process.platform === "win32" ? "notepad" : "vi");
20449
+ const parts = rawEditor.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) ?? [
20450
+ rawEditor
20451
+ ];
20452
+ const editorCmd = parts[0].replace(/^["']|["']$/g, "");
20453
+ const editorArgs = parts.slice(1).map((a) => a.replace(/^["']|["']$/g, ""));
20454
+ await new Promise((resolve4) => {
20455
+ const child = spawn2(editorCmd, [...editorArgs, filePath], {
20456
+ stdio: "inherit"
20457
+ });
20458
+ child.on("exit", (code) => {
20459
+ if (code !== 0) {
20460
+ console.error(
20461
+ chalk28.red(
20462
+ `
20463
+ Editor exited with code ${code}. Changes not validated.
20464
+ `
20465
+ )
20466
+ );
20467
+ process.exitCode = code ?? 1;
20468
+ resolve4();
20469
+ } else {
20470
+ resolve4();
20471
+ }
20472
+ });
20473
+ child.on("error", (err) => {
20474
+ commandError(
20475
+ `Failed to launch editor (${rawEditor}): ${err.message}`,
20476
+ opts
20477
+ );
20478
+ resolve4();
20479
+ });
20480
+ });
20481
+ if (process.exitCode) return;
20482
+ try {
20483
+ const content = await fs29.readFile(filePath, "utf8");
20484
+ const { frontmatter } = parseFrontmatter(content);
20485
+ const schema2 = schemaByType[type2];
20486
+ const parsed = schema2.safeParse(frontmatter);
20487
+ if (!parsed.success) {
20488
+ if (opts.json) {
20489
+ console.log(
20490
+ JSON.stringify({
20491
+ valid: false,
20492
+ type: type2,
20493
+ slug,
20494
+ path: filePath,
20495
+ errors: parsed.error.issues.map((i) => ({
20496
+ field: i.path.join(".") || "root",
20497
+ message: i.message
20498
+ }))
20499
+ })
20500
+ );
20501
+ } else {
20502
+ console.log();
20503
+ console.log(
20504
+ ` ${symbols.warning} ${chalk28.yellow(`Validation failed for ${type2} "${slug}":`)}`
20505
+ );
20506
+ for (const issue of parsed.error.issues) {
20507
+ const field2 = issue.path.join(".") || "root";
20508
+ console.log(` - ${field2}: ${issue.message}`);
20509
+ }
20510
+ console.log();
20511
+ console.log(
20512
+ chalk28.dim(
20513
+ ` Fix the file and run: iamu validate ${path27.relative(projectRoot, filePath)}`
20514
+ )
20515
+ );
20516
+ console.log();
20517
+ }
20518
+ process.exitCode = 1;
20519
+ return;
20520
+ }
20521
+ if (opts.json) {
20522
+ console.log(JSON.stringify({ valid: true, type: type2, slug, path: filePath }));
20523
+ return;
20524
+ }
20525
+ console.log();
20526
+ console.log(` ${symbols.success} Validated ${type2} ${chalk28.cyan(slug)}`);
20527
+ console.log();
20528
+ } catch (err) {
20529
+ commandError(
20530
+ `Failed to validate ${type2} "${slug}" after edit: ${err instanceof Error ? err.message : String(err)}`,
20531
+ opts
20532
+ );
20533
+ }
20534
+ }
20535
+ var init_edit_entity = __esm({
20536
+ "src/commands/edit-entity.ts"() {
20537
+ "use strict";
20538
+ init_dist2();
20539
+ init_dist();
20540
+ init_symbols();
20541
+ init_context4();
20542
+ init_validate();
20543
+ }
20544
+ });
20545
+
20059
20546
  // src/commands/explore-crawl.ts
20060
20547
  function candidateSig(c) {
20061
20548
  return `${c.role}
@@ -20095,6 +20582,9 @@ async function settle(page, settleMs) {
20095
20582
  });
20096
20583
  await page.waitForLoadState("networkidle", { timeout: settleMs }).catch(() => {
20097
20584
  });
20585
+ const spaSettle = Math.min(Math.max(settleMs, 50), 200);
20586
+ await page.waitForTimeout(spaSettle).catch(() => {
20587
+ });
20098
20588
  }
20099
20589
  async function readPage(page) {
20100
20590
  return page.evaluate(READ_PAGE);
@@ -20112,8 +20602,12 @@ async function clickCandidate(page, want) {
20112
20602
  (x) => candidateSig(x) === candidateSig(want)
20113
20603
  );
20114
20604
  if (!c) return false;
20115
- await page.locator(`[data-iamu-x="${c.index}"]`).click({ timeout: CLICK_TIMEOUT_MS });
20116
- return true;
20605
+ try {
20606
+ await page.locator(`[data-iamu-x="${c.index}"]`).click({ timeout: CLICK_TIMEOUT_MS });
20607
+ return true;
20608
+ } catch {
20609
+ return false;
20610
+ }
20117
20611
  }
20118
20612
  async function crawl(page, opts) {
20119
20613
  const origin = originOf(opts.startUrl);
@@ -20170,7 +20664,7 @@ async function crawlFrom(page, opts, origin, takeBlocked) {
20170
20664
  const byKey = /* @__PURE__ */ new Map();
20171
20665
  const byPlaceView = /* @__PURE__ */ new Map();
20172
20666
  const signatureOf = (s) => exploreSignature(s.headings, s.title);
20173
- function addSurface(state, place, view, path32, depth) {
20667
+ function addSurface(state, place, view, path34, depth) {
20174
20668
  const key = exploreSurfaceKey(place, view);
20175
20669
  const surface = {
20176
20670
  key,
@@ -20182,7 +20676,7 @@ async function crawlFrom(page, opts, origin, takeBlocked) {
20182
20676
  headings: state.headings,
20183
20677
  fields: state.fields,
20184
20678
  depth,
20185
- path: path32,
20679
+ path: path34,
20186
20680
  actions: []
20187
20681
  };
20188
20682
  surfaces.push(surface);
@@ -20200,9 +20694,9 @@ ${surface.signature}`, key);
20200
20694
  });
20201
20695
  await settle(page, settleMs);
20202
20696
  }
20203
- async function replay(path32) {
20204
- if (!("goto" in (path32[0] ?? {}))) await goto(opts.startUrl);
20205
- for (const step of path32) {
20697
+ async function replay(path34) {
20698
+ if (!("goto" in (path34[0] ?? {}))) await goto(opts.startUrl);
20699
+ for (const step of path34) {
20206
20700
  if ("goto" in step) {
20207
20701
  await goto(step.goto);
20208
20702
  } else {
@@ -20326,8 +20820,8 @@ ${sig}`, key);
20326
20820
  if (stats.surfaces >= budget.max_surfaces) {
20327
20821
  stats.truncated = true;
20328
20822
  } else {
20329
- const path32 = !sameDoc && view === null ? [{ goto: after.url }] : [...surface.path, ...via, click];
20330
- addSurface(after, place, view, path32, surface.depth + 1);
20823
+ const path34 = !sameDoc && view === null ? [{ goto: after.url }] : [...surface.path, ...via, click];
20824
+ addSurface(after, place, view, path34, surface.depth + 1);
20331
20825
  }
20332
20826
  }
20333
20827
  }
@@ -20527,11 +21021,11 @@ __export(explore_exports, {
20527
21021
  exploreList: () => exploreList,
20528
21022
  exploreRun: () => exploreRun
20529
21023
  });
20530
- import path26 from "path";
20531
- import chalk27 from "chalk";
21024
+ import path28 from "path";
21025
+ import chalk29 from "chalk";
20532
21026
  function fail2(message, json2) {
20533
21027
  if (json2) console.error(JSON.stringify({ error: message }));
20534
- else console.error(chalk27.red(` ${message}`));
21028
+ else console.error(chalk29.red(` ${message}`));
20535
21029
  process.exitCode = 1;
20536
21030
  }
20537
21031
  function positiveInt(raw, fallback, flag) {
@@ -20630,7 +21124,7 @@ async function exploreRun(url, opts) {
20630
21124
  }
20631
21125
  const quiet = opts.json || process.env.IAMU_QUIET === "1";
20632
21126
  if (!quiet) {
20633
- console.error(chalk27.dim(` Exploring ${startUrl} (read-only)\u2026`));
21127
+ console.error(chalk29.dim(` Exploring ${startUrl} (read-only)\u2026`));
20634
21128
  }
20635
21129
  const now = /* @__PURE__ */ new Date();
20636
21130
  let opened = null;
@@ -20641,7 +21135,7 @@ async function exploreRun(url, opts) {
20641
21135
  startUrl,
20642
21136
  budget,
20643
21137
  deny,
20644
- log: quiet ? void 0 : (line) => console.error(chalk27.dim(line))
21138
+ log: quiet ? void 0 : (line) => console.error(chalk29.dim(line))
20645
21139
  });
20646
21140
  } catch (err) {
20647
21141
  fail2(err.message.split("\n")[0], opts.json);
@@ -20661,7 +21155,7 @@ async function exploreRun(url, opts) {
20661
21155
  surfaces: result.surfaces
20662
21156
  };
20663
21157
  const file = await writeExploreMap(ctx.projectRoot, map2);
20664
- const rel = path26.relative(process.cwd(), file);
21158
+ const rel = path28.relative(process.cwd(), file);
20665
21159
  if (opts.json) {
20666
21160
  console.log(JSON.stringify({ id: map2.id, path: rel, stats: map2.stats }));
20667
21161
  return;
@@ -20669,17 +21163,17 @@ async function exploreRun(url, opts) {
20669
21163
  const s = map2.stats;
20670
21164
  console.log(
20671
21165
  `
20672
- ${chalk27.bold(`${s.surfaces} surfaces`)} \xB7 ${s.actions_tried} controls tried \xB7 ${s.skipped} skipped \xB7 ${s.blocked_requests} writes blocked${s.errors ? ` \xB7 ${s.errors} errors` : ""}`
21166
+ ${chalk29.bold(`${s.surfaces} surfaces`)} \xB7 ${s.actions_tried} controls tried \xB7 ${s.skipped} skipped \xB7 ${s.blocked_requests} writes blocked${s.errors ? ` \xB7 ${s.errors} errors` : ""}`
20673
21167
  );
20674
21168
  if (s.truncated) {
20675
21169
  console.log(
20676
- chalk27.yellow(
21170
+ chalk29.yellow(
20677
21171
  " Stopped by the budget before the product ran out \u2014 raise --max-surfaces / --max-actions / --max-depth to go further."
20678
21172
  )
20679
21173
  );
20680
21174
  }
20681
21175
  console.log(` Map: ${rel}`);
20682
- console.log(chalk27.dim(" Next: iamu explore coverage"));
21176
+ console.log(chalk29.dim(" Next: iamu explore coverage"));
20683
21177
  }
20684
21178
  async function resolveMapId(projectRoot, id, offsetFromEnd = 1) {
20685
21179
  if (id && id !== "latest") return id;
@@ -20707,7 +21201,7 @@ async function exploreList(opts) {
20707
21201
  }
20708
21202
  for (const r of rows) {
20709
21203
  console.log(
20710
- ` ${r.id} ${r.surfaces} surfaces ${chalk27.dim(r.target_url)}`
21204
+ ` ${r.id} ${r.surfaces} surfaces ${chalk29.dim(r.target_url)}`
20711
21205
  );
20712
21206
  }
20713
21207
  }
@@ -20734,7 +21228,10 @@ async function exploreCoverageCommand(mapId, opts) {
20734
21228
  ...cov,
20735
21229
  ...capReport.capabilities.length > 0 ? {
20736
21230
  capabilities: capReport.capabilities,
20737
- unassigned_surfaces: capReport.unassigned_surfaces
21231
+ unassigned_surfaces: capReport.unassigned_surfaces,
21232
+ unassigned_prefixes: groupUnassignedByPrefix(
21233
+ capReport.unassigned_surfaces
21234
+ )
20738
21235
  } : {}
20739
21236
  })
20740
21237
  );
@@ -20745,22 +21242,22 @@ async function exploreCoverageCommand(mapId, opts) {
20745
21242
  Map ${id} (${map2.surfaces.length} surfaces) against ${cov.runs} runs, ${cov.steps} steps`
20746
21243
  );
20747
21244
  console.log(
20748
- ` ${chalk27.green(`${cov.reached} reached`)} \xB7 ${chalk27.yellow(`${cov.place_only} URL reached, view unclear`)} \xB7 ${chalk27.red(`${cov.unreached} never reached`)}`
21245
+ ` ${chalk29.green(`${cov.reached} reached`)} \xB7 ${chalk29.yellow(`${cov.place_only} URL reached, view unclear`)} \xB7 ${chalk29.red(`${cov.unreached} never reached`)}`
20749
21246
  );
20750
21247
  if (capReport.capabilities.length > 0) {
20751
- console.log(chalk27.bold("\n CAPABILITY COVERAGE"));
21248
+ console.log(chalk29.bold("\n CAPABILITY COVERAGE"));
20752
21249
  console.log(
20753
- chalk27.dim(
21250
+ chalk29.dim(
20754
21251
  " \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
20755
21252
  )
20756
21253
  );
20757
21254
  console.log(
20758
- chalk27.dim(
21255
+ chalk29.dim(
20759
21256
  ` ${"Capability".padEnd(24)} ${"Surfaces".padStart(8)} ${"Coverage".padStart(8)} ${"Scenarios (1\xB0 / 2\xB0)"}`
20760
21257
  )
20761
21258
  );
20762
21259
  console.log(
20763
- chalk27.dim(
21260
+ chalk29.dim(
20764
21261
  " \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
20765
21262
  )
20766
21263
  );
@@ -20771,8 +21268,8 @@ async function exploreCoverageCommand(mapId, opts) {
20771
21268
  );
20772
21269
  const covStr = `${cap.coverage_pct}%`.padStart(8);
20773
21270
  const scenarios = `${cap.primary_scenarios} / ${cap.secondary_scenarios}`;
20774
- const untestedFlag = cap.primary_scenarios === 0 && cap.unreached_surfaces > 0 ? chalk27.yellow(" (untested)") : "";
20775
- const covColored = cap.coverage_pct === 100 ? chalk27.green(covStr) : cap.coverage_pct === 0 ? chalk27.red(covStr) : chalk27.yellow(covStr);
21271
+ const untestedFlag = cap.primary_scenarios === 0 && cap.unreached_surfaces > 0 ? chalk29.yellow(" (untested)") : "";
21272
+ const covColored = cap.coverage_pct === 100 ? chalk29.green(covStr) : cap.coverage_pct === 0 ? chalk29.red(covStr) : chalk29.yellow(covStr);
20776
21273
  console.log(
20777
21274
  ` ${title} ${surfaces} ${covColored} ${scenarios}${untestedFlag}`
20778
21275
  );
@@ -20782,26 +21279,41 @@ async function exploreCoverageCommand(mapId, opts) {
20782
21279
  const reached = unassigned.filter((s) => s.status === "reached").length;
20783
21280
  const pct = Math.round(reached / unassigned.length * 100);
20784
21281
  console.log(
20785
- chalk27.dim(
21282
+ chalk29.dim(
20786
21283
  ` ${"(Unassigned)".padEnd(24)} ${`${reached}/${unassigned.length}`.padStart(8)} ${`${pct}%`.padStart(8)} -`
20787
21284
  )
20788
21285
  );
20789
21286
  }
20790
21287
  console.log(
20791
- chalk27.dim(
21288
+ chalk29.dim(
20792
21289
  " \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
20793
21290
  )
20794
21291
  );
21292
+ const prefixes = groupUnassignedByPrefix(capReport.unassigned_surfaces);
21293
+ if (prefixes.length > 0) {
21294
+ console.log(chalk29.bold("\n UNASSIGNED PAGES (no capability owns them)"));
21295
+ for (const g of prefixes) {
21296
+ const count = `${g.surfaces} page${g.surfaces === 1 ? "" : "s"} (${g.reached} reached)`;
21297
+ console.log(
21298
+ ` ${g.prefix.padEnd(24)} ${count.padEnd(24)} ${chalk29.dim(`e.g. ${g.examples.join(", ")}`)}`
21299
+ );
21300
+ }
21301
+ console.log(
21302
+ chalk29.dim(
21303
+ ` Give a prefix to the capability it belongs to: iamu set capability <slug> --add-path ${prefixes[0]?.prefix}`
21304
+ )
21305
+ );
21306
+ }
20795
21307
  const hasGaps = capReport.capabilities.some((c) => c.unreached_surfaces > 0) || capReport.unassigned_surfaces.some((s) => s.status !== "reached");
20796
21308
  if (hasGaps) {
20797
- console.log(chalk27.red("\n UNREACHED SURFACES (Gaps)"));
21309
+ console.log(chalk29.red("\n UNREACHED SURFACES (Gaps)"));
20798
21310
  for (const cap of capReport.capabilities) {
20799
21311
  const gaps = cap.surfaces.filter((s) => s.status !== "reached");
20800
21312
  if (gaps.length === 0) continue;
20801
- console.log(` ${chalk27.bold(cap.slug)}:`);
21313
+ console.log(` ${chalk29.bold(cap.slug)}:`);
20802
21314
  for (const s of gaps) {
20803
21315
  console.log(
20804
- ` ${chalk27.red("\u25CB")} ${s.key.padEnd(32)} ${s.label}${chalk27.dim(` (depth ${s.depth})`)}`
21316
+ ` ${chalk29.red("\u25CB")} ${s.key.padEnd(32)} ${s.label}${chalk29.dim(` (depth ${s.depth})`)}`
20805
21317
  );
20806
21318
  }
20807
21319
  }
@@ -20809,23 +21321,23 @@ async function exploreCoverageCommand(mapId, opts) {
20809
21321
  (s) => s.status !== "reached"
20810
21322
  );
20811
21323
  if (unassignedGaps.length > 0) {
20812
- console.log(chalk27.dim(" (Unassigned):"));
21324
+ console.log(chalk29.dim(" (Unassigned):"));
20813
21325
  for (const s of unassignedGaps) {
20814
21326
  console.log(
20815
- ` ${chalk27.dim("\u25CB")} ${s.key.padEnd(32)} ${s.label}${chalk27.dim(` (depth ${s.depth})`)}`
21327
+ ` ${chalk29.dim("\u25CB")} ${s.key.padEnd(32)} ${s.label}${chalk29.dim(` (depth ${s.depth})`)}`
20816
21328
  );
20817
21329
  }
20818
21330
  }
20819
21331
  }
20820
21332
  if (opts.all) {
20821
- console.log(chalk27.green("\n REACHED SURFACES"));
21333
+ console.log(chalk29.green("\n REACHED SURFACES"));
20822
21334
  for (const cap of capReport.capabilities) {
20823
21335
  const reached = cap.surfaces.filter((s) => s.status === "reached");
20824
21336
  if (reached.length === 0) continue;
20825
- console.log(` ${chalk27.bold(cap.slug)}:`);
21337
+ console.log(` ${chalk29.bold(cap.slug)}:`);
20826
21338
  for (const s of reached) {
20827
21339
  console.log(
20828
- ` ${chalk27.green("\u2713")} ${s.key.padEnd(32)} ${s.label}${chalk27.dim(` (depth ${s.depth})`)}`
21340
+ ` ${chalk29.green("\u2713")} ${s.key.padEnd(32)} ${s.label}${chalk29.dim(` (depth ${s.depth})`)}`
20829
21341
  );
20830
21342
  }
20831
21343
  }
@@ -20833,10 +21345,10 @@ async function exploreCoverageCommand(mapId, opts) {
20833
21345
  (s) => s.status === "reached"
20834
21346
  );
20835
21347
  if (unassignedReached.length > 0) {
20836
- console.log(chalk27.dim(" (Unassigned):"));
21348
+ console.log(chalk29.dim(" (Unassigned):"));
20837
21349
  for (const s of unassignedReached) {
20838
21350
  console.log(
20839
- ` ${chalk27.green("\u2713")} ${s.key.padEnd(32)} ${s.label}${chalk27.dim(` (depth ${s.depth})`)}`
21351
+ ` ${chalk29.green("\u2713")} ${s.key.padEnd(32)} ${s.label}${chalk29.dim(` (depth ${s.depth})`)}`
20840
21352
  );
20841
21353
  }
20842
21354
  }
@@ -20848,24 +21360,24 @@ async function exploreCoverageCommand(mapId, opts) {
20848
21360
  ${color(title)}`);
20849
21361
  for (const r of rows) {
20850
21362
  console.log(
20851
- ` ${r.key.padEnd(32)} ${r.label}${chalk27.dim(` (depth ${r.depth})`)}`
21363
+ ` ${r.key.padEnd(32)} ${r.label}${chalk29.dim(` (depth ${r.depth})`)}`
20852
21364
  );
20853
21365
  }
20854
21366
  };
20855
21367
  section(
20856
21368
  "Never reached by a run",
20857
21369
  cov.surfaces.filter((s) => s.status === "unreached"),
20858
- chalk27.red
21370
+ chalk29.red
20859
21371
  );
20860
21372
  section(
20861
21373
  "URL reached, but no step names this view",
20862
21374
  cov.surfaces.filter((s) => s.status === "place_only"),
20863
- chalk27.yellow
21375
+ chalk29.yellow
20864
21376
  );
20865
21377
  }
20866
21378
  if (cov.run_only_places.length > 0) {
20867
21379
  console.log(`
20868
- ${chalk27.cyan("Walked by runs, not in the map")}`);
21380
+ ${chalk29.cyan("Walked by runs, not in the map")}`);
20869
21381
  for (const p of cov.run_only_places) {
20870
21382
  console.log(
20871
21383
  ` ${p.place.padEnd(32)} ${p.steps} steps, ${p.runs.length} runs`
@@ -20874,7 +21386,7 @@ async function exploreCoverageCommand(mapId, opts) {
20874
21386
  }
20875
21387
  if (cov.runs === 0) {
20876
21388
  console.log(
20877
- chalk27.dim(
21389
+ chalk29.dim(
20878
21390
  "\n No runs yet \u2014 every surface counts as never reached. Walk a scenario first (iamu-run)."
20879
21391
  )
20880
21392
  );
@@ -20910,27 +21422,27 @@ async function exploreDiffCommand(a, b, opts) {
20910
21422
  console.log(`
20911
21423
  ${idA} \u2192 ${idB}`);
20912
21424
  console.log(
20913
- ` ${chalk27.bold(`${pct}% of surfaces match`)} (${d.common} shared, ${d.only_a.length} only in the first, ${d.only_b.length} only in the second)`
21425
+ ` ${chalk29.bold(`${pct}% of surfaces match`)} (${d.common} shared, ${d.only_a.length} only in the first, ${d.only_b.length} only in the second)`
20914
21426
  );
20915
21427
  if (d.only_a.length > 0) {
20916
21428
  console.log(`
20917
- ${chalk27.red("Only in the first")}`);
21429
+ ${chalk29.red("Only in the first")}`);
20918
21430
  for (const s of d.only_a) console.log(` ${s.key.padEnd(32)} ${s.label}`);
20919
21431
  }
20920
21432
  if (d.only_b.length > 0) {
20921
21433
  console.log(`
20922
- ${chalk27.green("Only in the second")}`);
21434
+ ${chalk29.green("Only in the second")}`);
20923
21435
  for (const s of d.only_b) console.log(` ${s.key.padEnd(32)} ${s.label}`);
20924
21436
  }
20925
21437
  if (d.changed_actions.length > 0) {
20926
21438
  console.log(
20927
21439
  `
20928
- ${chalk27.yellow(`Controls differ on ${d.changed_actions.length} shared surfaces`)}`
21440
+ ${chalk29.yellow(`Controls differ on ${d.changed_actions.length} shared surfaces`)}`
20929
21441
  );
20930
21442
  for (const c of d.changed_actions) {
20931
21443
  console.log(` ${c.key} ${c.label}`);
20932
- for (const x of c.only_a) console.log(chalk27.red(` - ${x}`));
20933
- for (const x of c.only_b) console.log(chalk27.green(` + ${x}`));
21444
+ for (const x of c.only_a) console.log(chalk29.red(` - ${x}`));
21445
+ for (const x of c.only_b) console.log(chalk29.green(` + ${x}`));
20934
21446
  }
20935
21447
  }
20936
21448
  }
@@ -21144,7 +21656,7 @@ __export(instructions_walk_exports, {
21144
21656
  });
21145
21657
  import { existsSync as existsSync12, readFileSync as readFileSync8 } from "fs";
21146
21658
  import { join as join9 } from "path";
21147
- import chalk28 from "chalk";
21659
+ import chalk30 from "chalk";
21148
21660
  function selfCommand() {
21149
21661
  const q = (p) => /[\s"'$]/.test(p) ? JSON.stringify(p) : p;
21150
21662
  return `${q(process.execPath)} ${q(process.argv[1] ?? "iamu")}`;
@@ -21274,7 +21786,7 @@ function composeWalkerPrompt(p) {
21274
21786
  "Squinting Guardrails:",
21275
21787
  "\u2022 If the exact target record/option is not available, apply the approximation strategy above to pick the closest plausible entity within the domain flow.",
21276
21788
  "\u2022 Squinting does NOT permit drifting to unrelated settings, account profiles, or off-target pages. Stay strictly within the scenario journey.",
21277
- "\u2022 If the system is completely empty or unseeded, and your persona role cannot create data, do NOT report false product bugs about the empty state. Note that preconditions are unseeded/blocked."
21789
+ "\u2022 If the system is completely empty or unseeded, and your persona role cannot create data, do NOT report false product bugs about the empty state. End the walk as blocked (below)."
21278
21790
  ].filter(Boolean) : [],
21279
21791
  ...p.untestableBoundaries && p.untestableBoundaries.length > 0 ? [
21280
21792
  "",
@@ -21310,6 +21822,7 @@ function composeWalkerPrompt(p) {
21310
21822
  `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`,
21311
21823
  "`--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.",
21312
21824
  `Before you finish, record synthesis with: ${iamu} summary ${p.runId} --worked "..." --worked "..." --question "..."`,
21825
+ `If the walk cannot start as it was set up \u2014 you cannot become this persona (the switch or login fails, or you are someone else), the world the situation needs is not there and you cannot create it, or the starting page does not load \u2014 stop immediately and end the walk with: ${iamu} summary ${p.runId} --blocked "<what was missing, as you saw it>". Do NOT record preliminary findings if preconditions are not met. That is about the setup, not the product: when the product has no way to do what you need, that is a finding (--type missing), not a block. Do not record findings about the missing setup itself.`,
21313
21826
  "Execute everything via CLI commands in the shell. Never invoke host Edit or Write tools on files in the workspace.",
21314
21827
  "",
21315
21828
  "Do not read the product's source code, other iamu/ entity files, or earlier runs: you know only what the persona knows.",
@@ -21427,6 +21940,7 @@ async function instructionsWalk(opts) {
21427
21940
  if (!targetUrl && doc.frontmatter.target_url) {
21428
21941
  targetUrl = doc.frontmatter.target_url;
21429
21942
  }
21943
+ if (doc.frontmatter.access) projectAccess = doc.frontmatter.access;
21430
21944
  const fromFm = doc.frontmatter.untestable_boundaries ?? [];
21431
21945
  const fromBody = extractUntestableBoundaries(doc.body);
21432
21946
  untestableBoundaries.push(
@@ -21512,40 +22026,40 @@ async function instructionsWalk(opts) {
21512
22026
  return;
21513
22027
  }
21514
22028
  console.log();
21515
- console.log(chalk28.bold(" Walk instructions"));
21516
- console.log(` ${chalk28.dim("scenario:")} ${sfm.slug}`);
21517
- console.log(` ${chalk28.dim("persona:")} ${pfm.name} (${pfm.role})`);
21518
- console.log(` ${chalk28.dim("outline:")} ${ofm.title}`);
22029
+ console.log(chalk30.bold(" Walk instructions"));
22030
+ console.log(` ${chalk30.dim("scenario:")} ${sfm.slug}`);
22031
+ console.log(` ${chalk30.dim("persona:")} ${pfm.name} (${pfm.role})`);
22032
+ console.log(` ${chalk30.dim("outline:")} ${ofm.title}`);
21519
22033
  if (sfm.state?.description || sfm.state?.approximation) {
21520
22034
  console.log(
21521
- ` ${chalk28.dim("state:")} ${sfm.state.description ? sfm.state.description : sfm.state.approximation ?? ""}`
22035
+ ` ${chalk30.dim("state:")} ${sfm.state.description ? sfm.state.description : sfm.state.approximation ?? ""}`
21522
22036
  );
21523
22037
  }
21524
- if (targetUrl) console.log(` ${chalk28.dim("target:")} ${targetUrl}`);
22038
+ if (targetUrl) console.log(` ${chalk30.dim("target:")} ${targetUrl}`);
21525
22039
  console.log(
21526
- ` ${chalk28.dim("access:")} ${projectAccess || personaAccess ? `${projectAccess ? "project" : ""}${projectAccess && personaAccess ? " + " : ""}${personaAccess ? "persona" : ""}` : "not recorded (walker would run as whoever is signed in)"}`
22040
+ ` ${chalk30.dim("access:")} ${projectAccess || personaAccess ? `${projectAccess ? "project" : ""}${projectAccess && personaAccess ? " + " : ""}${personaAccess ? "persona" : ""}` : "not recorded (walker would run as whoever is signed in)"}`
21527
22041
  );
21528
22042
  console.log(
21529
- ` ${chalk28.dim("dismissed:")} ${dismissedFacts.length} findings to skip`
22043
+ ` ${chalk30.dim("dismissed:")} ${dismissedFacts.length} findings to skip`
21530
22044
  );
21531
22045
  console.log(
21532
- ` ${chalk28.dim("guidance:")} ${guidance.length} tester guidance rule${guidance.length === 1 ? "" : "s"}`
22046
+ ` ${chalk30.dim("guidance:")} ${guidance.length} tester guidance rule${guidance.length === 1 ? "" : "s"}`
21533
22047
  );
21534
22048
  if (untestableBoundaries.length > 0) {
21535
22049
  console.log(
21536
- ` ${chalk28.dim("boundaries:")} ${untestableBoundaries.length} untestable boundaries to avoid`
22050
+ ` ${chalk30.dim("boundaries:")} ${untestableBoundaries.length} untestable boundaries to avoid`
21537
22051
  );
21538
22052
  }
21539
22053
  if (revisit.length > 0) {
21540
22054
  console.log(
21541
- ` ${chalk28.dim("re-check:")} ${revisit.length} place${revisit.length === 1 ? "" : "s"} where fixes await a re-run`
22055
+ ` ${chalk30.dim("re-check:")} ${revisit.length} place${revisit.length === 1 ? "" : "s"} where fixes await a re-run`
21542
22056
  );
21543
22057
  }
21544
22058
  console.log(
21545
- ` ${chalk28.dim("methodology:")} ${methodology ? "loaded" : "missing (run iamu update)"}`
22059
+ ` ${chalk30.dim("methodology:")} ${methodology ? "loaded" : "missing (run iamu update)"}`
21546
22060
  );
21547
22061
  console.log();
21548
- console.log(chalk28.dim(" Use --json to get the full instruction bundle."));
22062
+ console.log(chalk30.dim(" Use --json to get the full instruction bundle."));
21549
22063
  console.log();
21550
22064
  }
21551
22065
  var SCREEN;
@@ -21571,8 +22085,8 @@ __export(new_finding_exports, {
21571
22085
  });
21572
22086
  import { randomUUID as randomUUID10 } from "crypto";
21573
22087
  import { access, readFile as readFile7 } from "fs/promises";
21574
- import path27 from "path";
21575
- import chalk29 from "chalk";
22088
+ import path29 from "path";
22089
+ import chalk31 from "chalk";
21576
22090
  async function newFinding(opts) {
21577
22091
  const ctx = await resolveProject(opts);
21578
22092
  if (!ctx) return;
@@ -21638,7 +22152,7 @@ async function newFinding(opts) {
21638
22152
  }
21639
22153
  try {
21640
22154
  await access(
21641
- path27.join(runDirPath, "steps", `${String(n).padStart(3, "0")}.json`)
22155
+ path29.join(runDirPath, "steps", `${String(n).padStart(3, "0")}.json`)
21642
22156
  );
21643
22157
  } catch {
21644
22158
  commandError(
@@ -21653,7 +22167,7 @@ async function newFinding(opts) {
21653
22167
  if (!scenarioSlug) {
21654
22168
  try {
21655
22169
  const manifestRaw = await readFile7(
21656
- path27.join(runDirPath, "walkthrough.json"),
22170
+ path29.join(runDirPath, "walkthrough.json"),
21657
22171
  "utf8"
21658
22172
  );
21659
22173
  const manifest = JSON.parse(manifestRaw);
@@ -21757,10 +22271,10 @@ async function newFinding(opts) {
21757
22271
  return;
21758
22272
  }
21759
22273
  console.log();
21760
- console.log(` ${symbols.success} Created finding: ${chalk29.cyan(slug)}`);
21761
- console.log(` ${chalk29.dim("path:")} ${record.path}`);
21762
- console.log(` ${chalk29.dim("id:")} ${id}`);
21763
- for (const w of warnings) console.log(` ${chalk29.yellow("!")} ${w}`);
22274
+ console.log(` ${symbols.success} Created finding: ${chalk31.cyan(slug)}`);
22275
+ console.log(` ${chalk31.dim("path:")} ${record.path}`);
22276
+ console.log(` ${chalk31.dim("id:")} ${id}`);
22277
+ for (const w of warnings) console.log(` ${chalk31.yellow("!")} ${w}`);
21764
22278
  console.log();
21765
22279
  } catch (err) {
21766
22280
  commandError(
@@ -21785,9 +22299,13 @@ var summary_exports = {};
21785
22299
  __export(summary_exports, {
21786
22300
  summaryCommand: () => summaryCommand
21787
22301
  });
21788
- import { promises as fs28 } from "fs";
21789
- import path28 from "path";
21790
- import chalk30 from "chalk";
22302
+ import { promises as fs30 } from "fs";
22303
+ import path30 from "path";
22304
+ import chalk32 from "chalk";
22305
+ function blockedFrom(value) {
22306
+ const reason = value && typeof value === "object" ? value.reason : value;
22307
+ return typeof reason === "string" && reason.trim() ? { reason: reason.trim() } : void 0;
22308
+ }
21791
22309
  async function summaryCommand(runIdArg, opts) {
21792
22310
  const ctx = await resolveProject(opts);
21793
22311
  if (!ctx) return;
@@ -21802,6 +22320,13 @@ async function summaryCommand(runIdArg, opts) {
21802
22320
  let whatWorked = [];
21803
22321
  let openQuestions = [];
21804
22322
  let model = opts.model?.trim() || void 0;
22323
+ const summaryPath = path30.join(runDirPath, "summary.json");
22324
+ let blocked;
22325
+ try {
22326
+ const prev = JSON.parse(await fs30.readFile(summaryPath, "utf8"));
22327
+ blocked = blockedFrom(prev.blocked);
22328
+ } catch {
22329
+ }
21805
22330
  if (opts.file) {
21806
22331
  try {
21807
22332
  let raw;
@@ -21812,7 +22337,7 @@ async function summaryCommand(runIdArg, opts) {
21812
22337
  }
21813
22338
  raw = Buffer.concat(chunks).toString("utf8");
21814
22339
  } else {
21815
- raw = await fs28.readFile(path28.resolve(projectRoot, opts.file), "utf8");
22340
+ raw = await fs30.readFile(path30.resolve(projectRoot, opts.file), "utf8");
21816
22341
  }
21817
22342
  const parsed = JSON.parse(raw);
21818
22343
  if (Array.isArray(parsed.what_worked)) {
@@ -21828,6 +22353,7 @@ async function summaryCommand(runIdArg, opts) {
21828
22353
  if (typeof parsed.model === "string" && parsed.model.trim()) {
21829
22354
  model = parsed.model.trim();
21830
22355
  }
22356
+ blocked = blockedFrom(parsed.blocked) ?? blocked;
21831
22357
  } catch (err) {
21832
22358
  commandError(
21833
22359
  `Failed to parse summary file: ${err instanceof Error ? err.message : String(err)}`,
@@ -21849,12 +22375,17 @@ async function summaryCommand(runIdArg, opts) {
21849
22375
  if (cliQuestions.length > 0) {
21850
22376
  openQuestions = [...openQuestions, ...cliQuestions];
21851
22377
  }
22378
+ if (opts.blocked === false) {
22379
+ blocked = void 0;
22380
+ } else {
22381
+ blocked = blockedFrom(opts.blocked) ?? blocked;
22382
+ }
21852
22383
  const data = {
21853
22384
  what_worked: whatWorked,
21854
22385
  open_questions: openQuestions,
21855
- ...model ? { model } : {}
22386
+ ...model ? { model } : {},
22387
+ ...blocked ? { blocked } : {}
21856
22388
  };
21857
- const summaryPath = path28.join(runDirPath, "summary.json");
21858
22389
  await atomicWriteFile(summaryPath, `${JSON.stringify(data, null, 2)}
21859
22390
  `);
21860
22391
  if (opts.json) {
@@ -21862,7 +22393,7 @@ async function summaryCommand(runIdArg, opts) {
21862
22393
  JSON.stringify(
21863
22394
  {
21864
22395
  run_id: runId,
21865
- path: path28.relative(projectRoot, summaryPath),
22396
+ path: path30.relative(projectRoot, summaryPath),
21866
22397
  summary: data
21867
22398
  },
21868
22399
  null,
@@ -21873,16 +22404,19 @@ async function summaryCommand(runIdArg, opts) {
21873
22404
  }
21874
22405
  console.log();
21875
22406
  console.log(
21876
- ` ${symbols.success} Summary recorded for run ${chalk30.cyan(runId)}`
22407
+ ` ${symbols.success} Summary recorded for run ${chalk32.cyan(runId)}`
21877
22408
  );
22409
+ if (data.blocked) {
22410
+ console.log(` ${chalk32.yellow("blocked:")} ${data.blocked.reason}`);
22411
+ }
21878
22412
  if (data.what_worked.length > 0) {
21879
22413
  console.log(
21880
- ` ${chalk30.dim("what worked:")} ${data.what_worked.length} item(s)`
22414
+ ` ${chalk32.dim("what worked:")} ${data.what_worked.length} item(s)`
21881
22415
  );
21882
22416
  }
21883
22417
  if (data.open_questions.length > 0) {
21884
22418
  console.log(
21885
- ` ${chalk30.dim("open questions:")} ${data.open_questions.length} item(s)`
22419
+ ` ${chalk32.dim("open questions:")} ${data.open_questions.length} item(s)`
21886
22420
  );
21887
22421
  }
21888
22422
  console.log();
@@ -21903,7 +22437,7 @@ __export(view_results_exports, {
21903
22437
  promptViewResults: () => promptViewResults
21904
22438
  });
21905
22439
  import { isCancel as isCancel4, select as select4 } from "@clack/prompts";
21906
- import chalk31 from "chalk";
22440
+ import chalk33 from "chalk";
21907
22441
  function launchCloudDashboard(config, runId) {
21908
22442
  const cloudRunId = runId ? toCloudRunUuid(runId) : void 0;
21909
22443
  const url = getCloudDashboardUrl(
@@ -21912,7 +22446,7 @@ function launchCloudDashboard(config, runId) {
21912
22446
  cloudRunId
21913
22447
  );
21914
22448
  console.log();
21915
- console.log(` ${symbols.success} Opening Iamu Cloud: ${chalk31.cyan(url)}
22449
+ console.log(` ${symbols.success} Opening Iamu Cloud: ${chalk33.cyan(url)}
21916
22450
  `);
21917
22451
  openBrowser(url);
21918
22452
  }
@@ -21980,9 +22514,9 @@ __export(evaluate_exports, {
21980
22514
  evaluate: () => evaluate,
21981
22515
  evaluateRecord: () => evaluateRecord
21982
22516
  });
21983
- import { promises as fs29 } from "fs";
21984
- import path29 from "path";
21985
- import chalk32 from "chalk";
22517
+ import { promises as fs31 } from "fs";
22518
+ import path31 from "path";
22519
+ import chalk34 from "chalk";
21986
22520
  import { z as z26 } from "zod";
21987
22521
  function formatIssues(error) {
21988
22522
  return error.issues.map(
@@ -22012,10 +22546,10 @@ async function evaluate(runId, opts) {
22012
22546
  const resolved = await resolveProjectAndRunDir(runId, opts);
22013
22547
  if (!resolved) return;
22014
22548
  const { projectRoot, runDirPath } = resolved;
22015
- const canonicalPath = path29.join(runDirPath, "canonical.json");
22549
+ const canonicalPath = path31.join(runDirPath, "canonical.json");
22016
22550
  let canonical;
22017
22551
  try {
22018
- const raw = await fs29.readFile(canonicalPath, "utf8");
22552
+ const raw = await fs31.readFile(canonicalPath, "utf8");
22019
22553
  canonical = JSON.parse(raw);
22020
22554
  } catch {
22021
22555
  commandError(
@@ -22071,15 +22605,15 @@ async function evaluate(runId, opts) {
22071
22605
  )
22072
22606
  };
22073
22607
  const prompt = composeJudgePrompt(input);
22074
- const promptFilePath = opts?.promptFile ?? path29.join(runDirPath, "judge_prompt.md");
22075
- await fs29.writeFile(promptFilePath, prompt, "utf8");
22608
+ const promptFilePath = opts?.promptFile ?? path31.join(runDirPath, "judge_prompt.md");
22609
+ await fs31.writeFile(promptFilePath, prompt, "utf8");
22076
22610
  if (opts?.json) {
22077
22611
  console.log(
22078
22612
  JSON.stringify(
22079
22613
  {
22080
22614
  ...input,
22081
- prompt_file: path29.relative(projectRoot, promptFilePath),
22082
- prompt_file_abs: path29.resolve(promptFilePath),
22615
+ prompt_file: path31.relative(projectRoot, promptFilePath),
22616
+ prompt_file_abs: path31.resolve(promptFilePath),
22083
22617
  judge_prompt: prompt
22084
22618
  },
22085
22619
  null,
@@ -22096,10 +22630,10 @@ async function evaluateRecord(runId, opts) {
22096
22630
  const { projectRoot, runDirPath } = resolved;
22097
22631
  sendLivePhase(projectRoot, runId, "judging").catch(() => {
22098
22632
  });
22099
- const canonicalPath = path29.join(runDirPath, "canonical.json");
22633
+ const canonicalPath = path31.join(runDirPath, "canonical.json");
22100
22634
  let canonical;
22101
22635
  try {
22102
- const raw = await fs29.readFile(canonicalPath, "utf8");
22636
+ const raw = await fs31.readFile(canonicalPath, "utf8");
22103
22637
  canonical = JSON.parse(raw);
22104
22638
  } catch {
22105
22639
  commandError(`No canonical.json for run: ${runId}`, opts);
@@ -22121,7 +22655,7 @@ async function evaluateRecord(runId, opts) {
22121
22655
  }
22122
22656
  raw = Buffer.concat(chunks).toString("utf8");
22123
22657
  } else {
22124
- raw = await fs29.readFile(opts.file, "utf8");
22658
+ raw = await fs31.readFile(opts.file, "utf8");
22125
22659
  }
22126
22660
  parsedFile = JSON.parse(raw);
22127
22661
  } catch (err) {
@@ -22182,8 +22716,8 @@ ${formatIssues(fileResult.error)}`,
22182
22716
  overall: inputVerdicts.overall
22183
22717
  });
22184
22718
  const reportContent = renderCanonicalMarkdown(canonicalForReport);
22185
- const verdictsPath = path29.join(runDirPath, "verdicts.json");
22186
- const reportPath = path29.join(runDirPath, "report.md");
22719
+ const verdictsPath = path31.join(runDirPath, "verdicts.json");
22720
+ const reportPath = path31.join(runDirPath, "report.md");
22187
22721
  await Promise.all([
22188
22722
  atomicWriteFile(
22189
22723
  verdictsPath,
@@ -22203,8 +22737,8 @@ ${formatIssues(fileResult.error)}`,
22203
22737
  JSON.stringify(
22204
22738
  {
22205
22739
  runId,
22206
- verdictsPath: path29.relative(projectRoot, verdictsPath),
22207
- reportPath: path29.relative(projectRoot, reportPath),
22740
+ verdictsPath: path31.relative(projectRoot, verdictsPath),
22741
+ reportPath: path31.relative(projectRoot, reportPath),
22208
22742
  verdictsCount: validatedVerdicts.length,
22209
22743
  linkedCount: linked.length,
22210
22744
  ...cloudInfo ? { cloud: cloudInfo } : {}
@@ -22217,18 +22751,18 @@ ${formatIssues(fileResult.error)}`,
22217
22751
  }
22218
22752
  console.log();
22219
22753
  console.log(
22220
- ` ${symbols.success} Recorded ${validatedVerdicts.length} verdicts for run ${chalk32.cyan(runId)}`
22754
+ ` ${symbols.success} Recorded ${validatedVerdicts.length} verdicts for run ${chalk34.cyan(runId)}`
22221
22755
  );
22222
22756
  if (linked.length > 0) {
22223
22757
  console.log(
22224
- ` ${chalk32.dim("linked:")} ${linked.length} finding${linked.length === 1 ? "" : "s"} to the human test${linked.length === 1 ? "" : "s"} citing ${linked.length === 1 ? "it" : "them"}`
22758
+ ` ${chalk34.dim("linked:")} ${linked.length} finding${linked.length === 1 ? "" : "s"} to the human test${linked.length === 1 ? "" : "s"} citing ${linked.length === 1 ? "it" : "them"}`
22225
22759
  );
22226
22760
  }
22227
22761
  console.log(
22228
- ` ${chalk32.dim("verdicts:")} ${path29.relative(projectRoot, verdictsPath)}`
22762
+ ` ${chalk34.dim("verdicts:")} ${path31.relative(projectRoot, verdictsPath)}`
22229
22763
  );
22230
22764
  console.log(
22231
- ` ${chalk32.dim("report:")} ${path29.relative(projectRoot, reportPath)}`
22765
+ ` ${chalk34.dim("report:")} ${path31.relative(projectRoot, reportPath)}`
22232
22766
  );
22233
22767
  printCloudRunInfo(cloudInfo);
22234
22768
  await promptViewResults(projectRoot, { ...opts, runId });
@@ -22293,9 +22827,9 @@ __export(complete_exports, {
22293
22827
  screenshotSizeWarning: () => screenshotSizeWarning,
22294
22828
  validateCanonicalWalkthrough: () => validateCanonicalWalkthrough
22295
22829
  });
22296
- import { promises as fs30 } from "fs";
22297
- import path30 from "path";
22298
- import chalk33 from "chalk";
22830
+ import { promises as fs32 } from "fs";
22831
+ import path32 from "path";
22832
+ import chalk35 from "chalk";
22299
22833
  async function complete(runId, opts) {
22300
22834
  const run = await loadRun(runId, opts);
22301
22835
  if (!run) return;
@@ -22307,7 +22841,7 @@ async function complete(runId, opts) {
22307
22841
  }
22308
22842
  if (!opts?.json) {
22309
22843
  console.log();
22310
- console.log(chalk33.bold(" Assembling canonical JSON\u2026"));
22844
+ console.log(chalk35.bold(" Assembling canonical JSON\u2026"));
22311
22845
  }
22312
22846
  const provenanceAudit = auditStepProvenance(
22313
22847
  await readStepsForRun(run.projectRoot, runId)
@@ -22320,8 +22854,8 @@ async function complete(runId, opts) {
22320
22854
  const walkerSummary = await readWalkerSummary(run.runDirPath);
22321
22855
  let judgeReported;
22322
22856
  try {
22323
- const verdictsRaw = await fs30.readFile(
22324
- path30.join(run.runDirPath, "verdicts.json"),
22857
+ const verdictsRaw = await fs32.readFile(
22858
+ path32.join(run.runDirPath, "verdicts.json"),
22325
22859
  "utf8"
22326
22860
  );
22327
22861
  const parsed = JSON.parse(verdictsRaw);
@@ -22343,9 +22877,11 @@ async function complete(runId, opts) {
22343
22877
  ...durationSec != null ? { total_duration_sec: durationSec } : {},
22344
22878
  ...velocitySec != null ? { velocity_sec_per_step: velocitySec } : {}
22345
22879
  };
22880
+ const { blocked: _previousBlock, ...manifestRest } = run.manifest;
22346
22881
  run.manifest = {
22347
- ...run.manifest,
22348
- models: updatedModels
22882
+ ...manifestRest,
22883
+ models: updatedModels,
22884
+ ...walkerSummary.blocked ? { blocked: walkerSummary.blocked } : {}
22349
22885
  };
22350
22886
  const assembled = await assembleRun(
22351
22887
  run.projectRoot,
@@ -22373,16 +22909,16 @@ async function loadRun(runId, opts) {
22373
22909
  if (!resolved) return null;
22374
22910
  const { projectRoot, runDirPath } = resolved;
22375
22911
  try {
22376
- await fs30.access(runDirPath);
22912
+ await fs32.access(runDirPath);
22377
22913
  } catch {
22378
22914
  commandError(`Run not found: ${runId}`, opts);
22379
22915
  return null;
22380
22916
  }
22381
- const manifestPath = path30.join(runDirPath, "walkthrough.json");
22917
+ const manifestPath = path32.join(runDirPath, "walkthrough.json");
22382
22918
  let manifest;
22383
22919
  try {
22384
22920
  manifest = JSON.parse(
22385
- await fs30.readFile(manifestPath, "utf8")
22921
+ await fs32.readFile(manifestPath, "utf8")
22386
22922
  );
22387
22923
  } catch {
22388
22924
  commandError(`No walkthrough.json found for run: ${runId}`, opts);
@@ -22394,8 +22930,8 @@ async function reportAlreadyCompleted(run, opts) {
22394
22930
  const { runId, manifest } = run;
22395
22931
  if (opts?.json) {
22396
22932
  try {
22397
- const raw = await fs30.readFile(
22398
- path30.join(run.runDirPath, "canonical.json"),
22933
+ const raw = await fs32.readFile(
22934
+ path32.join(run.runDirPath, "canonical.json"),
22399
22935
  "utf8"
22400
22936
  );
22401
22937
  console.log(raw.trim());
@@ -22415,18 +22951,18 @@ async function reportAlreadyCompleted(run, opts) {
22415
22951
  return;
22416
22952
  }
22417
22953
  console.log();
22418
- console.log(chalk33.bold(" Run: ") + chalk33.cyan(runId));
22419
- console.log(` status ${chalk33.green("completed")}`);
22954
+ console.log(chalk35.bold(" Run: ") + chalk35.cyan(runId));
22955
+ console.log(` status ${chalk35.green("completed")}`);
22420
22956
  if (manifest.completed_at) {
22421
- console.log(` completed ${chalk33.dim(manifest.completed_at)}`);
22957
+ console.log(` completed ${chalk35.dim(manifest.completed_at)}`);
22422
22958
  }
22423
22959
  const viewUrl = await pushedRunUrl(run.projectRoot, runId);
22424
22960
  if (viewUrl) {
22425
- console.log(` view ${chalk33.underline(viewUrl)}`);
22961
+ console.log(` view ${chalk35.underline(viewUrl)}`);
22426
22962
  }
22427
22963
  console.log();
22428
22964
  console.log(
22429
- chalk33.dim(
22965
+ chalk35.dim(
22430
22966
  " Already completed \u2014 nothing to do. Use --force to reassemble the report."
22431
22967
  )
22432
22968
  );
@@ -22442,13 +22978,13 @@ function reportStrictProvenanceFailure(audit, opts) {
22442
22978
  }
22443
22979
  console.error();
22444
22980
  console.error(
22445
- chalk33.red(` ${symbols.error} Strict provenance audit failed:`)
22981
+ chalk35.red(` ${symbols.error} Strict provenance audit failed:`)
22446
22982
  );
22447
22983
  for (const w of audit.warnings) {
22448
- console.error(chalk33.red(` \u2022 ${w}`));
22984
+ console.error(chalk35.red(` \u2022 ${w}`));
22449
22985
  }
22450
22986
  console.error(
22451
- chalk33.dim(
22987
+ chalk35.dim(
22452
22988
  " Run was flagged as synthetic / scripted. Remove --strict-provenance to complete anyway."
22453
22989
  )
22454
22990
  );
@@ -22489,41 +23025,48 @@ function completeJsonPayload(canonical, extras) {
22489
23025
  function printCompletionSummary(run, completedAt, assembled, extras, provenanceAudit) {
22490
23026
  const { runId, projectRoot, manifest } = run;
22491
23027
  const { canonical } = assembled;
22492
- const count = (n) => n === 0 ? chalk33.dim("0") : chalk33.cyan(String(n));
22493
- console.log(chalk33.bold(" Run: ") + chalk33.cyan(runId));
22494
- console.log(` scenario ${chalk33.dim(manifest.scenario_slug)}`);
22495
- console.log(` url ${chalk33.dim(manifest.target_url)}`);
23028
+ const count = (n) => n === 0 ? chalk35.dim("0") : chalk35.cyan(String(n));
23029
+ console.log(chalk35.bold(" Run: ") + chalk35.cyan(runId));
23030
+ console.log(` scenario ${chalk35.dim(manifest.scenario_slug)}`);
23031
+ console.log(` url ${chalk35.dim(manifest.target_url)}`);
22496
23032
  if (manifest.environment) {
22497
- console.log(` env ${chalk33.dim(manifest.environment)}`);
23033
+ console.log(` env ${chalk35.dim(manifest.environment)}`);
22498
23034
  }
22499
23035
  if (manifest.build) {
22500
- console.log(` build ${chalk33.dim(formatBuild(manifest.build))}`);
23036
+ console.log(` build ${chalk35.dim(formatBuild(manifest.build))}`);
22501
23037
  }
22502
23038
  if (manifest.viewport) {
22503
- console.log(` device ${chalk33.dim(formatViewport(manifest.viewport))}`);
23039
+ console.log(` device ${chalk35.dim(formatViewport(manifest.viewport))}`);
23040
+ }
23041
+ if (manifest.blocked) {
23042
+ console.log(
23043
+ ` status ${chalk35.yellow("blocked")} ${chalk35.dim("\u2014 the walk could not start as set up")}`
23044
+ );
23045
+ console.log(` reason ${manifest.blocked.reason}`);
23046
+ } else {
23047
+ console.log(` status ${chalk35.green("completed")}`);
22504
23048
  }
22505
- console.log(` status ${chalk33.green("completed")}`);
22506
23049
  console.log(` findings ${count(canonical.findings.length)}`);
22507
23050
  console.log(` steps ${count(canonical.steps.length)}`);
22508
- console.log(` started ${chalk33.dim(manifest.created_at)}`);
22509
- console.log(` completed ${chalk33.dim(completedAt)}`);
23051
+ console.log(` started ${chalk35.dim(manifest.created_at)}`);
23052
+ console.log(` completed ${chalk35.dim(completedAt)}`);
22510
23053
  const models = manifest.models;
22511
23054
  if (models?.velocity_sec_per_step != null) {
22512
23055
  console.log(
22513
- ` velocity ${chalk33.cyan(`~${models.velocity_sec_per_step}s/step`)} ${chalk33.dim(`(${models.total_duration_sec}s total)`)}`
23056
+ ` velocity ${chalk35.cyan(`~${models.velocity_sec_per_step}s/step`)} ${chalk35.dim(`(${models.total_duration_sec}s total)`)}`
22514
23057
  );
22515
23058
  }
22516
23059
  if (models && (models.walker || models.judge || models.matcher)) {
22517
23060
  const w = models.walker_reported ? `${models.walker} (${models.walker_reported})` : models.walker ?? "fast";
22518
23061
  const j = models.judge_reported ? `${models.judge} (${models.judge_reported})` : models.judge ?? "thorough";
22519
- console.log(` models ${chalk33.dim(`walker: ${w} \xB7 judge: ${j}`)}`);
23062
+ console.log(` models ${chalk35.dim(`walker: ${w} \xB7 judge: ${j}`)}`);
22520
23063
  }
22521
23064
  console.log();
22522
23065
  console.log(
22523
- ` ${chalk33.dim("canonical:")} ${chalk33.dim(path30.relative(projectRoot, assembled.canonicalPath))}`
23066
+ ` ${chalk35.dim("canonical:")} ${chalk35.dim(path32.relative(projectRoot, assembled.canonicalPath))}`
22524
23067
  );
22525
23068
  console.log(
22526
- ` ${chalk33.dim("report:")} ${chalk33.dim(path30.relative(projectRoot, assembled.reportPath))}`
23069
+ ` ${chalk35.dim("report:")} ${chalk35.dim(path32.relative(projectRoot, assembled.reportPath))}`
22527
23070
  );
22528
23071
  if (extras.viewportWarning) {
22529
23072
  console.log();
@@ -22531,25 +23074,36 @@ function printCompletionSummary(run, completedAt, assembled, extras, provenanceA
22531
23074
  }
22532
23075
  if (extras.codeWarning) {
22533
23076
  console.log();
22534
- console.log(` ${chalk33.yellow("!")} ${extras.codeWarning}`);
23077
+ console.log(` ${chalk35.yellow("!")} ${extras.codeWarning}`);
22535
23078
  }
22536
23079
  if (extras.revisit) printRevisit(extras.revisit);
22537
23080
  if (assembled.verification) printVerification(assembled.verification);
22538
23081
  if (assembled.journeyDiff) printJourneyDiff(assembled.journeyDiff);
22539
23082
  printProvenanceWarnings(provenanceAudit);
22540
23083
  printCloudRunInfo(extras.cloudInfo);
23084
+ if (manifest.blocked) {
23085
+ console.log();
23086
+ console.log(
23087
+ ` ${symbols.warning} ${chalk35.yellow("Not compared with earlier runs.")} Fix the setup (iamu set scenario ${manifest.scenario_slug} \u2026, or the persona's access) and run the scenario again.`
23088
+ );
23089
+ if (canonical.findings.length > 0) {
23090
+ console.log(
23091
+ ` ${symbols.warning} ${chalk35.yellow(`Walk recorded ${canonical.findings.length} finding(s) before being blocked.`)} Because setup was incomplete, these findings will not be verified against future runs.`
23092
+ );
23093
+ }
23094
+ }
22541
23095
  }
22542
23096
  function printProvenanceWarnings(audit) {
22543
23097
  if (audit.warnings.length === 0) return;
22544
23098
  console.log();
22545
23099
  console.log(
22546
- ` ${symbols.warning} ${chalk33.yellow("Step provenance audit flagged suspicious timing:")}`
23100
+ ` ${symbols.warning} ${chalk35.yellow("Step provenance audit flagged suspicious timing:")}`
22547
23101
  );
22548
23102
  for (const w of audit.warnings) {
22549
- console.log(` ${chalk33.dim("\u2022")} ${w}`);
23103
+ console.log(` ${chalk35.dim("\u2022")} ${w}`);
22550
23104
  }
22551
23105
  console.log(
22552
- chalk33.dim(
23106
+ chalk35.dim(
22553
23107
  " Run appears to be programmatically batch-generated rather than an interactive walkthrough."
22554
23108
  )
22555
23109
  );
@@ -22581,17 +23135,17 @@ function screenshotFitsViewport(width, expected) {
22581
23135
  }
22582
23136
  async function screenshotSizeWarning(runDirPath, viewport) {
22583
23137
  if (!viewport) return null;
22584
- const dir = path30.join(runDirPath, "screenshots");
23138
+ const dir = path32.join(runDirPath, "screenshots");
22585
23139
  let first;
22586
23140
  try {
22587
- first = (await fs30.readdir(dir)).filter((f) => f.toLowerCase().endsWith(".png")).sort((a, b) => a.localeCompare(b, void 0, { numeric: true }))[0];
23141
+ first = (await fs32.readdir(dir)).filter((f) => f.toLowerCase().endsWith(".png")).sort((a, b) => a.localeCompare(b, void 0, { numeric: true }))[0];
22588
23142
  } catch {
22589
23143
  return null;
22590
23144
  }
22591
23145
  if (!first) return null;
22592
23146
  let size = null;
22593
23147
  try {
22594
- const handle = await fs30.open(path30.join(dir, first), "r");
23148
+ const handle = await fs32.open(path32.join(dir, first), "r");
22595
23149
  try {
22596
23150
  const head = Buffer.alloc(24);
22597
23151
  const { bytesRead } = await handle.read(head, 0, 24, 0);
@@ -22608,14 +23162,16 @@ async function screenshotSizeWarning(runDirPath, viewport) {
22608
23162
  async function readWalkerSummary(runDirPath) {
22609
23163
  try {
22610
23164
  const data = JSON.parse(
22611
- await fs30.readFile(path30.join(runDirPath, "summary.json"), "utf8")
23165
+ await fs32.readFile(path32.join(runDirPath, "summary.json"), "utf8")
22612
23166
  );
22613
23167
  const strings = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string" && x.trim() !== "") : void 0;
22614
23168
  const model = typeof data.model === "string" && data.model.trim() !== "" ? data.model.trim() : void 0;
23169
+ const reason = data.blocked?.reason;
22615
23170
  return {
22616
23171
  what_worked: strings(data.what_worked),
22617
23172
  open_questions: strings(data.open_questions),
22618
- model
23173
+ model,
23174
+ ...typeof reason === "string" && reason.trim() !== "" ? { blocked: { reason: reason.trim() } } : {}
22619
23175
  };
22620
23176
  } catch {
22621
23177
  return {};
@@ -22717,7 +23273,7 @@ function validateCanonicalWalkthrough(canonical) {
22717
23273
  async function applyRecordedVerdicts(canonical, runDirPath) {
22718
23274
  try {
22719
23275
  const recorded = JSON.parse(
22720
- await fs30.readFile(path30.join(runDirPath, "verdicts.json"), "utf8")
23276
+ await fs32.readFile(path32.join(runDirPath, "verdicts.json"), "utf8")
22721
23277
  );
22722
23278
  if (Array.isArray(recorded.verdicts)) {
22723
23279
  return applyVerdicts(canonical, recorded);
@@ -22727,7 +23283,7 @@ async function applyRecordedVerdicts(canonical, runDirPath) {
22727
23283
  return canonical;
22728
23284
  }
22729
23285
  async function persistCanonical(runDirPath, canonical) {
22730
- const canonicalPath = path30.join(runDirPath, "canonical.json");
23286
+ const canonicalPath = path32.join(runDirPath, "canonical.json");
22731
23287
  await atomicWriteFile(
22732
23288
  canonicalPath,
22733
23289
  `${JSON.stringify(canonical, null, 2)}
@@ -22736,7 +23292,7 @@ async function persistCanonical(runDirPath, canonical) {
22736
23292
  return canonicalPath;
22737
23293
  }
22738
23294
  async function persistReport(runDirPath, canonical) {
22739
- const reportPath = path30.join(runDirPath, "report.md");
23295
+ const reportPath = path32.join(runDirPath, "report.md");
22740
23296
  await atomicWriteFile(reportPath, renderCanonicalMarkdown(canonical));
22741
23297
  return reportPath;
22742
23298
  }
@@ -22748,7 +23304,7 @@ async function assembleRun(projectRoot, runId, manifest, completedAt) {
22748
23304
  completedAt,
22749
23305
  await readWalkerSummary(runDirPath)
22750
23306
  );
22751
- const verification = await compareWithBaseline(
23307
+ const verification = manifest.blocked ? null : await compareWithBaseline(
22752
23308
  projectRoot,
22753
23309
  manifest.scenario_slug,
22754
23310
  runId,
@@ -22836,17 +23392,17 @@ function placeLabel(p) {
22836
23392
  function printRevisit(r) {
22837
23393
  const missed = r.places.filter((p) => !p.reached);
22838
23394
  console.log();
22839
- const head = ` ${chalk33.bold("Re-check places:")} reached ${r.reached} of ${r.requested}`;
23395
+ const head = ` ${chalk35.bold("Re-check places:")} reached ${r.reached} of ${r.requested}`;
22840
23396
  if (missed.length === 0) {
22841
- console.log(`${head} ${chalk33.green("\u2713")}`);
23397
+ console.log(`${head} ${chalk35.green("\u2713")}`);
22842
23398
  return;
22843
23399
  }
22844
23400
  console.log(`${head} \u2014 not reached:`);
22845
23401
  for (const p of missed) {
22846
- console.log(` ${chalk33.yellow("\u25CB")} ${placeLabel(p)}`);
23402
+ console.log(` ${chalk35.yellow("\u25CB")} ${placeLabel(p)}`);
22847
23403
  }
22848
23404
  console.log(
22849
- chalk33.dim(
23405
+ chalk35.dim(
22850
23406
  " Fixes there stay awaiting a re-run until a walk gets there."
22851
23407
  )
22852
23408
  );
@@ -22854,7 +23410,7 @@ function printRevisit(r) {
22854
23410
  function printVerification(verification) {
22855
23411
  console.log();
22856
23412
  console.log(
22857
- chalk33.bold(" Since this scenario's last run") + chalk33.dim(` (${verification.baseline_run_id.slice(0, 30)}\u2026)`) + chalk33.bold(":")
23413
+ chalk35.bold(" Since this scenario's last run") + chalk35.dim(` (${verification.baseline_run_id.slice(0, 30)}\u2026)`) + chalk35.bold(":")
22858
23414
  );
22859
23415
  if (verification.resolved.length > 0) {
22860
23416
  console.log(
@@ -22863,7 +23419,7 @@ function printVerification(verification) {
22863
23419
  }
22864
23420
  if (verification.not_observed.length > 0) {
22865
23421
  console.log(
22866
- ` ${symbols.pending} ${verification.not_observed.length} not seen this run ${chalk33.dim("(not evidence of a fix)")}`
23422
+ ` ${symbols.pending} ${verification.not_observed.length} not seen this run ${chalk35.dim("(not evidence of a fix)")}`
22867
23423
  );
22868
23424
  }
22869
23425
  if (verification.persists.length > 0) {
@@ -22905,14 +23461,14 @@ __export(match_exports, {
22905
23461
  match: () => match,
22906
23462
  matchRecord: () => matchRecord
22907
23463
  });
22908
- import { promises as fs31 } from "fs";
22909
- import path31 from "path";
22910
- import chalk34 from "chalk";
23464
+ import { promises as fs33 } from "fs";
23465
+ import path33 from "path";
23466
+ import chalk36 from "chalk";
22911
23467
  async function completedManifest2(runDirPath, runId, opts) {
22912
23468
  let manifest;
22913
23469
  try {
22914
23470
  manifest = JSON.parse(
22915
- await fs31.readFile(path31.join(runDirPath, "walkthrough.json"), "utf8")
23471
+ await fs33.readFile(path33.join(runDirPath, "walkthrough.json"), "utf8")
22916
23472
  );
22917
23473
  } catch {
22918
23474
  commandError(`Run not found: ${runId}`, opts);
@@ -22939,14 +23495,14 @@ async function match(runId, opts) {
22939
23495
  runId,
22940
23496
  manifest.scenario_slug
22941
23497
  );
22942
- const needed = input.current.length > 0 && (input.prior.length > 0 || (input.guidance ?? []).length > 0);
23498
+ const needed = !manifest.blocked && input.current.length > 0 && (input.prior.length > 0 || (input.guidance ?? []).length > 0);
22943
23499
  const prompt = needed ? composeMatchPrompt(input) : null;
22944
- const promptFilePath = opts?.promptFile ?? path31.join(runDirPath, "match_prompt.md");
23500
+ const promptFilePath = opts?.promptFile ?? path33.join(runDirPath, "match_prompt.md");
22945
23501
  if (prompt) {
22946
- await fs31.writeFile(promptFilePath, prompt, "utf8");
23502
+ await fs33.writeFile(promptFilePath, prompt, "utf8");
22947
23503
  } else {
22948
23504
  try {
22949
- await fs31.unlink(promptFilePath);
23505
+ await fs33.unlink(promptFilePath);
22950
23506
  } catch {
22951
23507
  }
22952
23508
  }
@@ -22956,8 +23512,9 @@ async function match(runId, opts) {
22956
23512
  {
22957
23513
  ...input,
22958
23514
  needed,
22959
- prompt_file: prompt ? path31.relative(ctx.projectRoot, promptFilePath) : null,
22960
- prompt_file_abs: prompt ? path31.resolve(promptFilePath) : null,
23515
+ ...manifest.blocked ? { blocked: manifest.blocked } : {},
23516
+ prompt_file: prompt ? path33.relative(ctx.projectRoot, promptFilePath) : null,
23517
+ prompt_file_abs: prompt ? path33.resolve(promptFilePath) : null,
22961
23518
  match_prompt: prompt
22962
23519
  },
22963
23520
  null,
@@ -22968,7 +23525,7 @@ async function match(runId, opts) {
22968
23525
  }
22969
23526
  if (!prompt) {
22970
23527
  console.log(
22971
- chalk34.dim(
23528
+ chalk36.dim(
22972
23529
  " No earlier findings or tester guidance for this scenario to match against \u2014 nothing to do."
22973
23530
  )
22974
23531
  );
@@ -23000,7 +23557,7 @@ async function matchRecord(runId, opts) {
23000
23557
  }
23001
23558
  raw = Buffer.concat(chunks).toString("utf8");
23002
23559
  } else {
23003
- raw = await fs31.readFile(opts.file, "utf8");
23560
+ raw = await fs33.readFile(opts.file, "utf8");
23004
23561
  }
23005
23562
  answer = JSON.parse(raw);
23006
23563
  } catch (err) {
@@ -23046,17 +23603,17 @@ async function matchRecord(runId, opts) {
23046
23603
  }
23047
23604
  console.log();
23048
23605
  console.log(
23049
- ` ${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`
23606
+ ` ${chalk36.green("\u2713")} Matched ${result.linked} finding${result.linked === 1 ? "" : "s"} to earlier ones, ${result.new} first seen, ${result.rechecked_fixed} earlier re-checked fixed`
23050
23607
  );
23051
23608
  if (result.suppressed > 0) {
23052
23609
  console.log(
23053
- ` ${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)`
23610
+ ` ${chalk36.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)`
23054
23611
  );
23055
23612
  }
23056
23613
  if (verification) printVerification(verification);
23057
23614
  console.log();
23058
23615
  console.log(
23059
- chalk34.dim(" Run `iamu sync` to push the linked findings to the cloud.")
23616
+ chalk36.dim(" Run `iamu sync` to push the linked findings to the cloud.")
23060
23617
  );
23061
23618
  console.log();
23062
23619
  }
@@ -23078,7 +23635,7 @@ __export(prune_exports, {
23078
23635
  prune: () => prune
23079
23636
  });
23080
23637
  import { confirm, isCancel as isCancel5 } from "@clack/prompts";
23081
- import chalk35 from "chalk";
23638
+ import chalk37 from "chalk";
23082
23639
  function formatBytes(bytes) {
23083
23640
  if (bytes <= 0) return "0 B";
23084
23641
  if (bytes < 1024) return `${bytes} B`;
@@ -23124,7 +23681,7 @@ async function prune(opts = {}) {
23124
23681
  });
23125
23682
  if (preview.prunedRuns.length === 0) {
23126
23683
  console.log(
23127
- chalk35.dim(" No runs match the pruning criteria. Nothing to prune.")
23684
+ chalk37.dim(" No runs match the pruning criteria. Nothing to prune.")
23128
23685
  );
23129
23686
  return preview;
23130
23687
  }
@@ -23133,7 +23690,7 @@ async function prune(opts = {}) {
23133
23690
  message: `Prune ${mode} ${preview.prunedRuns.length} run(s) and reclaim ${formatBytes(preview.totalBytesReclaimed)}?`
23134
23691
  });
23135
23692
  if (isCancel5(confirmed) || !confirmed) {
23136
- console.log(chalk35.yellow(" Pruning cancelled."));
23693
+ console.log(chalk37.yellow(" Pruning cancelled."));
23137
23694
  return;
23138
23695
  }
23139
23696
  }
@@ -23144,14 +23701,14 @@ async function prune(opts = {}) {
23144
23701
  }
23145
23702
  if (result.dryRun) {
23146
23703
  console.log(
23147
- chalk35.cyan(
23704
+ chalk37.cyan(
23148
23705
  `
23149
23706
  [DRY RUN] Would prune ${result.prunedRuns.length} of ${result.totalRunsScanned} run(s):`
23150
23707
  )
23151
23708
  );
23152
23709
  } else {
23153
23710
  console.log(
23154
- chalk35.bold(
23711
+ chalk37.bold(
23155
23712
  `
23156
23713
  ${symbols.success} Pruned ${result.prunedRuns.length} of ${result.totalRunsScanned} run(s):`
23157
23714
  )
@@ -23159,18 +23716,18 @@ async function prune(opts = {}) {
23159
23716
  }
23160
23717
  if (result.prunedRuns.length === 0) {
23161
23718
  console.log(
23162
- chalk35.dim(" No runs matched the pruning criteria. Nothing pruned.")
23719
+ chalk37.dim(" No runs matched the pruning criteria. Nothing pruned.")
23163
23720
  );
23164
23721
  } else {
23165
23722
  for (const run of result.prunedRuns) {
23166
23723
  const mode = run.fullyDeleted ? "removed" : "screenshots purged";
23167
23724
  const count = run.screenshotsDeleted > 0 ? `${run.screenshotsDeleted} screenshots, ` : "";
23168
23725
  console.log(
23169
- ` ${chalk35.dim("\u2022")} ${chalk35.bold(run.runId)} ${chalk35.dim(`(${run.scenarioSlug ?? "unknown scenario"})`)}: ${chalk35.yellow(formatBytes(run.bytesReclaimed))} (${count}${mode})`
23726
+ ` ${chalk37.dim("\u2022")} ${chalk37.bold(run.runId)} ${chalk37.dim(`(${run.scenarioSlug ?? "unknown scenario"})`)}: ${chalk37.yellow(formatBytes(run.bytesReclaimed))} (${count}${mode})`
23170
23727
  );
23171
23728
  }
23172
23729
  console.log(
23173
- chalk35.dim(
23730
+ chalk37.dim(
23174
23731
  `
23175
23732
  Total reclaimed: ${formatBytes(result.totalBytesReclaimed)} (${result.totalScreenshotsDeleted} screenshots deleted)
23176
23733
  `
@@ -23192,7 +23749,7 @@ var init_prune2 = __esm({
23192
23749
  init_context4();
23193
23750
  import { existsSync as existsSync13 } from "fs";
23194
23751
  import { resolve as resolve3 } from "path";
23195
- import chalk38 from "chalk";
23752
+ import chalk40 from "chalk";
23196
23753
  import { Command } from "commander";
23197
23754
 
23198
23755
  // src/commands/register-cloud.ts
@@ -23366,9 +23923,9 @@ function registerProjectCommands(program2) {
23366
23923
  "after",
23367
23924
  "\nExamples:\n $ iamu validate --all\n $ iamu validate --all --fix --dry-run\n $ iamu validate iamu/personas/maria.md"
23368
23925
  ).action(
23369
- async (path32, opts) => {
23926
+ async (path34, opts) => {
23370
23927
  const { validate: validate2 } = await Promise.resolve().then(() => (init_validate(), validate_exports));
23371
- await validate2(path32, opts);
23928
+ await validate2(path34, opts);
23372
23929
  }
23373
23930
  );
23374
23931
  program2.command("update").helpGroup("Project:").description("Update and repair agent skills and prompts to latest version").option(
@@ -23420,6 +23977,51 @@ function registerProjectCommands(program2) {
23420
23977
  await newEntity2(type2, opts);
23421
23978
  }
23422
23979
  );
23980
+ program2.command("set <type> <slug>").helpGroup("Project:").description(
23981
+ "Change how an entity is staged: a scenario's start path, target and starting state, a persona's access key, a capability's routes (scenario, persona, capability)"
23982
+ ).option(
23983
+ "--entry <path>",
23984
+ "Where the walk starts, as a path on the product (scenario)"
23985
+ ).option("--no-entry", "Clear the start path (scenario)").option("--target <url>", "Default target URL (scenario)").option("--no-target", "Clear the default target URL (scenario)").option(
23986
+ "--state-description <text>",
23987
+ "What the world must contain, in domain words (scenario)"
23988
+ ).option(
23989
+ "--state-approximation <text>",
23990
+ "How the walker picks the closest record when the exact one is missing (scenario)"
23991
+ ).option("--no-state", "Clear the starting state (scenario)").option(
23992
+ "--access <key>",
23993
+ "Account, role or switcher key this persona uses (persona)"
23994
+ ).option("--no-access", "Clear the access key (persona)").option(
23995
+ "--paths <paths>",
23996
+ "Route prefixes, comma-separated; replaces the list (capability)"
23997
+ ).option("--no-paths", "Clear route prefixes (capability)").option(
23998
+ "--add-path <path>",
23999
+ "Add a route prefix (capability, repeatable)",
24000
+ (val, prev = []) => [...prev, val]
24001
+ ).option(
24002
+ "--remove-path <path>",
24003
+ "Remove a route prefix (capability, repeatable)",
24004
+ (val, prev = []) => [...prev, val]
24005
+ ).option("--area <name>", "Visual grouping category (capability)").option("--no-area", "Clear the area (capability)").option(
24006
+ "--file <path>",
24007
+ "JSON patch file to apply staging fields (or - for stdin)"
24008
+ ).option("--json", "Output as JSON").addHelpText(
24009
+ "after",
24010
+ '\nWhat a scenario is about (narrative, tension, human tests, brief) is not set here.\n\nExamples:\n $ iamu set scenario maria--handoff --entry /incidents --state-description "Five open incidents at one site"\n $ iamu set persona maria --access coordinator@site-a.test\n $ iamu set capability team-access --add-path /team\n $ iamu set scenario maria--handoff --file patch.json'
24011
+ ).action(
24012
+ async (type2, slug, opts) => {
24013
+ const { setEntity: setEntity2 } = await Promise.resolve().then(() => (init_set_entity(), set_entity_exports));
24014
+ await setEntity2(type2, slug, opts);
24015
+ }
24016
+ );
24017
+ program2.command("edit <type> <slug>").helpGroup("Project:").description(
24018
+ "Open an entity file in your editor ($VISUAL or $EDITOR) and validate on exit"
24019
+ ).option("--json", "Output as JSON").action(
24020
+ async (type2, slug, opts = {}) => {
24021
+ const { editEntity: editEntity2 } = await Promise.resolve().then(() => (init_edit_entity(), edit_entity_exports));
24022
+ await editEntity2(type2, slug, opts);
24023
+ }
24024
+ );
23423
24025
  const explore = program2.command("explore").helpGroup("Project:").description(
23424
24026
  "Map the running product with a read-only crawl; compare the map with runs (coverage) or with another map (diff)"
23425
24027
  );
@@ -23621,14 +24223,17 @@ function registerWalkthroughCommands(program2) {
23621
24223
  }
23622
24224
  );
23623
24225
  program2.command("summary [run-id]").helpGroup("Walkthrough:").description(
23624
- "Record walker summary (what worked, open questions) atomically"
24226
+ "Record walker summary (what worked, open questions, or that the walk was blocked) atomically"
23625
24227
  ).option("--run <run-id>", "Run ID (or pass as positional argument)").option(
23626
24228
  "--worked <note...>",
23627
24229
  "Things that worked well (can be specified multiple times)"
23628
24230
  ).option(
23629
24231
  "--question <note...>",
23630
24232
  "Open questions that arose (can be specified multiple times)"
23631
- ).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(
24233
+ ).option("--model <name>", "Model identifier (e.g. flash, haiku)").option(
24234
+ "--blocked <reason>",
24235
+ "The walk could not start as set up (persona unreachable, world missing): what was missing"
24236
+ ).option("--no-blocked", "Clear the blocked status (if previously set)").option("--file <path>", "Path to summary JSON file (or - for stdin)").option("--json", "Output as JSON").action(
23632
24237
  async (runIdArg, opts) => {
23633
24238
  const { summaryCommand: summaryCommand2 } = await Promise.resolve().then(() => (init_summary(), summary_exports));
23634
24239
  await summaryCommand2(runIdArg, opts);
@@ -23705,23 +24310,23 @@ init_skills();
23705
24310
 
23706
24311
  // src/ui/panic.ts
23707
24312
  init_logger();
23708
- import chalk36 from "chalk";
24313
+ import chalk38 from "chalk";
23709
24314
  var handlersRegistered = false;
23710
24315
  var customSigintHandler = null;
23711
24316
  function logFatalAndExit(title, errOrReason) {
23712
24317
  process.stderr.write("\x1B[?25h");
23713
24318
  if (isVerbose()) {
23714
- console.error(chalk36.red(`
24319
+ console.error(chalk38.red(`
23715
24320
  ${title}:`));
23716
24321
  console.error(
23717
24322
  errOrReason instanceof Error ? errOrReason.stack || errOrReason : errOrReason
23718
24323
  );
23719
24324
  } else {
23720
24325
  const msg = errOrReason instanceof Error ? errOrReason.message : String(errOrReason);
23721
- console.error(chalk36.red(`
24326
+ console.error(chalk38.red(`
23722
24327
  Fatal Error: ${msg}`));
23723
24328
  console.error(
23724
- chalk36.dim(
24329
+ chalk38.dim(
23725
24330
  " Run with --verbose or DEBUG=1 to view the full stack trace.\n"
23726
24331
  )
23727
24332
  );
@@ -23740,7 +24345,7 @@ function setupProcessHandlers() {
23740
24345
  }
23741
24346
  process.exit(130);
23742
24347
  } else {
23743
- console.error(chalk36.dim("\n Operation cancelled."));
24348
+ console.error(chalk38.dim("\n Operation cancelled."));
23744
24349
  process.exit(130);
23745
24350
  }
23746
24351
  });
@@ -23766,7 +24371,7 @@ function setupProcessHandlers() {
23766
24371
  import { mkdir as mkdir7, readFile as readFile8, writeFile as writeFile5 } from "fs/promises";
23767
24372
  import { homedir as homedir4 } from "os";
23768
24373
  import { join as join10 } from "path";
23769
- import chalk37 from "chalk";
24374
+ import chalk39 from "chalk";
23770
24375
  var CACHE_FILE = join10(homedir4(), ".iamu", "update-check.json");
23771
24376
  var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
23772
24377
  function isSemverGreater(latest, current) {
@@ -23819,7 +24424,7 @@ async function checkForUpdate(currentVersion) {
23819
24424
  function printUpdateNotice(currentVersion, latestVersion) {
23820
24425
  console.error();
23821
24426
  console.error(
23822
- ` ${chalk37.yellow("Update available:")} ${chalk37.dim(currentVersion)} \u2192 ${chalk37.green(latestVersion)} ${chalk37.dim("(run: npm i -g @iamu-dev/iamu)")}`
24427
+ ` ${chalk39.yellow("Update available:")} ${chalk39.dim(currentVersion)} \u2192 ${chalk39.green(latestVersion)} ${chalk39.dim("(run: npm i -g @iamu-dev/iamu)")}`
23823
24428
  );
23824
24429
  }
23825
24430
 
@@ -23856,7 +24461,7 @@ var program = new Command().name("iamu").description(
23856
24461
  }
23857
24462
  }
23858
24463
  if (root.color === false || process.env.NO_COLOR) {
23859
- chalk38.level = 0;
24464
+ chalk40.level = 0;
23860
24465
  }
23861
24466
  if (root.verbose) {
23862
24467
  process.env.IAMU_VERBOSE = "1";