@iamu-dev/iamu 0.8.0-beta.2 → 0.8.0-beta.3

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
@@ -522,6 +522,7 @@ var init_finding = __esm({
522
522
  DISMISS_REASONS = [
523
523
  "not_relevant",
524
524
  "walker_error",
525
+ "out_of_scope",
525
526
  "duplicate"
526
527
  ];
527
528
  dismissReasonSchema = z9.enum(DISMISS_REASONS);
@@ -571,11 +572,12 @@ var init_finding = __esm({
571
572
  addressed_at: isoTimestamp.nullish(),
572
573
  /**
573
574
  * Why a `dismissed` finding was dismissed: `not_relevant` (by design,
574
- * out of scope), `walker_error` (the walker misread the product) or
575
- * `duplicate`. Absent means `not_relevant`. The first two also create
576
- * a tester guidance rule.
575
+ * out of scope), `walker_error` (the walker misread the product),
576
+ * `out_of_scope` or `duplicate`. Absent means `not_relevant`.
577
577
  */
578
578
  dismiss_reason: dismissReasonSchema.nullish(),
579
+ /** Finding slug or id this repeats when `dismiss_reason` is `duplicate`. */
580
+ duplicate_of: z9.string().nullish(),
579
581
  /**
580
582
  * The active tester guidance rule this finding matches, set by
581
583
  * `iamu match-record`. The finding is kept — the rule flagged it,
@@ -698,7 +700,11 @@ var init_guidance = __esm({
698
700
  "../../packages/protocol/dist/schemas/frontmatter/guidance.js"() {
699
701
  "use strict";
700
702
  init_common();
701
- GUIDANCE_KINDS = ["not_relevant", "walker_error"];
703
+ GUIDANCE_KINDS = [
704
+ "not_relevant",
705
+ "walker_error",
706
+ "out_of_scope"
707
+ ];
702
708
  guidanceKindSchema = z10.enum(GUIDANCE_KINDS);
703
709
  guidanceStatusSchema = z10.enum(["active", "retired"]);
704
710
  guidanceScopeSchema = z10.object({
@@ -1598,6 +1604,7 @@ var init_walkthrough_canonical = __esm({
1598
1604
  canonicalStepSchema = z22.object({
1599
1605
  id: z22.string(),
1600
1606
  step_number: z22.number().int(),
1607
+ timestamp: z22.string().optional(),
1601
1608
  url: z22.string().optional(),
1602
1609
  /** The walker's short name for the screen. */
1603
1610
  surface: z22.string().optional(),
@@ -5835,9 +5842,9 @@ function pidIsDead(rec) {
5835
5842
  async function isStale(lockPath, rec, staleMs, now) {
5836
5843
  if (rec === null) {
5837
5844
  try {
5838
- const stat3 = await fs6.stat(lockPath);
5839
- if (stat3.size === 0) {
5840
- const mtime = stat3.birthtimeMs && stat3.birthtimeMs > 0 ? stat3.birthtimeMs : stat3.mtimeMs;
5845
+ const stat4 = await fs6.stat(lockPath);
5846
+ if (stat4.size === 0) {
5847
+ const mtime = stat4.birthtimeMs && stat4.birthtimeMs > 0 ? stat4.birthtimeMs : stat4.mtimeMs;
5841
5848
  if (now - mtime <= staleMs) {
5842
5849
  return false;
5843
5850
  }
@@ -6838,13 +6845,13 @@ function fenceTracker() {
6838
6845
  };
6839
6846
  }
6840
6847
  function cleanName(raw, legacy) {
6841
- let s = raw.replace(/\s+#+\s*$/, "").replace(/[*_`]/g, "").replace(/[‘’]/g, "'").replace(/[“”]/g, '"').replace(/\s+/g, " ").trim();
6848
+ let s = raw.replace(/\\(["'])/g, "$1").replace(/\s+#+\s*$/, "").replace(/[*_`]/g, "").replace(/[‘’]/g, "'").replace(/[“”]/g, '"').replace(/\s+/g, " ").trim();
6842
6849
  if (legacy)
6843
6850
  s = s.replace(LEGACY_ID, "");
6844
- return s.replace(/[.:]+$/, "").trim();
6851
+ return s.replace(/[.:—–-]+$/, "").trim();
6845
6852
  }
6846
6853
  function normaliseRuleName(raw, legacy = false) {
6847
- return cleanName(raw, legacy).toLowerCase();
6854
+ return cleanName(raw, legacy).replace(/["'\\]/g, "").replace(/\s+/g, " ").trim().toLowerCase();
6848
6855
  }
6849
6856
  function scanLines(text) {
6850
6857
  const inFence = fenceTracker();
@@ -6913,7 +6920,7 @@ function rulesInText(text, opts = {}) {
6913
6920
  continue;
6914
6921
  if (l.heading) {
6915
6922
  const { level, text: h } = l.heading;
6916
- if (level >= 2 && level <= 4) {
6923
+ if (level >= 2 && level <= 5) {
6917
6924
  let end2 = i + 1;
6918
6925
  while (end2 < to) {
6919
6926
  const next = lines[end2].heading;
@@ -6932,25 +6939,30 @@ function rulesInText(text, opts = {}) {
6932
6939
  headings.push(l.heading);
6933
6940
  continue;
6934
6941
  }
6935
- const item = LIST_ITEM.exec(l.text);
6936
- const rest = item ? l.text.slice(item[0].length) : l.text.trimStart();
6942
+ const unquoted = l.text.replace(/^\s*>\s?/, "");
6943
+ const item = LIST_ITEM.exec(unquoted);
6944
+ const rest = item ? unquoted.slice(item[0].length) : unquoted.trimStart();
6937
6945
  const bold = BOLD_LEAD.exec(rest);
6938
6946
  if (!bold)
6939
6947
  continue;
6940
6948
  const inner = bold[2].trim();
6941
- if (!/[.:]$/.test(inner) && !bold[3])
6949
+ const afterBold = rest.slice(bold[0].length);
6950
+ const hasStop = /[.:!?—–]$/.test(inner) || Boolean(bold[3]) || afterBold.trim() === "";
6951
+ if (!hasStop)
6942
6952
  continue;
6943
6953
  const prev = i === 0 ? null : lines[i - 1];
6944
- const startsParagraph = !prev || prev.text.trim() === "" || Boolean(prev.heading);
6954
+ const prevUnquoted = prev ? prev.text.replace(/^\s*>\s?/, "") : "";
6955
+ const startsParagraph = !prev || prevUnquoted.trim() === "" || Boolean(prev.heading);
6945
6956
  if (!item && !startsParagraph)
6946
6957
  continue;
6947
6958
  const indent = item ? item[1].length : 0;
6948
6959
  let end = i + 1;
6949
6960
  while (end < to) {
6950
6961
  const n = lines[end];
6951
- if (n.heading || !n.fenced && n.text.trim() === "")
6962
+ const nUnquoted = n.text.replace(/^\s*>\s?/, "");
6963
+ if (n.heading || !n.fenced && nUnquoted.trim() === "")
6952
6964
  break;
6953
- const nItem = LIST_ITEM.exec(n.text);
6965
+ const nItem = LIST_ITEM.exec(nUnquoted);
6954
6966
  if (item && nItem && nItem[1].length <= indent)
6955
6967
  break;
6956
6968
  end++;
@@ -6975,7 +6987,19 @@ function matchRuleName(rules, citation, legacy = false) {
6975
6987
  section: normaliseRuleName(citation.slice(0, at), legacy),
6976
6988
  name: normaliseRuleName(citation.slice(at + 1), legacy)
6977
6989
  } : null;
6978
- return rules.filter((r) => r.name.toLowerCase() === whole || scoped !== null && r.name.toLowerCase() === scoped.name && (r.section ?? "").toLowerCase() === scoped.section);
6990
+ const exact = rules.filter((r) => {
6991
+ const rName = normaliseRuleName(r.name, legacy);
6992
+ const rSec = normaliseRuleName(r.section ?? "", legacy);
6993
+ return rName === whole || scoped !== null && rName === scoped.name && rSec === scoped.section;
6994
+ });
6995
+ if (exact.length > 0)
6996
+ return exact;
6997
+ const leadOf = (s) => normaliseRuleName(s.split(/\s*(?::|—|–|\s-\s)\s*/)[0] ?? "", legacy);
6998
+ return rules.filter((r) => {
6999
+ const rLead = leadOf(r.name);
7000
+ const rSec = normaliseRuleName(r.section ?? "", legacy);
7001
+ return rLead.length > 0 && rLead === whole || scoped !== null && rLead.length > 0 && rLead === scoped.name && rSec === scoped.section;
7002
+ });
6979
7003
  }
6980
7004
  async function findRule(entries, citation) {
6981
7005
  const seen = /* @__PURE__ */ new Map();
@@ -7009,6 +7033,27 @@ async function citationFor(entries, match2) {
7009
7033
  const alone = await findRule(entries, { rule: match2.name, file: match2.file });
7010
7034
  return alone.length === 1 || !match2.section ? match2.name : `${match2.section} > ${match2.name}`;
7011
7035
  }
7036
+ async function canonicalRulesInFile(file) {
7037
+ if (file.kind !== "design")
7038
+ return [];
7039
+ let text;
7040
+ try {
7041
+ text = await fs10.readFile(file.abs, "utf8");
7042
+ } catch {
7043
+ return [];
7044
+ }
7045
+ const legacy = file.path === LEGACY_DESIGN_DOC;
7046
+ const rules = rulesInText(text, { legacy, heading: file.heading }) ?? [];
7047
+ const counts = /* @__PURE__ */ new Map();
7048
+ for (const r of rules) {
7049
+ const key = normaliseRuleName(r.name, legacy);
7050
+ counts.set(key, (counts.get(key) ?? 0) + 1);
7051
+ }
7052
+ return rules.map((r) => {
7053
+ const key = normaliseRuleName(r.name, legacy);
7054
+ return (counts.get(key) ?? 0) === 1 || !r.section ? r.name : `${r.section} > ${r.name}`;
7055
+ });
7056
+ }
7012
7057
  async function resolveRule(entries, citation) {
7013
7058
  const matches = await findRule(entries, citation);
7014
7059
  if (matches.length === 1)
@@ -7035,7 +7080,7 @@ var init_design_rules = __esm({
7035
7080
  LEGACY_ID = new RegExp(`^${LEGACY_ID_PATTERN}\\s+`);
7036
7081
  HEADING = /^(#{1,6})\s+(.+?)\s*$/;
7037
7082
  LIST_ITEM = /^(\s*)(?:[-*+]|\d+[.)])\s+/;
7038
- BOLD_LEAD = /^(\*\*|__)(.+?)\1\s*([.:])?/;
7083
+ BOLD_LEAD = /^(\*\*|__)(.+?)\1(?:\s*([.:—–]|\s-\s))?/;
7039
7084
  sameFile = (f, file) => {
7040
7085
  const want = file.trim().replace(/\\/g, "/");
7041
7086
  return f.entry === want || f.path === want;
@@ -7723,15 +7768,15 @@ async function entryProblem(projectRoot, entry) {
7723
7768
  if (!isInsideProjectRoot(projectRoot, abs)) {
7724
7769
  return `Path escapes project root: ${rel}`;
7725
7770
  }
7726
- let stat3;
7771
+ let stat4;
7727
7772
  try {
7728
- stat3 = await fs13.stat(abs);
7773
+ stat4 = await fs13.stat(abs);
7729
7774
  } catch {
7730
7775
  return `Not found: ${rel}`;
7731
7776
  }
7732
7777
  if (!heading)
7733
7778
  return null;
7734
- if (!stat3.isFile())
7779
+ if (!stat4.isFile())
7735
7780
  return `${rel} is a folder; a section needs a file`;
7736
7781
  const text = await fs13.readFile(abs, "utf8");
7737
7782
  if (sectionLines(text, heading))
@@ -8707,7 +8752,14 @@ async function isProposalStale(projectRoot, p) {
8707
8752
  }
8708
8753
  async function readDesignContext(projectRoot, opts = {}) {
8709
8754
  const state = await readDesignSources(projectRoot);
8710
- const files = selectDesignFiles(state.file.files, opts.urls).map((e) => resolveDesignFile(projectRoot, e));
8755
+ const files = await Promise.all(selectDesignFiles(state.file.files, opts.urls).map(async (e) => {
8756
+ const resolved = resolveDesignFile(projectRoot, e);
8757
+ if (resolved.kind === "design") {
8758
+ const rules = await canonicalRulesInFile(resolved);
8759
+ return rules.length > 0 ? { ...resolved, rules } : resolved;
8760
+ }
8761
+ return resolved;
8762
+ }));
8711
8763
  const proposals = [];
8712
8764
  for (const p of await listDesignProposals(projectRoot)) {
8713
8765
  proposals.push({
@@ -9636,7 +9688,7 @@ async function suppressionRules(projectRoot, outlineSlug) {
9636
9688
  if (!outlineSlug)
9637
9689
  return [];
9638
9690
  try {
9639
- return (await activeGuidanceFor(projectRoot, { outline: outlineSlug })).filter((r) => r.kind === "not_relevant").map((r) => ({ id: r.id, rule: r.rule }));
9691
+ return (await activeGuidanceFor(projectRoot, { outline: outlineSlug })).filter((r) => r.kind === "not_relevant" || r.kind === "out_of_scope").map((r) => ({ id: r.id, rule: r.rule }));
9640
9692
  } catch {
9641
9693
  return [];
9642
9694
  }
@@ -10537,6 +10589,63 @@ async function readDesignerIgnored(projectRoot) {
10537
10589
  return readForWrite(projectRoot).catch(() => []);
10538
10590
  }
10539
10591
  async function ignoreDesignerIssue(projectRoot, runId, issueId, opts = {}) {
10592
+ const reasonGiven = opts.reason?.trim() || null;
10593
+ const when = opts.now ?? (/* @__PURE__ */ new Date()).toISOString();
10594
+ const cascadeDismissSiblings = async (targetFm, note) => {
10595
+ const cid = targetFm.canonical_id;
10596
+ if (!cid)
10597
+ return;
10598
+ const { entities } = await listEntities(projectRoot, "finding").catch(() => ({ entities: [] }));
10599
+ for (const sibling of entities) {
10600
+ if (sibling.frontmatter.id === targetFm.id)
10601
+ continue;
10602
+ if (sibling.frontmatter.canonical_id !== cid)
10603
+ continue;
10604
+ if (sibling.frontmatter.triage_status === "dismissed")
10605
+ continue;
10606
+ await writeEntity(projectRoot, "finding", {
10607
+ ...sibling.frontmatter,
10608
+ triage_status: "dismissed",
10609
+ dismiss_reason: "not_relevant",
10610
+ triage_note: note,
10611
+ updated_at: when
10612
+ }, sibling.body);
10613
+ }
10614
+ };
10615
+ const trimmedRef = issueId.trim().toUpperCase();
10616
+ if (/^P\d+$/.test(trimmedRef)) {
10617
+ const refs2 = await fs21.readFile(designerRefsFile(runDir(projectRoot, runId)), "utf8").then((raw) => JSON.parse(raw)).catch(() => null);
10618
+ const findingId = refs2?.[trimmedRef];
10619
+ const pFile = findingId ? await readEntity(projectRoot, "finding", findingId).catch(() => null) : null;
10620
+ if (!pFile) {
10621
+ throw new DesignerRecordError("unknown_designer_finding", `${runId} has no designer finding ${issueId}`);
10622
+ }
10623
+ const already = pFile.frontmatter.triage_status === "dismissed";
10624
+ const note = reasonGiven ?? (pFile.frontmatter.triage_note?.trim() || "Dismissed via iamu designer-dismiss");
10625
+ if (!already || reasonGiven && reasonGiven !== pFile.frontmatter.triage_note) {
10626
+ await writeEntity(projectRoot, "finding", {
10627
+ ...pFile.frontmatter,
10628
+ triage_status: "dismissed",
10629
+ dismiss_reason: "not_relevant",
10630
+ triage_note: note,
10631
+ updated_at: when
10632
+ }, pFile.body);
10633
+ await cascadeDismissSiblings(pFile.frontmatter, note);
10634
+ }
10635
+ return {
10636
+ entry: {
10637
+ title: pFile.frontmatter.title ?? pFile.frontmatter.slug,
10638
+ screen: pFile.frontmatter.screen ?? null,
10639
+ what: pFile.frontmatter.what ?? "",
10640
+ severity: pFile.frontmatter.severity ?? void 0,
10641
+ reason: reasonGiven ?? pFile.frontmatter.triage_note ?? null,
10642
+ run: runId,
10643
+ issue: trimmedRef,
10644
+ at: when
10645
+ },
10646
+ already
10647
+ };
10648
+ }
10540
10649
  const record = await readDesignerRecord(projectRoot, runId);
10541
10650
  if (!record) {
10542
10651
  throw new DesignerRecordError("not_judged", `Run ${runId} has no designer's review to dismiss a finding of`);
@@ -10546,8 +10655,6 @@ async function ignoreDesignerIssue(projectRoot, runId, issueId, opts = {}) {
10546
10655
  if (!issue) {
10547
10656
  throw new DesignerRecordError("unknown_designer_finding", `${runId} has no designer finding ${issueId}`);
10548
10657
  }
10549
- const reasonGiven = opts.reason?.trim() || null;
10550
- const when = opts.now ?? (/* @__PURE__ */ new Date()).toISOString();
10551
10658
  const file = issue.finding_id ? await readEntity(projectRoot, "finding", issue.finding_id).catch(() => null) : null;
10552
10659
  if (file) {
10553
10660
  const already = file.frontmatter.triage_status === "dismissed";
@@ -10560,6 +10667,7 @@ async function ignoreDesignerIssue(projectRoot, runId, issueId, opts = {}) {
10560
10667
  triage_note: note,
10561
10668
  updated_at: when
10562
10669
  }, file.body);
10670
+ await cascadeDismissSiblings(file.frontmatter, note);
10563
10671
  }
10564
10672
  return {
10565
10673
  entry: {
@@ -10926,6 +11034,7 @@ async function buildCanonical(projectRoot, manifest, completedAt, inputs) {
10926
11034
  return {
10927
11035
  id: `step-${stepNum}`,
10928
11036
  step_number: stepNum,
11037
+ ...typeof s.timestamp === "string" && s.timestamp ? { timestamp: s.timestamp } : {},
10929
11038
  screen_title: s.action,
10930
11039
  // Step files record the path relative to the run directory
10931
11040
  // (`screenshots/step-1.png`); older ones recorded the bare file
@@ -11424,7 +11533,7 @@ async function assembleWalkthroughBundle(projectRoot, runId, opts = {}) {
11424
11533
  scenarioIdForPush = manifest.scenario_slug;
11425
11534
  }
11426
11535
  }
11427
- const bundleSteps = steps2.map((s) => {
11536
+ const bundleSteps = steps2.map((s, idx) => {
11428
11537
  const shot = screenshots.get(s.step_number);
11429
11538
  const ref = shot ? {
11430
11539
  part_name: `screenshot_${s.step_number}`,
@@ -11432,7 +11541,7 @@ async function assembleWalkthroughBundle(projectRoot, runId, opts = {}) {
11432
11541
  content_type: shot.contentType
11433
11542
  } : null;
11434
11543
  return {
11435
- seq: s.step_number,
11544
+ seq: idx + 1,
11436
11545
  title: s.action,
11437
11546
  persona_reasoning: s.note ?? null,
11438
11547
  grounding: s.grounding ?? null,
@@ -17054,9 +17163,9 @@ function withSessionStateLock(filePath, fn) {
17054
17163
  const now = Date.now();
17055
17164
  if (rec === null) {
17056
17165
  try {
17057
- const stat3 = statSync(targetPath);
17058
- if (stat3.size === 0) {
17059
- const mtime = stat3.birthtimeMs && stat3.birthtimeMs > 0 ? stat3.birthtimeMs : stat3.mtimeMs;
17166
+ const stat4 = statSync(targetPath);
17167
+ if (stat4.size === 0) {
17168
+ const mtime = stat4.birthtimeMs && stat4.birthtimeMs > 0 ? stat4.birthtimeMs : stat4.mtimeMs;
17060
17169
  if (now - mtime <= staleMs)
17061
17170
  return false;
17062
17171
  }
@@ -18673,7 +18782,12 @@ async function buildSurfaceDossier(projectRoot, target, opts = {}) {
18673
18782
  seenDismissed.add(d.title);
18674
18783
  return true;
18675
18784
  });
18676
- const guidance = (await listEntities(projectRoot, "guidance")).entities.filter((g) => (g.frontmatter.status ?? "active") === "active").map((g) => g.body.trim().split(/\n\s*\n/)[0] ?? "").filter(Boolean);
18785
+ const guidance = (await listEntities(projectRoot, "guidance")).entities.filter((g) => (g.frontmatter.status ?? "active") === "active").map((g) => {
18786
+ const { rule, reason } = parseGuidanceBody(g.body);
18787
+ if (!rule)
18788
+ return "";
18789
+ return reason ? `${rule} (${reason})` : rule;
18790
+ }).filter(Boolean);
18677
18791
  const ids = new Set(allOnSurface.map((r) => r.fm.id));
18678
18792
  const directions = (await listDirections(projectRoot)).filter((d) => d.frontmatter.status !== "rejected" && d.frontmatter.absorbs.some((id) => ids.has(id))).map((d) => ({
18679
18793
  slug: d.frontmatter.slug,
@@ -20517,6 +20631,23 @@ async function buildRunDesignerInput(projectRoot, runId) {
20517
20631
  const ended = summary?.ended;
20518
20632
  const testability = testabilityPath(projectRoot);
20519
20633
  const pfm = persona?.frontmatter;
20634
+ const outlineSlug = outline?.frontmatter.slug ?? scenario?.frontmatter.outline ?? null;
20635
+ const guidanceRules = outlineSlug ? await activeGuidanceFor(projectRoot, { outline: outlineSlug }).catch(() => []) : (await listEntities(projectRoot, "guidance").catch(() => ({ entities: [] }))).entities.filter((g) => (g.frontmatter.status ?? "active") === "active").map((g) => {
20636
+ const { rule, reason: reason2 } = parseGuidanceBody(g.body);
20637
+ return { rule, reason: reason2 };
20638
+ }).filter((g) => Boolean(g.rule));
20639
+ const guidance = guidanceRules.map((r) => r.reason ? `${r.rule} (${r.reason})` : r.rule);
20640
+ const designFiles = await Promise.all(docs.map(async (f) => {
20641
+ const rules = f.kind === "design" ? f.rules ?? await canonicalRulesInFile(f) : void 0;
20642
+ return {
20643
+ entry: f.entry,
20644
+ kind: f.kind,
20645
+ abs: f.abs,
20646
+ heading: f.heading,
20647
+ applies_to: f.applies_to,
20648
+ ...rules && rules.length > 0 ? { rules } : {}
20649
+ };
20650
+ }));
20520
20651
  const bundle = {
20521
20652
  run_id: runId,
20522
20653
  project_root: projectRoot,
@@ -20541,13 +20672,7 @@ async function buildRunDesignerInput(projectRoot, runId) {
20541
20672
  ended: ended && typeof ended.note === "string" ? { reached: ended.reached === true, note: ended.note } : null
20542
20673
  } : null,
20543
20674
  design: docs.length > 0 || proposals.length > 0 ? {
20544
- files: docs.map((f) => ({
20545
- entry: f.entry,
20546
- kind: f.kind,
20547
- abs: f.abs,
20548
- heading: f.heading,
20549
- applies_to: f.applies_to
20550
- })),
20675
+ files: designFiles,
20551
20676
  proposals: proposals.map((p) => ({
20552
20677
  slug: p.slug,
20553
20678
  kind: p.kind,
@@ -20575,6 +20700,7 @@ async function buildRunDesignerInput(projectRoot, runId) {
20575
20700
  reason: e.reason
20576
20701
  }))
20577
20702
  ],
20703
+ guidance,
20578
20704
  facts: context?.facts ? designerFacts(context.facts) : null,
20579
20705
  setup_notes: {
20580
20706
  file: existsSync5(testability) ? testability : null,
@@ -20623,6 +20749,9 @@ var init_designer_input = __esm({
20623
20749
  init_advice_input();
20624
20750
  init_design_facts();
20625
20751
  init_design_proposals();
20752
+ init_design_rules();
20753
+ init_entity();
20754
+ init_guidance2();
20626
20755
  init_paths();
20627
20756
  init_review_issues();
20628
20757
  init_url_path();
@@ -20630,6 +20759,7 @@ var init_designer_input = __esm({
20630
20759
  init_designer_ignored();
20631
20760
  init_designer_ledger();
20632
20761
  init_walkthrough_canonical2();
20762
+ init_dist();
20633
20763
  LONG_VISIT = 6;
20634
20764
  FRAMES_PER_VISIT = 8;
20635
20765
  FRAMES_PER_RUN = 24;
@@ -21941,15 +22071,15 @@ function resolveApiUrl(configured) {
21941
22071
  assertSecureApiUrl(url, configured ? "API URL" : "IAMU_API_URL");
21942
22072
  return url;
21943
22073
  }
21944
- function parseStore(raw, path73) {
22074
+ function parseStore(raw, path74) {
21945
22075
  let parsed;
21946
22076
  try {
21947
22077
  parsed = JSON.parse(raw);
21948
22078
  } catch (err) {
21949
- throw new CredentialsFileError(path73, "not valid JSON", err);
22079
+ throw new CredentialsFileError(path74, "not valid JSON", err);
21950
22080
  }
21951
22081
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
21952
- throw new CredentialsFileError(path73, "expected a JSON object");
22082
+ throw new CredentialsFileError(path74, "expected a JSON object");
21953
22083
  }
21954
22084
  return parsed;
21955
22085
  }
@@ -21957,26 +22087,26 @@ function isNotFound(err) {
21957
22087
  return typeof err === "object" && err !== null && err.code === "ENOENT";
21958
22088
  }
21959
22089
  async function readCredentialsStore() {
21960
- const path73 = getCredentialsPath();
22090
+ const path74 = getCredentialsPath();
21961
22091
  let raw;
21962
22092
  try {
21963
- raw = await readFile(path73, "utf-8");
22093
+ raw = await readFile(path74, "utf-8");
21964
22094
  } catch (err) {
21965
22095
  if (isNotFound(err)) return {};
21966
22096
  warnUnreadableStoreOnce(err);
21967
22097
  throw err;
21968
22098
  }
21969
22099
  try {
21970
- return parseStore(raw, path73);
22100
+ return parseStore(raw, path74);
21971
22101
  } catch (err) {
21972
22102
  warnUnreadableStoreOnce(err);
21973
22103
  throw err;
21974
22104
  }
21975
22105
  }
21976
22106
  async function writeCredentialsStore(store) {
21977
- const path73 = getCredentialsPath();
21978
- await mkdir(dirname(path73), { recursive: true, mode: 448 });
21979
- await atomicWriteFile(path73, `${JSON.stringify(store, null, 2)}
22107
+ const path74 = getCredentialsPath();
22108
+ await mkdir(dirname(path74), { recursive: true, mode: 448 });
22109
+ await atomicWriteFile(path74, `${JSON.stringify(store, null, 2)}
21980
22110
  `, {
21981
22111
  mode: 384
21982
22112
  });
@@ -22004,16 +22134,16 @@ function resolveApiKey(apiUrl) {
22004
22134
  }
22005
22135
  }
22006
22136
  const normalized = url.replace(/\/+$/, "");
22007
- for (const path73 of candidatePaths) {
22137
+ for (const path74 of candidatePaths) {
22008
22138
  let raw;
22009
22139
  try {
22010
- raw = readFileSync2(path73, "utf-8");
22140
+ raw = readFileSync2(path74, "utf-8");
22011
22141
  } catch (err) {
22012
22142
  if (!isNotFound(err)) warnUnreadableStoreOnce(err);
22013
22143
  continue;
22014
22144
  }
22015
22145
  try {
22016
- const store = parseStore(raw, path73);
22146
+ const store = parseStore(raw, path74);
22017
22147
  const entry = store[url] ?? store[normalized] ?? store[`${normalized}/`];
22018
22148
  if (typeof entry !== "object" || entry === null) continue;
22019
22149
  const token = entry.token;
@@ -22054,12 +22184,12 @@ var init_credentials = __esm({
22054
22184
  * @param reason - What is wrong with it.
22055
22185
  * @param cause - Underlying parse error, if any.
22056
22186
  */
22057
- constructor(path73, reason, cause) {
22187
+ constructor(path74, reason, cause) {
22058
22188
  super(
22059
- `Credentials file ${path73} is corrupt (${reason}). Fix it by hand, or delete it and run \`iamu auth login\` again.`,
22189
+ `Credentials file ${path74} is corrupt (${reason}). Fix it by hand, or delete it and run \`iamu auth login\` again.`,
22060
22190
  {
22061
22191
  cause,
22062
- suggestedAction: `Delete ${path73} and run \`iamu auth login\`, or set IAMU_TOKEN.`
22192
+ suggestedAction: `Delete ${path74} and run \`iamu auth login\`, or set IAMU_TOKEN.`
22063
22193
  }
22064
22194
  );
22065
22195
  }
@@ -23028,8 +23158,8 @@ import { dirname as dirname3, join as join5, resolve as resolve2 } from "path";
23028
23158
  import { fileURLToPath as fileURLToPath2 } from "url";
23029
23159
  import chalk6 from "chalk";
23030
23160
  function getVersion() {
23031
- if ("0.8.0-beta.2") {
23032
- return "0.8.0-beta.2";
23161
+ if ("0.8.0-beta.3") {
23162
+ return "0.8.0-beta.3";
23033
23163
  }
23034
23164
  try {
23035
23165
  const thisDir = dirname3(fileURLToPath2(import.meta.url));
@@ -23141,16 +23271,16 @@ async function needsWrite(filePath, content) {
23141
23271
  return true;
23142
23272
  }
23143
23273
  }
23144
- function isCliWrittenSkill(path73) {
23274
+ function isCliWrittenSkill(path74) {
23145
23275
  try {
23146
- return /^generatedBy:/m.test(readFileSync6(path73, "utf-8"));
23276
+ return /^generatedBy:/m.test(readFileSync6(path74, "utf-8"));
23147
23277
  } catch {
23148
23278
  return false;
23149
23279
  }
23150
23280
  }
23151
- function isCliWrittenPrompt(path73) {
23281
+ function isCliWrittenPrompt(path74) {
23152
23282
  try {
23153
- const content = readFileSync6(path73, "utf-8");
23283
+ const content = readFileSync6(path74, "utf-8");
23154
23284
  return /^generatedBy:/m.test(content) || /^#\s+iamu:/m.test(content) || /^description:\s*.*iamu/im.test(content);
23155
23285
  } catch {
23156
23286
  return false;
@@ -23162,8 +23292,8 @@ function planSkillFiles(hosts, projectRoot, version2) {
23162
23292
  for (const host of hosts) {
23163
23293
  if (pluginProvidesSkills(host, projectRoot)) {
23164
23294
  for (const name of skillTemplates.keys()) {
23165
- const path73 = join5(host.getSkillsDir(projectRoot), name, "SKILL.md");
23166
- if (isCliWrittenSkill(path73)) files.push({ host, path: path73, content: null });
23295
+ const path74 = join5(host.getSkillsDir(projectRoot), name, "SKILL.md");
23296
+ if (isCliWrittenSkill(path74)) files.push({ host, path: path74, content: null });
23167
23297
  }
23168
23298
  } else if (host.supportsSkills) {
23169
23299
  const skillsDir = host.getSkillsDir(projectRoot);
@@ -25193,13 +25323,13 @@ function git(args2, cwd) {
25193
25323
  }
25194
25324
  function hashFile(absPath) {
25195
25325
  const hash = createHash5("sha256");
25196
- const stat3 = lstatSync(absPath);
25197
- if (stat3.isSymbolicLink()) {
25326
+ const stat4 = lstatSync(absPath);
25327
+ if (stat4.isSymbolicLink()) {
25198
25328
  hash.update(`symlink:${readlinkSync(absPath)}`);
25199
25329
  return hash.digest("hex");
25200
25330
  }
25201
- if (!stat3.isFile()) {
25202
- hash.update(`${stat3.isDirectory() ? "dir" : "other"}`);
25331
+ if (!stat4.isFile()) {
25332
+ hash.update(`${stat4.isDirectory() ? "dir" : "other"}`);
25203
25333
  return hash.digest("hex");
25204
25334
  }
25205
25335
  const fd = openSync3(absPath, "r");
@@ -31460,12 +31590,20 @@ function awaitingSteps(projectRoot, findings2) {
31460
31590
  }
31461
31591
  function defaultRule(kind, findings2) {
31462
31592
  const titles = findings2.map((f) => f.frontmatter.title ?? f.frontmatter.ui_fact ?? f.slug).join("; ");
31463
- return kind === "walker_error" ? `Earlier walkers got this wrong: ${titles}` : `Don't report: ${titles}`;
31593
+ if (kind === "walker_error") {
31594
+ return `Earlier walkers got this wrong: ${titles}`;
31595
+ }
31596
+ if (kind === "out_of_scope") {
31597
+ return `Handled outside system; don't report: ${titles}`;
31598
+ }
31599
+ return `Don't report: ${titles}`;
31464
31600
  }
31465
31601
  async function resolveScope(projectRoot, flag, findings2, opts) {
31466
31602
  if (flag === "project") return {};
31603
+ const withScenario = findings2.filter((f) => Boolean(f.frontmatter.scenario));
31604
+ if (withScenario.length === 0) return {};
31467
31605
  const outlines = /* @__PURE__ */ new Set();
31468
- for (const f of findings2) {
31606
+ for (const f of withScenario) {
31469
31607
  const scenario = await safeReadEntity(
31470
31608
  projectRoot,
31471
31609
  "scenario",
@@ -31506,6 +31644,47 @@ async function resolveScope(projectRoot, flag, findings2, opts) {
31506
31644
  }
31507
31645
  return { capability };
31508
31646
  }
31647
+ async function resolveDesignerRefInTriage(projectRoot, ref) {
31648
+ const m = /^(?:([^#]+)#)?([DP]\d+)$/i.exec(ref.trim());
31649
+ if (!m) return null;
31650
+ const runPart = m[1]?.trim();
31651
+ const code = (m[2] ?? "").toUpperCase();
31652
+ const targetRunId = resolveRunId(projectRoot, runPart);
31653
+ if (!targetRunId) return null;
31654
+ if (code.startsWith("P")) {
31655
+ const refsRaw = await fs62.readFile(
31656
+ path59.join(runDir(projectRoot, targetRunId), DESIGNER_REFS_FILE),
31657
+ "utf8"
31658
+ ).catch(() => null);
31659
+ if (!refsRaw) return null;
31660
+ try {
31661
+ const parsed = JSON.parse(refsRaw);
31662
+ const findingId = parsed[code];
31663
+ if (findingId) {
31664
+ return await readEntity(
31665
+ projectRoot,
31666
+ "finding",
31667
+ findingId
31668
+ );
31669
+ }
31670
+ } catch {
31671
+ return null;
31672
+ }
31673
+ return null;
31674
+ }
31675
+ const rec = await readDesignerRecord(projectRoot, targetRunId).catch(
31676
+ () => null
31677
+ );
31678
+ const issue = rec?.issues.find((i) => i.id.toUpperCase() === code);
31679
+ if (issue?.finding_id) {
31680
+ return await readEntity(
31681
+ projectRoot,
31682
+ "finding",
31683
+ issue.finding_id
31684
+ );
31685
+ }
31686
+ return null;
31687
+ }
31509
31688
  async function triage(refs2, opts) {
31510
31689
  const ctx = await resolveProject(opts);
31511
31690
  if (!ctx) return;
@@ -31545,42 +31724,39 @@ async function triage(refs2, opts) {
31545
31724
  }
31546
31725
  reason = opts.reason;
31547
31726
  }
31548
- if (status2.data === "dismissed") {
31549
- reason ??= "not_relevant";
31550
- if (!opts.note?.trim()) {
31727
+ if (opts.duplicateOf !== void 0) {
31728
+ if (status2.data !== "dismissed") {
31729
+ commandError("--duplicate-of applies only to --status dismissed", {
31730
+ ...opts,
31731
+ code: "invalid_duplicate_of_usage",
31732
+ fix: `iamu triage ${refs2.join(" ")} --status dismissed --duplicate-of ${opts.duplicateOf}`
31733
+ });
31734
+ return;
31735
+ }
31736
+ if (reason && reason !== "duplicate") {
31551
31737
  commandError(
31552
- "Dismissing needs --note naming why (by design, out of scope, what the walker misread, duplicate of \u2026) \u2014 it teaches the next walk",
31738
+ `--duplicate-of requires --reason duplicate \u2014 got "${reason}"`,
31553
31739
  {
31554
31740
  ...opts,
31555
- code: "missing_dismiss_note",
31556
- fix: `iamu triage ${refs2.join(" ")} --status dismissed --note "by design"`
31741
+ code: "invalid_duplicate_of_usage",
31742
+ fix: `iamu triage ${refs2.join(" ")} --status dismissed --reason duplicate --duplicate-of ${opts.duplicateOf}`
31557
31743
  }
31558
31744
  );
31559
31745
  return;
31560
31746
  }
31747
+ reason = "duplicate";
31748
+ }
31749
+ if (status2.data === "dismissed") {
31750
+ reason ??= "not_relevant";
31561
31751
  }
31562
31752
  const isSweep = opts.sweep !== void 0 && opts.sweep !== false;
31563
31753
  const sweepQuery = typeof opts.sweep === "string" ? opts.sweep.trim() : "";
31564
31754
  if (isSweep && status2.data !== "dismissed" && status2.data !== "wont_fix") {
31565
- commandError(
31566
- "--sweep applies only when dismissing (--status dismissed or wont_fix)",
31567
- {
31568
- ...opts,
31569
- code: "invalid_sweep_usage",
31570
- fix: `iamu triage ${refs2.join(" ")} --status dismissed --note "by design" --sweep`
31571
- }
31572
- );
31573
- return;
31574
- }
31575
- if (isSweep && status2.data === "wont_fix" && !opts.note?.trim()) {
31576
- commandError(
31577
- "Dismissing with --sweep needs --note naming why \u2014 it teaches the whole project",
31578
- {
31579
- ...opts,
31580
- code: "missing_dismiss_note",
31581
- fix: `iamu triage ${refs2.join(" ")} --status wont_fix --note "by design" --sweep`
31582
- }
31583
- );
31755
+ commandError("--sweep applies only when dismissing (--status dismissed)", {
31756
+ ...opts,
31757
+ code: "invalid_sweep_usage",
31758
+ fix: `iamu triage ${refs2.join(" ")} --status dismissed --sweep`
31759
+ });
31584
31760
  return;
31585
31761
  }
31586
31762
  if (isSweep && opts.scope !== void 0) {
@@ -31623,6 +31799,9 @@ async function triage(refs2, opts) {
31623
31799
  "finding",
31624
31800
  ref
31625
31801
  );
31802
+ if (!entity) {
31803
+ entity = await resolveDesignerRefInTriage(projectRoot, ref);
31804
+ }
31626
31805
  } catch (err) {
31627
31806
  commandError(
31628
31807
  `Finding ${ref} does not validate: ${err instanceof Error ? err.message.split("\n")[0] : String(err)} \u2014 run \`iamu validate --all\``,
@@ -31699,29 +31878,25 @@ async function triage(refs2, opts) {
31699
31878
  }
31700
31879
  const allTargets = [...loaded, ...swept];
31701
31880
  const persona = allTargets.filter((f) => isPersonaFinding(f.frontmatter));
31702
- if (!isSweep && opts.rule !== void 0 && persona.length < loaded.length) {
31703
- commandError(
31704
- "--rule teaches the walker, and a designer finding is not the walker's: dismiss it without --rule (later designer's reviews are told not to raise it again)",
31705
- {
31706
- ...opts,
31707
- code: "rule_for_designer_finding",
31708
- fix: `iamu triage ${refs2.join(" ")} --status dismissed --note "<why>"`
31709
- }
31710
- );
31711
- return;
31712
- }
31713
31881
  const effectiveReason = reason ?? (isSweep ? "not_relevant" : null);
31714
- const teaches = (status2.data === "dismissed" || isSweep && status2.data === "wont_fix") && (effectiveReason === "not_relevant" || effectiveReason === "walker_error") && opts.guidance !== false && (persona.length > 0 || isSweep);
31882
+ const duplicateTarget = status2.data === "dismissed" && effectiveReason === "duplicate" ? opts.duplicateOf?.trim() || null : null;
31883
+ const teaches = (status2.data === "dismissed" || isSweep && status2.data === "wont_fix") && (effectiveReason === "not_relevant" || effectiveReason === "walker_error" || effectiveReason === "out_of_scope") && opts.guidance !== false && (persona.length > 0 || isSweep || opts.rule !== void 0);
31715
31884
  let scope = {};
31716
31885
  if (teaches && !isSweep) {
31717
- const resolved = await resolveScope(projectRoot, scopeFlag, persona, opts);
31886
+ const scopeTargets = persona.length > 0 ? persona : allTargets;
31887
+ const resolved = await resolveScope(
31888
+ projectRoot,
31889
+ scopeFlag,
31890
+ scopeTargets,
31891
+ opts
31892
+ );
31718
31893
  if (!resolved) return;
31719
31894
  scope = resolved;
31720
31895
  }
31721
31896
  let guidance = null;
31722
31897
  if (teaches && effectiveReason) {
31723
31898
  const ruleSources = allTargets.length > 0 ? allTargets : persona;
31724
- const ruleText = opts.rule?.trim() || (sweepQuery && ruleSources.length === 0 ? effectiveReason === "walker_error" ? `Earlier walkers got this wrong: ${sweepQuery}` : `Don't report: ${sweepQuery}` : defaultRule(effectiveReason, ruleSources));
31899
+ const ruleText = opts.rule?.trim() || (sweepQuery && ruleSources.length === 0 ? effectiveReason === "walker_error" ? `Earlier walkers got this wrong: ${sweepQuery}` : effectiveReason === "out_of_scope" ? `Handled outside system; don't report: ${sweepQuery}` : `Don't report: ${sweepQuery}` : defaultRule(effectiveReason, ruleSources));
31725
31900
  const rule = await createGuidanceRule(projectRoot, {
31726
31901
  kind: effectiveReason,
31727
31902
  rule: ruleText,
@@ -31762,7 +31937,8 @@ async function triage(refs2, opts) {
31762
31937
  updated_at: now,
31763
31938
  addressed_at: addressedAt(f.frontmatter),
31764
31939
  dismiss_reason: status2.data === "dismissed" ? effectiveReason : null,
31765
- ...isSweep && guidance ? { suppressed_by_guidance: guidance.id } : {}
31940
+ duplicate_of: duplicateTarget,
31941
+ ...(isSweep || !isPersonaFinding(f.frontmatter)) && guidance ? { suppressed_by_guidance: guidance.id } : {}
31766
31942
  };
31767
31943
  if (opts.note !== void 0) fm.triage_note = opts.note.trim();
31768
31944
  await writeEntity(projectRoot, "finding", fm, f.body);
@@ -31790,6 +31966,7 @@ async function triage(refs2, opts) {
31790
31966
  updated_at: now,
31791
31967
  addressed_at: addressedAt(c.frontmatter),
31792
31968
  dismiss_reason: status2.data === "dismissed" ? effectiveReason : null,
31969
+ duplicate_of: duplicateTarget,
31793
31970
  ...isSweep && guidance ? { suppressed_by_guidance: guidance.id } : {},
31794
31971
  ...opts.note !== void 0 ? { triage_note: opts.note.trim() } : {}
31795
31972
  },
@@ -32877,7 +33054,7 @@ var init_browse_driver = __esm({
32877
33054
  });
32878
33055
 
32879
33056
  // src/commands/step-numbering.ts
32880
- import { open, readdir, unlink } from "fs/promises";
33057
+ import { open, readdir, stat as stat2, unlink } from "fs/promises";
32881
33058
  import path61 from "path";
32882
33059
  function stepFileName(stepNumber2) {
32883
33060
  return `${String(stepNumber2).padStart(3, "0")}.json`;
@@ -32890,7 +33067,16 @@ async function peekNextStepNumber(stepsDir) {
32890
33067
  let max = 0;
32891
33068
  for (const f of files) {
32892
33069
  const m = /^(\d+)\.json$/.exec(f);
32893
- if (m) max = Math.max(max, Number(m[1]));
33070
+ if (!m) continue;
33071
+ const fullPath = path61.join(stepsDir, f);
33072
+ const st = await stat2(fullPath).catch(() => null);
33073
+ if (!st) continue;
33074
+ if (st.size === 0 && Date.now() - st.mtimeMs > STALE_RESERVATION_MS) {
33075
+ await unlink(fullPath).catch(() => {
33076
+ });
33077
+ continue;
33078
+ }
33079
+ max = Math.max(max, Number(m[1]));
32894
33080
  }
32895
33081
  return max + 1;
32896
33082
  }
@@ -32913,13 +33099,21 @@ async function reserveStepFile(stepsDir, limit) {
32913
33099
  };
32914
33100
  } catch (err) {
32915
33101
  if (err.code !== "EEXIST") throw err;
33102
+ const st = await stat2(stepPath).catch(() => null);
33103
+ if (st && st.size === 0 && Date.now() - st.mtimeMs > STALE_RESERVATION_MS) {
33104
+ await unlink(stepPath).catch(() => {
33105
+ });
33106
+ continue;
33107
+ }
32916
33108
  candidate = Math.max(candidate + 1, await peekNextStepNumber(stepsDir));
32917
33109
  }
32918
33110
  }
32919
33111
  }
33112
+ var STALE_RESERVATION_MS;
32920
33113
  var init_step_numbering = __esm({
32921
33114
  "src/commands/step-numbering.ts"() {
32922
33115
  "use strict";
33116
+ STALE_RESERVATION_MS = 2e3;
32923
33117
  }
32924
33118
  });
32925
33119
 
@@ -32931,7 +33125,7 @@ __export(step_exports, {
32931
33125
  recordStep: () => recordStep,
32932
33126
  stepCommand: () => stepCommand
32933
33127
  });
32934
- import { copyFile, mkdir as mkdir4, rename, stat as stat2 } from "fs/promises";
33128
+ import { copyFile, mkdir as mkdir4, rename, stat as stat3 } from "fs/promises";
32935
33129
  import path62 from "path";
32936
33130
  import chalk30 from "chalk";
32937
33131
  function parseMaxSteps(val) {
@@ -32961,9 +33155,10 @@ async function recordStep(input) {
32961
33155
  runId: input.runId
32962
33156
  });
32963
33157
  const { stepNumber: stepNumber2, stepPath } = reservation;
33158
+ let parsedStep;
33159
+ let finalScreenshotPath = null;
32964
33160
  try {
32965
33161
  let screenshotRef = null;
32966
- let finalScreenshotPath = null;
32967
33162
  if (shotSource) {
32968
33163
  const destName = `step-${stepNumber2}.png`;
32969
33164
  const destPath = path62.join(screenshotsDir, destName);
@@ -32979,7 +33174,7 @@ async function recordStep(input) {
32979
33174
  }
32980
33175
  screenshotRef = path62.posix.join("screenshots", destName);
32981
33176
  }
32982
- const parsedStep = runStepSchema.parse({
33177
+ parsedStep = runStepSchema.parse({
32983
33178
  step_number: stepNumber2,
32984
33179
  timestamp: input.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
32985
33180
  action: input.action.trim(),
@@ -32991,33 +33186,33 @@ async function recordStep(input) {
32991
33186
  ...input.extra ? { extra: input.extra } : {}
32992
33187
  });
32993
33188
  await writeStep(runDirPath, parsedStep);
32994
- let cancelled = false;
32995
- try {
32996
- const liveRes = await sendLiveStep(
32997
- input.projectRoot,
32998
- input.runId,
32999
- parsedStep,
33000
- finalScreenshotPath
33001
- );
33002
- if (liveRes.cancelled) {
33003
- cancelled = true;
33004
- }
33005
- } catch {
33006
- }
33007
- return {
33008
- step: parsedStep,
33009
- stepPath,
33010
- screenshotPath: finalScreenshotPath,
33011
- cancelled
33012
- };
33013
33189
  } catch (err) {
33014
33190
  await reservation.release();
33015
33191
  throw err;
33016
33192
  }
33193
+ let cancelled = false;
33194
+ try {
33195
+ const liveRes = await sendLiveStep(
33196
+ input.projectRoot,
33197
+ input.runId,
33198
+ parsedStep,
33199
+ finalScreenshotPath
33200
+ );
33201
+ if (liveRes.cancelled) {
33202
+ cancelled = true;
33203
+ }
33204
+ } catch {
33205
+ }
33206
+ return {
33207
+ step: parsedStep,
33208
+ stepPath,
33209
+ screenshotPath: finalScreenshotPath,
33210
+ cancelled
33211
+ };
33017
33212
  }
33018
33213
  async function existingShot(shot) {
33019
33214
  const resolved = path62.resolve(shot);
33020
- const isFile = await stat2(resolved).then((s) => s.isFile()).catch(() => false);
33215
+ const isFile = await stat3(resolved).then((s) => s.isFile()).catch(() => false);
33021
33216
  if (!isFile) {
33022
33217
  throw new Error(
33023
33218
  `Screenshot not found: ${resolved} \u2014 save the screenshot first, or omit --shot.`
@@ -33353,6 +33548,7 @@ function chromeArgs(opts, port, headless, viewport) {
33353
33548
  `--window-size=${win.width},${win.height}`,
33354
33549
  "--no-first-run",
33355
33550
  "--no-default-browser-check",
33551
+ "--disable-features=Translate",
33356
33552
  "--password-store=basic",
33357
33553
  "--use-mock-keychain",
33358
33554
  ...headless ? ["--headless=new", "--disable-dev-shm-usage", "--disable-gpu"] : [],
@@ -38352,7 +38548,7 @@ async function crawlFrom(page, opts, origin, takeBlocked) {
38352
38548
  const byKey = /* @__PURE__ */ new Map();
38353
38549
  const byPlaceView = /* @__PURE__ */ new Map();
38354
38550
  const signatureOf = (s) => exploreSignature(s.headings, s.title);
38355
- function addSurface(state, place, view, path73, depth) {
38551
+ function addSurface(state, place, view, path74, depth) {
38356
38552
  const key = exploreSurfaceKey(place, view);
38357
38553
  const surface = {
38358
38554
  key,
@@ -38364,7 +38560,7 @@ async function crawlFrom(page, opts, origin, takeBlocked) {
38364
38560
  headings: state.headings,
38365
38561
  fields: state.fields,
38366
38562
  depth,
38367
- path: path73,
38563
+ path: path74,
38368
38564
  actions: []
38369
38565
  };
38370
38566
  surfaces.push(surface);
@@ -38382,9 +38578,9 @@ ${surface.signature}`, key);
38382
38578
  });
38383
38579
  await settle(page, settleMs);
38384
38580
  }
38385
- async function replay(path73) {
38386
- if (!("goto" in (path73[0] ?? {}))) await goto(opts.startUrl);
38387
- for (const step of path73) {
38581
+ async function replay(path74) {
38582
+ if (!("goto" in (path74[0] ?? {}))) await goto(opts.startUrl);
38583
+ for (const step of path74) {
38388
38584
  if ("goto" in step) {
38389
38585
  await goto(step.goto);
38390
38586
  } else {
@@ -38508,8 +38704,8 @@ ${sig}`, key);
38508
38704
  if (stats.surfaces >= budget.max_surfaces) {
38509
38705
  stats.truncated = true;
38510
38706
  } else {
38511
- const path73 = !sameDoc && view === null ? [{ goto: after.url }] : [...surface.path, ...via, click];
38512
- addSurface(after, place, view, path73, surface.depth + 1);
38707
+ const path74 = !sameDoc && view === null ? [{ goto: after.url }] : [...surface.path, ...via, click];
38708
+ addSurface(after, place, view, path74, surface.depth + 1);
38513
38709
  }
38514
38710
  }
38515
38711
  }
@@ -39434,7 +39630,9 @@ function guidanceLine(r) {
39434
39630
  return r.reason ? `- ${rule} (${r.reason.replace(/\s+/g, " ")})` : `- ${rule}`;
39435
39631
  }
39436
39632
  function guidanceSection(rules) {
39437
- const notRelevant = rules.filter((r) => r.kind === "not_relevant");
39633
+ const notRelevant = rules.filter(
39634
+ (r) => r.kind === "not_relevant" || r.kind === "out_of_scope"
39635
+ );
39438
39636
  const misreads = rules.filter((r) => r.kind === "walker_error");
39439
39637
  if (notRelevant.length === 0 && misreads.length === 0) return [];
39440
39638
  const out2 = ["", "Tester guidance \u2014 the team's decisions from earlier runs:"];
@@ -39521,7 +39719,8 @@ function composeWalkerPrompt(p) {
39521
39719
  "Squinting Guardrails:",
39522
39720
  "\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.",
39523
39721
  "\u2022 Squinting does NOT permit drifting to unrelated settings, account profiles, or off-target pages. Stay strictly within the scenario journey.",
39524
- "\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)."
39722
+ "\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).",
39723
+ "\u2022 If the expected state includes seeded defects inside an inspected subject (e.g. an embedded app preview, screenshot, submission, or failing check that your persona is supposed to review or flag), treat those defects as the subject data to act on \u2014 never report them as bugs in the host product itself."
39525
39724
  ].filter(Boolean) : [],
39526
39725
  ...p.untestableBoundaries && p.untestableBoundaries.length > 0 ? [
39527
39726
  "",
@@ -41098,6 +41297,235 @@ var init_designer = __esm({
41098
41297
  }
41099
41298
  });
41100
41299
 
41300
+ // src/commands/post-walk.ts
41301
+ var post_walk_exports = {};
41302
+ __export(post_walk_exports, {
41303
+ postWalk: () => postWalk
41304
+ });
41305
+ import { existsSync as existsSync23, promises as fs71 } from "fs";
41306
+ import path73 from "path";
41307
+ import chalk49 from "chalk";
41308
+ async function captureStageJson(fn) {
41309
+ const captured = [];
41310
+ const origLog = console.log;
41311
+ const origErr = console.error;
41312
+ console.log = (...args2) => {
41313
+ captured.push(args2.map(String).join(" "));
41314
+ };
41315
+ console.error = () => {
41316
+ };
41317
+ try {
41318
+ await fn();
41319
+ } finally {
41320
+ console.log = origLog;
41321
+ console.error = origErr;
41322
+ }
41323
+ const last = captured[captured.length - 1];
41324
+ if (!last) return null;
41325
+ try {
41326
+ return JSON.parse(last);
41327
+ } catch {
41328
+ return null;
41329
+ }
41330
+ }
41331
+ async function postWalk(runIdArg, opts = {}) {
41332
+ const ctx = await resolveProject(opts);
41333
+ if (!ctx) return;
41334
+ const { projectRoot } = ctx;
41335
+ const runId = resolveRunId(projectRoot, runIdArg ?? opts.run);
41336
+ if (!runId) {
41337
+ commandError(
41338
+ "Run ID is required. Pass <run-id>, specify --run <run-id>, or start a run first.",
41339
+ {
41340
+ ...opts,
41341
+ code: "missing_run_id",
41342
+ fix: "iamu status --json"
41343
+ }
41344
+ );
41345
+ return;
41346
+ }
41347
+ const runDirPath = resolveRunDir(projectRoot, runId, opts);
41348
+ if (!runDirPath) return;
41349
+ const manifestPath = path73.join(runDirPath, "walkthrough.json");
41350
+ let manifest;
41351
+ try {
41352
+ manifest = JSON.parse(
41353
+ await fs71.readFile(manifestPath, "utf8")
41354
+ );
41355
+ } catch {
41356
+ commandError(`Run not found: ${runId}`, {
41357
+ ...opts,
41358
+ code: "run_not_found",
41359
+ fix: "iamu status --json"
41360
+ });
41361
+ return;
41362
+ }
41363
+ if (manifest.status !== "completed") {
41364
+ await captureStageJson(
41365
+ () => complete(runId, { prompt: false, push: false, json: true })
41366
+ );
41367
+ if (process.exitCode && process.exitCode !== 0) return;
41368
+ manifest = JSON.parse(
41369
+ await fs71.readFile(manifestPath, "utf8")
41370
+ );
41371
+ }
41372
+ if (manifest.blocked) {
41373
+ const blockedResult = {
41374
+ run_id: runId,
41375
+ status: "blocked",
41376
+ blocked: manifest.blocked,
41377
+ recorded: [],
41378
+ stages: null
41379
+ };
41380
+ if (opts.json) {
41381
+ console.log(JSON.stringify(blockedResult, null, 2));
41382
+ } else {
41383
+ console.log(
41384
+ ` ${chalk49.yellow("!")} Run ${runId} is blocked: ${manifest.blocked.reason}`
41385
+ );
41386
+ }
41387
+ return;
41388
+ }
41389
+ const recordedNow = [];
41390
+ const matchesAnswerPath = path73.join(runDirPath, "matches.json");
41391
+ if (existsSync23(matchesAnswerPath)) {
41392
+ const res = await captureStageJson(
41393
+ () => matchRecord(runId, { file: matchesAnswerPath, json: true })
41394
+ );
41395
+ if (res && !process.exitCode) {
41396
+ recordedNow.push("match");
41397
+ await fs71.unlink(matchesAnswerPath).catch(() => {
41398
+ });
41399
+ }
41400
+ }
41401
+ const judgeAnswerPath = path73.join(runDirPath, "judge_answer.json");
41402
+ if (existsSync23(judgeAnswerPath)) {
41403
+ const res = await captureStageJson(
41404
+ () => evaluateRecord(runId, { file: judgeAnswerPath, json: true })
41405
+ );
41406
+ if (res && !process.exitCode) {
41407
+ recordedNow.push("evaluate");
41408
+ await fs71.unlink(judgeAnswerPath).catch(() => {
41409
+ });
41410
+ }
41411
+ }
41412
+ const adviseAnswerPath = path73.join(runDirPath, "advise_answer.json");
41413
+ if (!opts.critic && !existsSync23(path73.join(runDirPath, ADVICE_FILE)) && existsSync23(adviseAnswerPath)) {
41414
+ const res = await captureStageJson(
41415
+ () => adviseRecord(runId, { file: adviseAnswerPath, json: true })
41416
+ );
41417
+ if (res && !process.exitCode) {
41418
+ recordedNow.push("advise");
41419
+ await fs71.unlink(adviseAnswerPath).catch(() => {
41420
+ });
41421
+ }
41422
+ }
41423
+ const designerAnswerPath = path73.join(runDirPath, DESIGNER_ANSWER_FILE);
41424
+ if (!existingDesignerFile(runDirPath)) {
41425
+ if (opts.critic) {
41426
+ await captureStageJson(
41427
+ () => designerRecord(runId, { skipped: "--critic", json: true })
41428
+ );
41429
+ recordedNow.push("designer(skipped)");
41430
+ } else if (opts.ux === false) {
41431
+ await captureStageJson(
41432
+ () => designerRecord(runId, { skipped: "--no-ux", json: true })
41433
+ );
41434
+ recordedNow.push("designer(skipped)");
41435
+ } else if (existsSync23(designerAnswerPath)) {
41436
+ const res = await captureStageJson(
41437
+ () => designerRecord(runId, {
41438
+ file: designerAnswerPath,
41439
+ byOrchestrator: opts.byOrchestrator,
41440
+ json: true
41441
+ })
41442
+ );
41443
+ if (res && !process.exitCode) {
41444
+ recordedNow.push("designer");
41445
+ await fs71.unlink(designerAnswerPath).catch(() => {
41446
+ });
41447
+ }
41448
+ }
41449
+ }
41450
+ const matchInfo = await captureStageJson(() => match(runId, { json: true }));
41451
+ const evalRecorded = existsSync23(path73.join(runDirPath, "verdicts.json"));
41452
+ const evalInfo = evalRecorded ? null : await captureStageJson(() => evaluate(runId, { json: true }));
41453
+ const adviseRecorded = existsSync23(path73.join(runDirPath, ADVICE_FILE));
41454
+ const adviseInfo = opts.critic || adviseRecorded ? null : await captureStageJson(() => advise(runId, { json: true }));
41455
+ const designerRecorded = Boolean(existingDesignerFile(runDirPath));
41456
+ const designerInfo = opts.critic || opts.ux === false || designerRecorded ? null : await captureStageJson(() => designer(runId, { json: true }));
41457
+ const payload = {
41458
+ run_id: runId,
41459
+ status: "completed",
41460
+ recorded: recordedNow,
41461
+ stages: {
41462
+ match: {
41463
+ needed: Boolean(matchInfo?.needed),
41464
+ prompt_file: matchInfo?.prompt_file ?? null,
41465
+ prompt_file_abs: matchInfo?.prompt_file_abs ?? null,
41466
+ answer_file_abs: matchesAnswerPath,
41467
+ record_command: `iamu match-record ${runId} --file - <<'EOF'`
41468
+ },
41469
+ evaluate: {
41470
+ needed: !evalRecorded,
41471
+ recorded: evalRecorded,
41472
+ prompt_file: evalInfo?.prompt_file ?? null,
41473
+ prompt_file_abs: evalInfo?.prompt_file_abs ?? null,
41474
+ answer_file_abs: judgeAnswerPath,
41475
+ record_command: `iamu evaluate-record ${runId} --file - <<'EOF'`
41476
+ },
41477
+ advise: {
41478
+ needed: Boolean(adviseInfo?.needed),
41479
+ recorded: adviseRecorded,
41480
+ reason: opts.critic ? "--critic" : adviseInfo?.reason ?? null,
41481
+ prompt_file: adviseInfo?.prompt_file ?? null,
41482
+ prompt_file_abs: adviseInfo?.prompt_file_abs ?? null,
41483
+ answer_file_abs: adviseAnswerPath,
41484
+ record_command: `iamu advise-record ${runId} --file - <<'EOF'`
41485
+ },
41486
+ designer: {
41487
+ needed: Boolean(designerInfo?.needed),
41488
+ recorded: designerRecorded,
41489
+ reason: opts.critic ? "--critic" : opts.ux === false ? "--no-ux" : designerInfo?.reason ?? null,
41490
+ prompt_file: designerInfo?.prompt_file ?? null,
41491
+ prompt_file_abs: designerInfo?.prompt_file_abs ?? null,
41492
+ answer_file_abs: designerAnswerPath,
41493
+ key_screenshots: designerInfo?.key_screenshots ?? [],
41494
+ offer_template: Boolean(designerInfo?.offer_template),
41495
+ record_command: `iamu designer-record ${runId} --file - <<'EOF'`
41496
+ }
41497
+ }
41498
+ };
41499
+ if (opts.json) {
41500
+ console.log(JSON.stringify(payload, null, 2));
41501
+ return;
41502
+ }
41503
+ console.log();
41504
+ console.log(
41505
+ ` ${chalk49.green("\u2713")} Post-walk pipeline for ${chalk49.cyan(runId)}`
41506
+ );
41507
+ if (recordedNow.length > 0) {
41508
+ console.log(` Recorded: ${recordedNow.join(", ")}`);
41509
+ }
41510
+ for (const [name, s] of Object.entries(payload.stages)) {
41511
+ const state = "recorded" in s && s.recorded ? chalk49.green("recorded") : s.needed ? chalk49.yellow(`needed (${s.prompt_file})`) : chalk49.dim("not needed");
41512
+ console.log(` ${name}: ${state}`);
41513
+ }
41514
+ console.log();
41515
+ }
41516
+ var init_post_walk = __esm({
41517
+ "src/commands/post-walk.ts"() {
41518
+ "use strict";
41519
+ init_dist2();
41520
+ init_advise();
41521
+ init_complete();
41522
+ init_context4();
41523
+ init_designer();
41524
+ init_evaluate();
41525
+ init_match();
41526
+ }
41527
+ });
41528
+
41101
41529
  // src/commands/prune.ts
41102
41530
  var prune_exports = {};
41103
41531
  __export(prune_exports, {
@@ -41105,7 +41533,7 @@ __export(prune_exports, {
41105
41533
  prune: () => prune
41106
41534
  });
41107
41535
  import { confirm, isCancel as isCancel5 } from "@clack/prompts";
41108
- import chalk49 from "chalk";
41536
+ import chalk50 from "chalk";
41109
41537
  function formatBytes(bytes) {
41110
41538
  if (bytes <= 0) return "0 B";
41111
41539
  if (bytes < 1024) return `${bytes} B`;
@@ -41151,7 +41579,7 @@ async function prune(opts = {}) {
41151
41579
  });
41152
41580
  if (preview.prunedRuns.length === 0) {
41153
41581
  console.log(
41154
- chalk49.dim(" No runs match the pruning criteria. Nothing to prune.")
41582
+ chalk50.dim(" No runs match the pruning criteria. Nothing to prune.")
41155
41583
  );
41156
41584
  return preview;
41157
41585
  }
@@ -41160,7 +41588,7 @@ async function prune(opts = {}) {
41160
41588
  message: `Prune ${mode} ${preview.prunedRuns.length} run(s) and reclaim ${formatBytes(preview.totalBytesReclaimed)}?`
41161
41589
  });
41162
41590
  if (isCancel5(confirmed) || !confirmed) {
41163
- console.log(chalk49.yellow(" Pruning cancelled."));
41591
+ console.log(chalk50.yellow(" Pruning cancelled."));
41164
41592
  return;
41165
41593
  }
41166
41594
  }
@@ -41171,14 +41599,14 @@ async function prune(opts = {}) {
41171
41599
  }
41172
41600
  if (result.dryRun) {
41173
41601
  console.log(
41174
- chalk49.cyan(
41602
+ chalk50.cyan(
41175
41603
  `
41176
41604
  [DRY RUN] Would prune ${result.prunedRuns.length} of ${result.totalRunsScanned} run(s):`
41177
41605
  )
41178
41606
  );
41179
41607
  } else {
41180
41608
  console.log(
41181
- chalk49.bold(
41609
+ chalk50.bold(
41182
41610
  `
41183
41611
  ${symbols.success} Pruned ${result.prunedRuns.length} of ${result.totalRunsScanned} run(s):`
41184
41612
  )
@@ -41186,18 +41614,18 @@ async function prune(opts = {}) {
41186
41614
  }
41187
41615
  if (result.prunedRuns.length === 0) {
41188
41616
  console.log(
41189
- chalk49.dim(" No runs matched the pruning criteria. Nothing pruned.")
41617
+ chalk50.dim(" No runs matched the pruning criteria. Nothing pruned.")
41190
41618
  );
41191
41619
  } else {
41192
41620
  for (const run of result.prunedRuns) {
41193
41621
  const mode = run.fullyDeleted ? "removed" : "screenshots purged";
41194
41622
  const count = run.screenshotsDeleted > 0 ? `${run.screenshotsDeleted} screenshots, ` : "";
41195
41623
  console.log(
41196
- ` ${chalk49.dim("\u2022")} ${chalk49.bold(run.runId)} ${chalk49.dim(`(${run.scenarioSlug ?? "unknown scenario"})`)}: ${chalk49.yellow(formatBytes(run.bytesReclaimed))} (${count}${mode})`
41624
+ ` ${chalk50.dim("\u2022")} ${chalk50.bold(run.runId)} ${chalk50.dim(`(${run.scenarioSlug ?? "unknown scenario"})`)}: ${chalk50.yellow(formatBytes(run.bytesReclaimed))} (${count}${mode})`
41197
41625
  );
41198
41626
  }
41199
41627
  console.log(
41200
- chalk49.dim(
41628
+ chalk50.dim(
41201
41629
  `
41202
41630
  Total reclaimed: ${formatBytes(result.totalBytesReclaimed)} (${result.totalScreenshotsDeleted} screenshots deleted)
41203
41631
  `
@@ -41218,9 +41646,9 @@ var init_prune2 = __esm({
41218
41646
  // src/bin.ts
41219
41647
  init_dist();
41220
41648
  init_context4();
41221
- import { existsSync as existsSync23 } from "fs";
41649
+ import { existsSync as existsSync24 } from "fs";
41222
41650
  import { resolve as resolve3 } from "path";
41223
- import chalk52 from "chalk";
41651
+ import chalk53 from "chalk";
41224
41652
  import { Command } from "commander";
41225
41653
 
41226
41654
  // src/commands/register-cloud.ts
@@ -41510,12 +41938,12 @@ function registerFindingsCommands(program2) {
41510
41938
  await findings2(opts);
41511
41939
  }
41512
41940
  );
41513
- program2.command("triage [findings...]").helpGroup("Findings:").description("Set findings' triage status").requiredOption(
41514
- "--status <status>",
41515
- "open, addressed, deferred, wont_fix or dismissed"
41516
- ).option("--note <text>", "Why \u2014 required when dismissing").option(
41941
+ program2.command("triage [findings...]").helpGroup("Findings:").description("Set findings' triage status").requiredOption("--status <status>", "open, addressed or dismissed").option("--note <text>", "Optional triage note or reason").option(
41517
41942
  "--reason <reason>",
41518
- "Dismiss reason: not_relevant (default), walker_error or duplicate"
41943
+ "Dismiss reason: not_relevant (default), walker_error, out_of_scope or duplicate"
41944
+ ).option(
41945
+ "--duplicate-of <finding>",
41946
+ "Finding slug or id this repeats (implies --reason duplicate)"
41519
41947
  ).option(
41520
41948
  "--rule <text>",
41521
41949
  `Guidance rule text (default: "Don't report: <title>")`
@@ -41527,7 +41955,7 @@ function registerFindingsCommands(program2) {
41527
41955
  "Project-wide sweep ('we never want X'): dismiss all matching open persona & designer findings across every scenario/surface and record a project-wide guidance rule"
41528
41956
  ).option("--no-guidance", "Dismiss without writing a tester guidance rule").option("--json", "Output as JSON").addHelpText(
41529
41957
  "after",
41530
- "\nDismissing as not_relevant or walker_error teaches the walker: it writes a\ntester guidance rule (skip it with --no-guidance)."
41958
+ "\nDismissing as not_relevant, walker_error, or out_of_scope teaches the walker: it writes a\ntester guidance rule (skip it with --no-guidance)."
41531
41959
  ).action(
41532
41960
  async (refs2, opts) => {
41533
41961
  const { triage: triage2 } = await Promise.resolve().then(() => (init_triage(), triage_exports));
@@ -41626,9 +42054,9 @@ function registerProjectCommands(program2) {
41626
42054
  "after",
41627
42055
  "\nExamples:\n $ iamu validate --all\n $ iamu validate --all --fix --dry-run\n $ iamu validate iamu/personas/maria.md"
41628
42056
  ).action(
41629
- async (path73, opts) => {
42057
+ async (path74, opts) => {
41630
42058
  const { validate: validate2 } = await Promise.resolve().then(() => (init_validate(), validate_exports));
41631
- await validate2(path73, opts);
42059
+ await validate2(path74, opts);
41632
42060
  }
41633
42061
  );
41634
42062
  program2.command("update").helpGroup("Project:").description("Update and repair agent skills and prompts to latest version").option(
@@ -42070,6 +42498,20 @@ function registerWalkthroughCommands(program2) {
42070
42498
  await complete2(runId, opts);
42071
42499
  }
42072
42500
  );
42501
+ program2.command("post-walk [run-id]").helpGroup("Walkthrough:").description(
42502
+ "Complete a run, record staged answers, and prepare post-walk prompts"
42503
+ ).option("--run <run-id>", "Run ID (or pass as positional argument)").option("--critic", "Skip advisor and mark designer skipped (--critic)").option("--no-ux", "Mark designer's review skipped (--no-ux)").option(
42504
+ "--by-orchestrator",
42505
+ "Mark recorded designer answer as written by the orchestrator"
42506
+ ).option("--json", "Output post-walk status and prompt paths as JSON").addHelpText(
42507
+ "after",
42508
+ "\nExamples:\n $ iamu post-walk <run-id> --json\n $ iamu post-walk <run-id> --critic --json"
42509
+ ).action(
42510
+ async (runId, opts) => {
42511
+ const { postWalk: postWalk2 } = await Promise.resolve().then(() => (init_post_walk(), post_walk_exports));
42512
+ await postWalk2(runId, opts);
42513
+ }
42514
+ );
42073
42515
  program2.command("match [run-id]").helpGroup("Walkthrough:").description(
42074
42516
  "Compose the prompt that links this run's findings to earlier ones"
42075
42517
  ).option("--run <run-id>", "Run ID (or pass as positional argument)").option("--json", "Output the match input bundle as JSON").option("--prompt-file <path>", "Write the generated prompt to a file").action(
@@ -42228,7 +42670,7 @@ init_logger();
42228
42670
  // src/ui/panic.ts
42229
42671
  init_dist();
42230
42672
  init_logger();
42231
- import chalk50 from "chalk";
42673
+ import chalk51 from "chalk";
42232
42674
  var handlersRegistered = false;
42233
42675
  var customSigintHandler = null;
42234
42676
  function logFatalAndExit(title, errOrReason) {
@@ -42248,16 +42690,16 @@ function logFatalAndExit(title, errOrReason) {
42248
42690
  process.exit(1);
42249
42691
  }
42250
42692
  if (isVerbose()) {
42251
- console.error(chalk50.red(`
42693
+ console.error(chalk51.red(`
42252
42694
  ${title}:`));
42253
42695
  console.error(
42254
42696
  errOrReason instanceof Error ? errOrReason.stack || errOrReason : errOrReason
42255
42697
  );
42256
42698
  } else {
42257
- console.error(chalk50.red(`
42699
+ console.error(chalk51.red(`
42258
42700
  Fatal Error: ${msg}`));
42259
42701
  console.error(
42260
- chalk50.dim(
42702
+ chalk51.dim(
42261
42703
  " Run with --verbose or DEBUG=1 to view the full stack trace.\n"
42262
42704
  )
42263
42705
  );
@@ -42276,7 +42718,7 @@ function setupProcessHandlers() {
42276
42718
  }
42277
42719
  process.exit(130);
42278
42720
  } else {
42279
- console.error(chalk50.dim("\n Operation cancelled."));
42721
+ console.error(chalk51.dim("\n Operation cancelled."));
42280
42722
  process.exit(130);
42281
42723
  }
42282
42724
  });
@@ -42302,7 +42744,7 @@ function setupProcessHandlers() {
42302
42744
  import { mkdir as mkdir7, readFile as readFile8, writeFile as writeFile5 } from "fs/promises";
42303
42745
  import { homedir as homedir4 } from "os";
42304
42746
  import { join as join12 } from "path";
42305
- import chalk51 from "chalk";
42747
+ import chalk52 from "chalk";
42306
42748
  var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
42307
42749
  function getIamuHome() {
42308
42750
  return process.env.IAMU_HOME || join12(homedir4(), ".iamu");
@@ -42387,7 +42829,7 @@ async function checkForUpdate(currentVersion) {
42387
42829
  function printUpdateNotice(currentVersion, latestVersion) {
42388
42830
  console.error();
42389
42831
  console.error(
42390
- ` ${chalk51.yellow("Update available:")} ${chalk51.dim(currentVersion)} \u2192 ${chalk51.green(latestVersion)} ${chalk51.dim("(run: npm i -g @iamu-dev/iamu)")}`
42832
+ ` ${chalk52.yellow("Update available:")} ${chalk52.dim(currentVersion)} \u2192 ${chalk52.green(latestVersion)} ${chalk52.dim("(run: npm i -g @iamu-dev/iamu)")}`
42391
42833
  );
42392
42834
  }
42393
42835
 
@@ -42418,7 +42860,7 @@ var program = new Command().name("iamu").description(
42418
42860
  const effectiveCwd = root.cwd || process.env.IAMU_DIR || process.env.IAMU_CWD;
42419
42861
  if (effectiveCwd) {
42420
42862
  const targetDir = resolve3(process.cwd(), effectiveCwd);
42421
- if (!existsSync23(targetDir)) {
42863
+ if (!existsSync24(targetDir)) {
42422
42864
  commandError(`Directory does not exist: ${effectiveCwd}`, {
42423
42865
  ...root,
42424
42866
  code: "directory_not_found",
@@ -42441,7 +42883,7 @@ var program = new Command().name("iamu").description(
42441
42883
  }
42442
42884
  }
42443
42885
  if (root.color === false || process.env.NO_COLOR) {
42444
- chalk52.level = 0;
42886
+ chalk53.level = 0;
42445
42887
  }
42446
42888
  if (root.verbose) {
42447
42889
  process.env.IAMU_VERBOSE = "1";