@iamu-dev/iamu 0.3.0-beta.0 → 0.3.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -5459,10 +5459,20 @@ async function readTestability(projectRoot) {
5459
5459
  body: parsed.body
5460
5460
  };
5461
5461
  }
5462
+ async function writeTestability(projectRoot, input, body) {
5463
+ const p = testabilityPath(projectRoot);
5464
+ const isLocalDoc = "frontmatter" in input && typeof input.frontmatter === "object";
5465
+ const fm = isLocalDoc ? input.frontmatter : input;
5466
+ const effectiveBody = body !== void 0 ? body : isLocalDoc ? input.body : "";
5467
+ const validated = testabilityFrontmatterSchema.parse(fm);
5468
+ const serialized = serializeFrontmatter(validated, effectiveBody);
5469
+ await atomicWriteFile(p, serialized);
5470
+ }
5462
5471
  var init_testability2 = __esm({
5463
5472
  "../../packages/local-store/dist/testability.js"() {
5464
5473
  "use strict";
5465
5474
  init_dist();
5475
+ init_atomic();
5466
5476
  init_markdown();
5467
5477
  init_paths();
5468
5478
  }
@@ -8621,7 +8631,7 @@ function formatRefError(err) {
8621
8631
  const dir = ENTITY_DIRS[err.missing_ref];
8622
8632
  const hint = MISSING_REF_HINTS[err.missing_ref] ?? "";
8623
8633
  if (!err.missing_slug) {
8624
- const fieldHint = err.missing_ref === "capability" && err.entity_type === "outline" ? "add `primary_capability:` to the outline frontmatter" : `add a \`${err.missing_ref}:\` field to the file`;
8634
+ const fieldHint = err.missing_ref === "capability" && err.entity_type === "outline" ? `run \`iamu set outline ${err.entity_slug ?? "<slug>"} --capability <slug>\`` : `add a \`${err.missing_ref}:\` field to the file`;
8625
8635
  return `${err.entity_type} ${err.entity_slug || err.entity_id}: ${err.missing_ref} slug is missing from frontmatter \u2014 ${fieldHint}`;
8626
8636
  }
8627
8637
  return `${err.entity_type} ${err.entity_slug || err.entity_id}: \`${err.missing_slug}\` not found in iamu/${dir}/${hint}`;
@@ -8635,7 +8645,7 @@ var init_refs = __esm({
8635
8645
  init_sync_tracking();
8636
8646
  init_walkthrough_mirror();
8637
8647
  MISSING_REF_HINTS = {
8638
- capability: " \u2014 create it with `iamu new capability` or check the slug in frontmatter",
8648
+ capability: " \u2014 create it with `iamu new capability` or set with `iamu set outline <slug> --capability <slug>`",
8639
8649
  outline: " \u2014 create it with `iamu new` or verify the slug matches a file in iamu/outlines/",
8640
8650
  persona: " \u2014 create it with `iamu new persona` or check the slug",
8641
8651
  scenario: " \u2014 verify the scenario exists locally with `iamu list scenarios`"
@@ -12636,7 +12646,8 @@ var init_symbols = __esm({
12636
12646
  add: chalk.cyan("+"),
12637
12647
  info: chalk.blue(supportsUnicode ? "\u2139" : "i"),
12638
12648
  dot: chalk.dim(supportsUnicode ? "\u2022" : "*"),
12639
- question: chalk.yellow("?")
12649
+ question: chalk.yellow("?"),
12650
+ step: chalk.blue(supportsUnicode ? "\u25B6" : ">")
12640
12651
  };
12641
12652
  }
12642
12653
  });
@@ -13581,8 +13592,8 @@ import { dirname as dirname3, join as join4, resolve as resolve2 } from "path";
13581
13592
  import { fileURLToPath as fileURLToPath2 } from "url";
13582
13593
  import chalk6 from "chalk";
13583
13594
  function getVersion() {
13584
- if ("0.3.0-beta.0") {
13585
- return "0.3.0-beta.0";
13595
+ if ("0.3.0-beta.2") {
13596
+ return "0.3.0-beta.2";
13586
13597
  }
13587
13598
  try {
13588
13599
  const thisDir = dirname3(fileURLToPath2(import.meta.url));
@@ -15952,9 +15963,16 @@ async function report(scenario, opts) {
15952
15963
  if (viewport) {
15953
15964
  console.log(` ${chalk13.dim("device:")} ${formatViewport(viewport)}`);
15954
15965
  }
15955
- const viewUrl = await pushedRunUrl(projectRoot, run.runId);
15956
- if (viewUrl) {
15957
- console.log(` ${chalk13.dim("view:")} ${chalk13.underline(viewUrl)}`);
15966
+ const config = await readConfig(projectRoot);
15967
+ if (config.cloud_linked && config.cloud_project_id) {
15968
+ const viewUrl = await pushedRunUrl(projectRoot, run.runId);
15969
+ if (viewUrl) {
15970
+ console.log(` ${chalk13.dim("view:")} ${chalk13.underline(viewUrl)}`);
15971
+ } else {
15972
+ console.log(
15973
+ ` ${chalk13.dim("view:")} ${chalk13.dim("not synced yet")} ${chalk13.dim("(run `iamu sync` to view in cloud)")}`
15974
+ );
15975
+ }
15958
15976
  }
15959
15977
  if (c) {
15960
15978
  const v = c.verification;
@@ -18635,23 +18653,38 @@ async function stepCommand(runArg, opts) {
18635
18653
  );
18636
18654
  return;
18637
18655
  }
18638
- console.log();
18639
- console.log(
18640
- ` ${symbols.success} Step ${chalk20.cyan(String(res.step.step_number))} recorded: ${res.step.action}`
18641
- );
18642
- if (res.step.note) {
18643
- console.log(` ${chalk20.dim("note:")} ${res.step.note}`);
18644
- }
18645
- if (res.step.url) {
18646
- console.log(` ${chalk20.dim("url:")} ${res.step.url}`);
18647
- }
18648
- if (res.step.screenshot) {
18649
- console.log(` ${chalk20.dim("shot:")} ${res.step.screenshot}`);
18656
+ const stepNumStr = String(res.step.step_number).padStart(2, "0");
18657
+ if (isVerbose()) {
18658
+ console.log();
18659
+ console.log(
18660
+ ` ${symbols.success} Step ${chalk20.cyan(stepNumStr)} recorded: ${res.step.action}`
18661
+ );
18662
+ if (res.step.note) {
18663
+ console.log(` ${chalk20.dim("note:")} ${res.step.note}`);
18664
+ }
18665
+ if (res.step.url) {
18666
+ console.log(` ${chalk20.dim("url:")} ${res.step.url}`);
18667
+ }
18668
+ if (res.step.screenshot) {
18669
+ console.log(` ${chalk20.dim("shot:")} ${res.step.screenshot}`);
18670
+ }
18671
+ console.log(
18672
+ ` ${chalk20.dim("file:")} ${path25.relative(projectRoot, res.stepPath)}`
18673
+ );
18674
+ console.log();
18675
+ } else {
18676
+ let route = "";
18677
+ if (res.step.url) {
18678
+ try {
18679
+ route = new URL(res.step.url).pathname;
18680
+ } catch {
18681
+ route = res.step.url;
18682
+ }
18683
+ }
18684
+ console.log(
18685
+ ` ${symbols.step} Step ${chalk20.cyan(stepNumStr)} \xB7 ${chalk20.bold(res.step.action)}${route ? chalk20.dim(` \xB7 ${route}`) : ""}`
18686
+ );
18650
18687
  }
18651
- console.log(
18652
- ` ${chalk20.dim("file:")} ${path25.relative(projectRoot, res.stepPath)}`
18653
- );
18654
- console.log();
18655
18688
  } catch (err) {
18656
18689
  commandError(err.message, opts);
18657
18690
  }
@@ -18662,6 +18695,7 @@ var init_step = __esm({
18662
18695
  "use strict";
18663
18696
  init_dist2();
18664
18697
  init_dist();
18698
+ init_logger();
18665
18699
  init_symbols();
18666
18700
  init_context4();
18667
18701
  init_live_run_client();
@@ -19246,16 +19280,34 @@ function printStep(opts, record, projectRoot, stagnationWarning, challengeWarnin
19246
19280
  console.log(JSON.stringify(doc, null, 2));
19247
19281
  return;
19248
19282
  }
19249
- console.log(
19250
- ` ${chalk21.green("\u2713")} Step ${chalk21.cyan(String(step.step_number))} recorded: ${step.action}`
19251
- );
19252
- for (const w of warnings) {
19253
- console.log(` ${chalk21.yellow("\u26A0\uFE0F ")} ${chalk21.yellow(w)}`);
19254
- }
19255
- if (step.note) console.log(` ${chalk21.dim("note:")} ${step.note}`);
19256
- if (step.url) console.log(` ${chalk21.dim("url:")} ${step.url}`);
19257
- if (step.screenshot) {
19258
- console.log(` ${chalk21.dim("screenshot:")} ${step.screenshot}`);
19283
+ const stepNumStr = String(step.step_number).padStart(2, "0");
19284
+ if (isVerbose()) {
19285
+ console.log(
19286
+ ` ${symbols.success} Step ${chalk21.cyan(stepNumStr)} recorded: ${step.action}`
19287
+ );
19288
+ for (const w of warnings) {
19289
+ console.log(` ${symbols.warning} ${chalk21.yellow(w)}`);
19290
+ }
19291
+ if (step.note) console.log(` ${chalk21.dim("note:")} ${step.note}`);
19292
+ if (step.url) console.log(` ${chalk21.dim("url:")} ${step.url}`);
19293
+ if (step.screenshot) {
19294
+ console.log(` ${chalk21.dim("screenshot:")} ${step.screenshot}`);
19295
+ }
19296
+ } else {
19297
+ let route = "";
19298
+ if (step.url) {
19299
+ try {
19300
+ route = new URL(step.url).pathname;
19301
+ } catch {
19302
+ route = step.url;
19303
+ }
19304
+ }
19305
+ console.log(
19306
+ ` ${symbols.step} Step ${chalk21.cyan(stepNumStr)} \xB7 ${chalk21.bold(step.action)}${route ? chalk21.dim(` \xB7 ${route}`) : ""}`
19307
+ );
19308
+ for (const w of warnings) {
19309
+ console.log(` ${symbols.warning} ${chalk21.yellow(w)}`);
19310
+ }
19259
19311
  }
19260
19312
  }
19261
19313
  async function browseStep(args, opts, driver) {
@@ -19562,6 +19614,8 @@ var init_browse = __esm({
19562
19614
  "use strict";
19563
19615
  init_dist2();
19564
19616
  init_dist();
19617
+ init_logger();
19618
+ init_symbols();
19565
19619
  init_browse_actions();
19566
19620
  init_browse_driver();
19567
19621
  init_context4();
@@ -20818,6 +20872,15 @@ function validateFile(filePath, raw, projectRoot) {
20818
20872
  );
20819
20873
  }
20820
20874
  }
20875
+ if (type2 === "outline" && zodResult.success) {
20876
+ const fm = zodResult.data;
20877
+ if (!fm.primary_capability && !fm.capability) {
20878
+ result.warnings = result.warnings ?? [];
20879
+ result.warnings.push(
20880
+ `outline has no primary capability \u2014 sync will require one (run \`iamu set outline ${fm.slug ?? "<slug>"} --capability <slug>\`)`
20881
+ );
20882
+ }
20883
+ }
20821
20884
  return result;
20822
20885
  }
20823
20886
  async function buildRepairLookups(projectRoot) {
@@ -21386,6 +21449,15 @@ async function newEntity(type2, opts) {
21386
21449
  );
21387
21450
  return;
21388
21451
  }
21452
+ if (entityType === "outline" && !opts.capability) {
21453
+ try {
21454
+ const { entities } = await listEntities(projectRoot, "capability");
21455
+ if (entities.length === 1 && typeof entities[0].frontmatter.slug === "string") {
21456
+ opts.capability = entities[0].frontmatter.slug;
21457
+ }
21458
+ } catch {
21459
+ }
21460
+ }
21389
21461
  const id = randomUUID10();
21390
21462
  const frontmatter = defaultFrontmatter(entityType, id, slug, opts.name, opts);
21391
21463
  const body = opts.body ?? (entityType === "outline" ? "" : opts.narrative) ?? "";
@@ -21504,6 +21576,42 @@ function applyChanges(type2, fm, opts) {
21504
21576
  put("paths", paths);
21505
21577
  }
21506
21578
  if (opts.area !== void 0) put("area", opts.area || null);
21579
+ } else if (type2 === "outline") {
21580
+ if (opts.capability === false) {
21581
+ put("primary_capability", null);
21582
+ put("capability", null);
21583
+ } else if (opts.capability !== void 0) {
21584
+ put("primary_capability", opts.capability || null);
21585
+ put("capability", opts.capability || null);
21586
+ }
21587
+ if (opts.additionalCapabilities === false) {
21588
+ put("additional_capabilities", []);
21589
+ } else if (opts.additionalCapabilities !== void 0) {
21590
+ const caps = typeof opts.additionalCapabilities === "string" ? splitList(opts.additionalCapabilities) : Array.isArray(opts.additionalCapabilities) ? opts.additionalCapabilities : [];
21591
+ put("additional_capabilities", caps);
21592
+ }
21593
+ } else if (type2 === "testability") {
21594
+ if (opts.target !== void 0) put("target_url", opts.target || null);
21595
+ if (opts.devCommand !== void 0)
21596
+ put("dev_command", opts.devCommand || null);
21597
+ if (opts.authStrategy !== void 0)
21598
+ put("auth_strategy", opts.authStrategy || null);
21599
+ if (opts.access !== void 0) put("access", opts.access || null);
21600
+ if (opts.harnesses !== void 0) {
21601
+ put("harnesses", opts.harnesses ? splitList(opts.harnesses) : null);
21602
+ }
21603
+ if (opts.untestableBoundaries !== void 0) {
21604
+ put(
21605
+ "untestable_boundaries",
21606
+ opts.untestableBoundaries ? splitList(opts.untestableBoundaries) : null
21607
+ );
21608
+ }
21609
+ } else if (type2 === "context") {
21610
+ if (opts.domain !== void 0) put("domain", opts.domain || null);
21611
+ if (opts.industry !== void 0) put("industry", opts.industry || null);
21612
+ if (opts.jurisdiction !== void 0)
21613
+ put("jurisdiction", opts.jurisdiction || null);
21614
+ if (opts.notes !== void 0) put("notes", opts.notes || null);
21507
21615
  }
21508
21616
  return changed;
21509
21617
  }
@@ -21519,6 +21627,15 @@ async function setEntity(type2, slug, opts) {
21519
21627
  );
21520
21628
  return;
21521
21629
  }
21630
+ if (type2 !== "testability" && type2 !== "context" && !slug) {
21631
+ commandError(`A slug is required when setting a ${type2}.`, {
21632
+ ...opts,
21633
+ code: "missing_slug",
21634
+ fix: `iamu set ${type2} <slug>`
21635
+ });
21636
+ return;
21637
+ }
21638
+ const effectiveSlug = slug || type2;
21522
21639
  const foreign = Object.keys(FLAG_NAMES).filter(
21523
21640
  (k) => !allowed.includes(k) && given(opts, k)
21524
21641
  );
@@ -21528,7 +21645,7 @@ async function setEntity(type2, slug, opts) {
21528
21645
  {
21529
21646
  ...opts,
21530
21647
  code: "invalid_field_combination",
21531
- fix: `iamu set ${type2} ${slug} ${FLAG_NAMES[allowed[0]]} ...`
21648
+ fix: `iamu set ${type2} ${effectiveSlug} ${FLAG_NAMES[allowed[0]]} ...`
21532
21649
  }
21533
21650
  );
21534
21651
  return;
@@ -21569,7 +21686,38 @@ async function setEntity(type2, slug, opts) {
21569
21686
  const allowedPatchKeys = {
21570
21687
  scenario: ["entry", "entry_path", "target", "target_url", "state"],
21571
21688
  persona: ["access"],
21572
- capability: ["paths", "addPath", "removePath", "area"]
21689
+ capability: ["paths", "addPath", "removePath", "area"],
21690
+ outline: [
21691
+ "capability",
21692
+ "primary_capability",
21693
+ "additional_capabilities",
21694
+ "additionalCapabilities"
21695
+ ],
21696
+ testability: [
21697
+ "target",
21698
+ "target_url",
21699
+ "devCommand",
21700
+ "dev_command",
21701
+ "authStrategy",
21702
+ "auth_strategy",
21703
+ "access",
21704
+ "harnesses",
21705
+ "untestableBoundaries",
21706
+ "untestable_boundaries",
21707
+ "body"
21708
+ ],
21709
+ context: [
21710
+ "domain",
21711
+ "industry",
21712
+ "jurisdiction",
21713
+ "notes",
21714
+ "description",
21715
+ "name",
21716
+ "governed_by",
21717
+ "actors",
21718
+ "terminology",
21719
+ "body"
21720
+ ]
21573
21721
  };
21574
21722
  const foreignPatchKeys = Object.keys(parsed).filter(
21575
21723
  (k) => !allowedPatchKeys[type2]?.includes(k)
@@ -21628,6 +21776,65 @@ async function setEntity(type2, slug, opts) {
21628
21776
  if (effectiveOpts.area === void 0 && parsed.area !== void 0) {
21629
21777
  effectiveOpts.area = parsed.area === null || parsed.area === false ? false : String(parsed.area);
21630
21778
  }
21779
+ } else if (type2 === "outline") {
21780
+ if (effectiveOpts.capability === void 0 && (parsed.capability !== void 0 || parsed.primary_capability !== void 0)) {
21781
+ const v = parsed.capability ?? parsed.primary_capability;
21782
+ effectiveOpts.capability = v === null || v === false ? false : String(v);
21783
+ }
21784
+ if (effectiveOpts.additionalCapabilities === void 0 && (parsed.additionalCapabilities !== void 0 || parsed.additional_capabilities !== void 0)) {
21785
+ const v = parsed.additionalCapabilities ?? parsed.additional_capabilities;
21786
+ effectiveOpts.additionalCapabilities = v === null || v === false ? false : Array.isArray(v) ? v.join(",") : String(v);
21787
+ }
21788
+ } else if (type2 === "testability") {
21789
+ if (effectiveOpts.target === void 0 && (parsed.target !== void 0 || parsed.target_url !== void 0)) {
21790
+ const v = parsed.target ?? parsed.target_url;
21791
+ effectiveOpts.target = v === null || v === false ? false : String(v);
21792
+ }
21793
+ if (effectiveOpts.devCommand === void 0 && (parsed.devCommand !== void 0 || parsed.dev_command !== void 0)) {
21794
+ const v = parsed.devCommand ?? parsed.dev_command;
21795
+ effectiveOpts.devCommand = v === null || v === false ? false : String(v);
21796
+ }
21797
+ if (effectiveOpts.authStrategy === void 0 && (parsed.authStrategy !== void 0 || parsed.auth_strategy !== void 0)) {
21798
+ const v = parsed.authStrategy ?? parsed.auth_strategy;
21799
+ effectiveOpts.authStrategy = v === null || v === false ? false : String(v);
21800
+ }
21801
+ if (effectiveOpts.access === void 0 && parsed.access !== void 0) {
21802
+ effectiveOpts.access = parsed.access === null || parsed.access === false ? false : String(parsed.access);
21803
+ }
21804
+ if (effectiveOpts.harnesses === void 0 && parsed.harnesses !== void 0) {
21805
+ effectiveOpts.harnesses = parsed.harnesses === null || parsed.harnesses === false ? false : Array.isArray(parsed.harnesses) ? parsed.harnesses.join(",") : String(parsed.harnesses);
21806
+ }
21807
+ if (effectiveOpts.untestableBoundaries === void 0 && (parsed.untestableBoundaries !== void 0 || parsed.untestable_boundaries !== void 0)) {
21808
+ const b = parsed.untestableBoundaries ?? parsed.untestable_boundaries;
21809
+ effectiveOpts.untestableBoundaries = b === null || b === false ? false : Array.isArray(b) ? b.join(",") : String(b);
21810
+ }
21811
+ if (effectiveOpts.body === void 0 && parsed.body !== void 0) {
21812
+ effectiveOpts.body = String(parsed.body);
21813
+ }
21814
+ } else if (type2 === "context") {
21815
+ if (effectiveOpts.domain === void 0 && parsed.domain !== void 0) {
21816
+ effectiveOpts.domain = parsed.domain === null || parsed.domain === false ? false : String(parsed.domain);
21817
+ }
21818
+ if (effectiveOpts.industry === void 0 && parsed.industry !== void 0) {
21819
+ effectiveOpts.industry = parsed.industry === null || parsed.industry === false ? false : String(parsed.industry);
21820
+ }
21821
+ if (effectiveOpts.jurisdiction === void 0 && parsed.jurisdiction !== void 0) {
21822
+ effectiveOpts.jurisdiction = parsed.jurisdiction === null || parsed.jurisdiction === false ? false : String(parsed.jurisdiction);
21823
+ }
21824
+ if (effectiveOpts.notes === void 0 && parsed.notes !== void 0) {
21825
+ effectiveOpts.notes = parsed.notes === null || parsed.notes === false ? false : String(parsed.notes);
21826
+ }
21827
+ if (effectiveOpts.description === void 0 && parsed.description !== void 0) {
21828
+ effectiveOpts.description = parsed.description === null || parsed.description === false ? false : String(parsed.description);
21829
+ }
21830
+ if (effectiveOpts.name === void 0 && parsed.name !== void 0) {
21831
+ effectiveOpts.name = parsed.name === null || parsed.name === false ? false : String(parsed.name);
21832
+ }
21833
+ if (effectiveOpts.body === void 0 && parsed.body !== void 0) {
21834
+ effectiveOpts.body = String(parsed.body);
21835
+ } else if (effectiveOpts.body === void 0 && effectiveOpts.description !== void 0) {
21836
+ effectiveOpts.body = effectiveOpts.description ? String(effectiveOpts.description) : "";
21837
+ }
21631
21838
  }
21632
21839
  }
21633
21840
  if (!allowed.some((k) => given(effectiveOpts, k))) {
@@ -21636,11 +21843,112 @@ async function setEntity(type2, slug, opts) {
21636
21843
  {
21637
21844
  ...opts,
21638
21845
  code: "nothing_to_set",
21639
- fix: `iamu set ${type2} ${slug} ${FLAG_NAMES[allowed[0]]} ...`
21846
+ fix: `iamu set ${type2}${type2 === "testability" || type2 === "context" ? "" : ` ${effectiveSlug}`} ${FLAG_NAMES[allowed[0]]} ...`
21640
21847
  }
21641
21848
  );
21642
21849
  return;
21643
21850
  }
21851
+ if (type2 === "testability") {
21852
+ const doc = await readTestability(projectRoot) ?? {
21853
+ frontmatter: {
21854
+ schema_version: 1,
21855
+ status: "draft",
21856
+ target_url: "http://localhost:3000"
21857
+ },
21858
+ body: ""
21859
+ };
21860
+ const fm2 = { ...doc.frontmatter };
21861
+ const changed2 = applyChanges(type2, fm2, effectiveOpts);
21862
+ let body = doc.body;
21863
+ if (effectiveOpts.body !== void 0) {
21864
+ body = effectiveOpts.body;
21865
+ changed2.push("body");
21866
+ }
21867
+ const writtenPath2 = testabilityPath(projectRoot);
21868
+ try {
21869
+ await writeTestability(
21870
+ projectRoot,
21871
+ fm2,
21872
+ body
21873
+ );
21874
+ } catch (err) {
21875
+ const message = err instanceof ZodError2 ? err.issues.map((i) => `${i.path.join(".") || "root"}: ${i.message}`).join("; ") : err instanceof Error ? err.message : String(err);
21876
+ commandError(`Could not set testability: ${message}`, {
21877
+ ...opts,
21878
+ code: "testability_write_error",
21879
+ fix: "iamu set testability --target <url>"
21880
+ });
21881
+ return;
21882
+ }
21883
+ if (opts.json) {
21884
+ console.log(
21885
+ JSON.stringify({
21886
+ slug: "testability",
21887
+ type: "testability",
21888
+ path: writtenPath2,
21889
+ changed: changed2
21890
+ })
21891
+ );
21892
+ return;
21893
+ }
21894
+ console.log();
21895
+ console.log(
21896
+ ` ${symbols.success} Updated testability: ${changed2.join(", ")}`
21897
+ );
21898
+ console.log(` ${chalk28.dim("path:")} ${writtenPath2}`);
21899
+ console.log();
21900
+ return;
21901
+ }
21902
+ if (type2 === "context") {
21903
+ const doc = await readContext(projectRoot) ?? {
21904
+ frontmatter: {
21905
+ schema_version: SCHEMA_VERSION,
21906
+ status: "draft"
21907
+ },
21908
+ notes_body: ""
21909
+ };
21910
+ const fm2 = { ...doc.frontmatter };
21911
+ const changed2 = applyChanges(type2, fm2, effectiveOpts);
21912
+ let body = doc.notes_body;
21913
+ if (effectiveOpts.body !== void 0) {
21914
+ body = effectiveOpts.body;
21915
+ changed2.push("body");
21916
+ } else if (effectiveOpts.description !== void 0) {
21917
+ body = effectiveOpts.description ? String(effectiveOpts.description) : "";
21918
+ changed2.push("body");
21919
+ }
21920
+ const writtenPath2 = contextPath(projectRoot);
21921
+ try {
21922
+ await writeContext(projectRoot, {
21923
+ frontmatter: fm2,
21924
+ notes_body: body
21925
+ });
21926
+ } catch (err) {
21927
+ const message = err instanceof ZodError2 ? err.issues.map((i) => `${i.path.join(".") || "root"}: ${i.message}`).join("; ") : err instanceof Error ? err.message : String(err);
21928
+ commandError(`Could not set context: ${message}`, {
21929
+ ...opts,
21930
+ code: "context_write_error",
21931
+ fix: "iamu set context --domain <domain>"
21932
+ });
21933
+ return;
21934
+ }
21935
+ if (opts.json) {
21936
+ console.log(
21937
+ JSON.stringify({
21938
+ slug: "context",
21939
+ type: "context",
21940
+ path: writtenPath2,
21941
+ changed: changed2
21942
+ })
21943
+ );
21944
+ return;
21945
+ }
21946
+ console.log();
21947
+ console.log(` ${symbols.success} Updated context: ${changed2.join(", ")}`);
21948
+ console.log(` ${chalk28.dim("path:")} ${writtenPath2}`);
21949
+ console.log();
21950
+ return;
21951
+ }
21644
21952
  if (type2 === "capability") {
21645
21953
  const candidatePaths = [
21646
21954
  ...typeof effectiveOpts.paths === "string" ? splitList(effectiveOpts.paths) : [],
@@ -21653,7 +21961,7 @@ async function setEntity(type2, slug, opts) {
21653
21961
  {
21654
21962
  ...opts,
21655
21963
  code: "invalid_route_prefix",
21656
- fix: `iamu set ${type2} ${slug} --paths /${invalid[0]?.replace(/^\/+/, "") || ""}`
21964
+ fix: `iamu set ${type2} ${effectiveSlug} --paths /${invalid[0]?.replace(/^\/+/, "") || ""}`
21657
21965
  }
21658
21966
  );
21659
21967
  return;
@@ -21726,7 +22034,25 @@ var init_set_entity = __esm({
21726
22034
  "state"
21727
22035
  ],
21728
22036
  persona: ["access"],
21729
- capability: ["paths", "addPath", "removePath", "area"]
22037
+ capability: ["paths", "addPath", "removePath", "area"],
22038
+ outline: ["capability", "additionalCapabilities"],
22039
+ testability: [
22040
+ "target",
22041
+ "devCommand",
22042
+ "authStrategy",
22043
+ "access",
22044
+ "harnesses",
22045
+ "untestableBoundaries",
22046
+ "body"
22047
+ ],
22048
+ context: [
22049
+ "domain",
22050
+ "industry",
22051
+ "jurisdiction",
22052
+ "notes",
22053
+ "description",
22054
+ "body"
22055
+ ]
21730
22056
  };
21731
22057
  FLAG_NAMES = {
21732
22058
  entry: "--entry",
@@ -21738,7 +22064,20 @@ var init_set_entity = __esm({
21738
22064
  paths: "--paths",
21739
22065
  addPath: "--add-path",
21740
22066
  removePath: "--remove-path",
21741
- area: "--area"
22067
+ area: "--area",
22068
+ capability: "--capability",
22069
+ additionalCapabilities: "--additional-capabilities",
22070
+ devCommand: "--dev-command",
22071
+ authStrategy: "--auth-strategy",
22072
+ harnesses: "--harnesses",
22073
+ untestableBoundaries: "--untestable-boundaries",
22074
+ domain: "--domain",
22075
+ industry: "--industry",
22076
+ jurisdiction: "--jurisdiction",
22077
+ notes: "--notes",
22078
+ description: "--description",
22079
+ name: "--name",
22080
+ body: "--body"
21742
22081
  };
21743
22082
  }
21744
22083
  });
@@ -22999,7 +23338,7 @@ __export(instructions_walk_exports, {
22999
23338
  revisitSection: () => revisitSection,
23000
23339
  viewportLine: () => viewportLine
23001
23340
  });
23002
- import { existsSync as existsSync16, readFileSync as readFileSync9 } from "fs";
23341
+ import { existsSync as existsSync16, readFileSync as readFileSync9, writeFileSync as writeFileSync4 } from "fs";
23003
23342
  import { join as join9 } from "path";
23004
23343
  import chalk31 from "chalk";
23005
23344
  function selfCommand() {
@@ -23386,8 +23725,17 @@ async function instructionsWalk(opts) {
23386
23725
  state: sfm.state ?? null,
23387
23726
  context: contextYml,
23388
23727
  untestableBoundaries
23389
- }) : null
23728
+ }) : null,
23729
+ walker_prompt_path: null
23390
23730
  };
23731
+ if (runDirPath && result.walker_prompt) {
23732
+ try {
23733
+ const promptPath = join9(runDirPath, "walker_prompt.txt");
23734
+ writeFileSync4(promptPath, result.walker_prompt, "utf-8");
23735
+ result.walker_prompt_path = promptPath;
23736
+ } catch {
23737
+ }
23738
+ }
23391
23739
  if (opts.json) {
23392
23740
  console.log(JSON.stringify(result, null, 2));
23393
23741
  return;
@@ -24408,13 +24756,25 @@ async function evaluateRecord(runId, opts) {
24408
24756
  });
24409
24757
  return;
24410
24758
  }
24759
+ const defaultVerdictsPath = path34.join(runDirPath, "verdicts.json");
24411
24760
  if (!opts.file && !opts.data) {
24412
- commandError("Either --file <path> or --data <json> is required", {
24413
- ...opts,
24414
- code: "missing_verdicts_input",
24415
- fix: "iamu evaluate-record --file verdicts.json"
24416
- });
24417
- return;
24761
+ let hasExisting = false;
24762
+ try {
24763
+ await fs32.access(defaultVerdictsPath);
24764
+ hasExisting = true;
24765
+ } catch {
24766
+ hasExisting = false;
24767
+ }
24768
+ if (hasExisting) {
24769
+ opts.file = defaultVerdictsPath;
24770
+ } else {
24771
+ commandError("Either --file <path> or --data <json> is required", {
24772
+ ...opts,
24773
+ code: "missing_verdicts_input",
24774
+ fix: `iamu evaluate-record ${runId} --data '<json>'`
24775
+ });
24776
+ return;
24777
+ }
24418
24778
  }
24419
24779
  let parsedFile;
24420
24780
  try {
@@ -25371,13 +25731,25 @@ async function matchRecord(runId, opts) {
25371
25731
  });
25372
25732
  const manifest = await completedManifest2(runDirPath, runId, opts);
25373
25733
  if (!manifest) return;
25734
+ const defaultMatchesPath = path36.join(runDirPath, "matches.json");
25374
25735
  if (!opts.file && !opts.data) {
25375
- commandError("Either --file <path> or --data <json> is required", {
25376
- ...opts,
25377
- code: "missing_matches_input",
25378
- fix: "iamu match-record --file matches.json"
25379
- });
25380
- return;
25736
+ let hasExisting = false;
25737
+ try {
25738
+ await fs34.access(defaultMatchesPath);
25739
+ hasExisting = true;
25740
+ } catch {
25741
+ hasExisting = false;
25742
+ }
25743
+ if (hasExisting) {
25744
+ opts.file = defaultMatchesPath;
25745
+ } else {
25746
+ commandError("Either --file <path> or --data <json> is required", {
25747
+ ...opts,
25748
+ code: "missing_matches_input",
25749
+ fix: `iamu match-record ${runId} --data '<json>'`
25750
+ });
25751
+ return;
25752
+ }
25381
25753
  }
25382
25754
  let answer;
25383
25755
  try {
@@ -25811,12 +26183,12 @@ function registerProjectCommands(program) {
25811
26183
  await newEntity2(type2, opts);
25812
26184
  }
25813
26185
  );
25814
- program.command("set <type> <slug>").helpGroup("Project:").description(
25815
- "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)"
26186
+ program.command("set <type> [slug]").helpGroup("Project:").description(
26187
+ "Change how an entity is staged: a scenario's start path, target and starting state, a persona's access key, a capability's routes, or project testability / context (scenario, persona, capability, testability, context)"
25816
26188
  ).option(
25817
26189
  "--entry <path>",
25818
26190
  "Where the walk starts, as a path on the product (scenario)"
25819
- ).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(
26191
+ ).option("--no-entry", "Clear the start path (scenario)").option("--target <url>", "Default target URL (scenario, testability)").option("--no-target", "Clear the default target URL (scenario)").option(
25820
26192
  "--state-description <text>",
25821
26193
  "What the world must contain, in domain words (scenario)"
25822
26194
  ).option(
@@ -25824,8 +26196,8 @@ function registerProjectCommands(program) {
25824
26196
  "How the walker picks the closest record when the exact one is missing (scenario)"
25825
26197
  ).option("--no-state", "Clear the starting state (scenario)").option(
25826
26198
  "--access <key>",
25827
- "Account, role or switcher key this persona uses (persona)"
25828
- ).option("--no-access", "Clear the access key (persona)").option(
26199
+ "Account, role or switcher key this persona uses (persona, testability)"
26200
+ ).option("--no-access", "Clear the access key (persona, testability)").option(
25829
26201
  "--paths <paths>",
25830
26202
  "Route prefixes, comma-separated; replaces the list (capability)"
25831
26203
  ).option("--no-paths", "Clear route prefixes (capability)").option(
@@ -25836,12 +26208,27 @@ function registerProjectCommands(program) {
25836
26208
  "--remove-path <path>",
25837
26209
  "Remove a route prefix (capability, repeatable)",
25838
26210
  (val, prev = []) => [...prev, val]
25839
- ).option("--area <name>", "Visual grouping category (capability)").option("--no-area", "Clear the area (capability)").option(
26211
+ ).option("--area <name>", "Visual grouping category (capability)").option("--no-area", "Clear the area (capability)").option("--capability <slug>", "Primary capability slug (outline)").option("--no-capability", "Clear primary capability (outline)").option(
26212
+ "--additional-capabilities <slugs>",
26213
+ "Secondary capability slugs, comma-separated (outline)"
26214
+ ).option(
26215
+ "--no-additional-capabilities",
26216
+ "Clear secondary capabilities (outline)"
26217
+ ).option("--dev-command <cmd>", "Dev startup command (testability)").option("--no-dev-command", "Clear dev startup command (testability)").option("--auth-strategy <strategy>", "Auth strategy (testability)").option("--no-auth-strategy", "Clear auth strategy (testability)").option(
26218
+ "--harnesses <list>",
26219
+ "Mock frameworks or seed scripts, comma-separated (testability)"
26220
+ ).option("--no-harnesses", "Clear harnesses (testability)").option(
26221
+ "--untestable-boundaries <list>",
26222
+ "Forbidden external endpoints, comma-separated (testability)"
26223
+ ).option(
26224
+ "--no-untestable-boundaries",
26225
+ "Clear untestable boundaries (testability)"
26226
+ ).option("--domain <text>", "Domain name (context)").option("--industry <text>", "Industry category (context)").option("--jurisdiction <text>", "Jurisdiction or region (context)").option("--notes <text>", "Orientation notes (context)").option("--description <text>", "Project description (context)").option("--name <text>", "Project name (context)").option("--body <text>", "Markdown narrative body (testability, context)").option(
25840
26227
  "--file <path>",
25841
26228
  "JSON patch file to apply staging fields (or - for stdin)"
25842
26229
  ).option("--json", "Output as JSON").addHelpText(
25843
26230
  "after",
25844
- '\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'
26231
+ '\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 outline shift-handoff --capability incident-reporting\n $ iamu set capability team-access --add-path /team\n $ iamu set testability --target http://localhost:5173 --dev-command "./dev" --auth-strategy dev_switcher\n $ iamu set context --domain "Workplace safety" --industry "Manufacturing" --description "..."\n $ iamu set scenario maria--handoff --file patch.json'
25845
26232
  ).action(
25846
26233
  async (type2, slug, opts) => {
25847
26234
  const { setEntity: setEntity2 } = await Promise.resolve().then(() => (init_set_entity(), set_entity_exports));
@@ -25960,41 +26347,65 @@ function registerWalkthroughCommands(program) {
25960
26347
  "--surface <text>",
25961
26348
  `"browse" (default) for iamu browse, or a description of your own browser tool \u2014 becomes the walker prompt's Driving surface section`
25962
26349
  ).option("--json", "Output as JSON").action(
25963
- async (phase, slug, opts) => {
26350
+ async (phase, slug, opts, cmd) => {
26351
+ const effectiveOpts = {
26352
+ ...cmd.optsWithGlobals(),
26353
+ ...opts
26354
+ };
25964
26355
  const { instructions: instructions2 } = await Promise.resolve().then(() => (init_instructions(), instructions_exports));
25965
- await instructions2(phase, slug, opts);
26356
+ await instructions2(phase, slug, effectiveOpts);
25966
26357
  }
25967
26358
  );
25968
26359
  instrCmd.command("walk [scenario-slug]").description("Compose walking prompt from scenario + persona + outline").option("--scenario <slug>", "Scenario slug (default: active scenario)").option("--run <run-id>", "Run ID (resolves target URL from manifest)").option(
25969
26360
  "--surface <text>",
25970
26361
  `"browse" (default) for iamu browse, or a description of your own browser tool \u2014 becomes the walker prompt's Driving surface section`
25971
26362
  ).option("--json", "Output as JSON").action(
25972
- async (scenarioSlug, opts) => {
26363
+ async (scenarioSlug, opts, cmd) => {
26364
+ const effectiveOpts = {
26365
+ ...cmd.parent?.opts(),
26366
+ ...cmd.optsWithGlobals(),
26367
+ ...opts
26368
+ };
25973
26369
  const { instructionsWalk: instructionsWalk2 } = await Promise.resolve().then(() => (init_instructions_walk(), instructions_walk_exports));
25974
26370
  await instructionsWalk2({
25975
- ...opts,
25976
- scenario: scenarioSlug ?? opts.scenario
26371
+ ...effectiveOpts,
26372
+ scenario: scenarioSlug ?? effectiveOpts.scenario
25977
26373
  });
25978
26374
  }
25979
26375
  );
25980
26376
  instrCmd.command("stage [scenario-slug]").description(
25981
26377
  "Dynamic staging instructions: persona, outline, tension, boundaries"
25982
26378
  ).option("--json", "Output as JSON").action(
25983
- async (scenarioSlug, opts) => {
26379
+ async (scenarioSlug, opts, cmd) => {
26380
+ const effectiveOpts = {
26381
+ ...cmd.parent?.opts(),
26382
+ ...cmd.optsWithGlobals(),
26383
+ ...opts
26384
+ };
25984
26385
  const { instructionsStage: instructionsStage2 } = await Promise.resolve().then(() => (init_instructions(), instructions_exports));
25985
- await instructionsStage2(scenarioSlug, opts);
26386
+ await instructionsStage2(scenarioSlug, effectiveOpts);
25986
26387
  }
25987
26388
  );
25988
- instrCmd.command("setup").description("Dynamic project setup instructions & prerequisite checks").option("--json", "Output as JSON").action(async (opts) => {
26389
+ instrCmd.command("setup").description("Dynamic project setup instructions & prerequisite checks").option("--json", "Output as JSON").action(async (opts, cmd) => {
26390
+ const effectiveOpts = {
26391
+ ...cmd.parent?.opts(),
26392
+ ...cmd.optsWithGlobals(),
26393
+ ...opts
26394
+ };
25989
26395
  const { instructionsSetup: instructionsSetup2 } = await Promise.resolve().then(() => (init_instructions(), instructions_exports));
25990
- await instructionsSetup2(opts);
26396
+ await instructionsSetup2(effectiveOpts);
25991
26397
  });
25992
26398
  instrCmd.command("triage [run-or-scenario-slug]").description(
25993
26399
  "Dynamic triage instructions: finding triage, confirmation, dismissal guidance"
25994
26400
  ).option("--run <run-id>", "Run ID to triage findings for").option("--json", "Output as JSON").action(
25995
- async (slug, opts) => {
26401
+ async (slug, opts, cmd) => {
26402
+ const effectiveOpts = {
26403
+ ...cmd.parent?.opts(),
26404
+ ...cmd.optsWithGlobals(),
26405
+ ...opts
26406
+ };
25996
26407
  const { instructionsTriage: instructionsTriage2 } = await Promise.resolve().then(() => (init_instructions(), instructions_exports));
25997
- await instructionsTriage2(slug, opts);
26408
+ await instructionsTriage2(slug, effectiveOpts);
25998
26409
  }
25999
26410
  );
26000
26411
  program.command("browse <command> [args...]").helpGroup("Walkthrough:").description(
@@ -26132,7 +26543,14 @@ function registerWalkthroughCommands(program) {
26132
26543
  );
26133
26544
  program.command("match-record <run-id>").helpGroup("Walkthrough:").description(
26134
26545
  "Record finding matches and re-derive the run's since-last-run delta"
26135
- ).option("--file <path>", "Path to matches JSON file (or - for stdin)").option("--data <json>", "Inline matches JSON string").option("--json", "Output match results as JSON").action(
26546
+ ).option("--file <path>", "Path to matches JSON file (or - for stdin)").option("--data <json>", "Inline matches JSON string").option("--json", "Output match results as JSON").addHelpText(
26547
+ "after",
26548
+ `
26549
+ Examples:
26550
+ $ iamu match-record <run-id> --data '{"matches":[]}'
26551
+ $ iamu match-record <run-id> --file matches.json
26552
+ $ iamu match-record <run-id> --json`
26553
+ ).action(
26136
26554
  async (runId, opts) => {
26137
26555
  const { matchRecord: matchRecord2 } = await Promise.resolve().then(() => (init_match(), match_exports));
26138
26556
  await matchRecord2(runId, opts);
@@ -26146,7 +26564,13 @@ function registerWalkthroughCommands(program) {
26146
26564
  );
26147
26565
  program.command("evaluate-record <run-id>").helpGroup("Walkthrough:").description("Record evaluation verdicts into the run's verdicts.json").option("--file <path>", "Path to verdicts JSON file (or - for stdin)").option("--data <json>", "Inline verdicts JSON string").option("--json", "Output summary as JSON").option("-y, --yes", "Skip interactive prompts").option("--no-prompt", "Skip interactive prompt to view results").option("--no-push", "Skip pushing the updated run to Iamu Cloud").option("--view", "Open results in browser immediately without prompt").addHelpText(
26148
26566
  "after",
26149
- "\nExamples:\n $ iamu evaluate-record <run-id> --file verdicts.json\n $ iamu evaluate-record <run-id> --file verdicts.json --no-prompt\n $ iamu evaluate-record <run-id> --file verdicts.json --no-push\n $ iamu evaluate-record <run-id> --file verdicts.json --json"
26567
+ `
26568
+ Examples:
26569
+ $ iamu evaluate-record <run-id> --data '{"verdicts":[]}' --no-prompt --no-push
26570
+ $ iamu evaluate-record <run-id> --file verdicts.json
26571
+ $ iamu evaluate-record <run-id> --file verdicts.json --no-prompt
26572
+ $ iamu evaluate-record <run-id> --file verdicts.json --no-push
26573
+ $ iamu evaluate-record <run-id> --file verdicts.json --json`
26150
26574
  ).action(
26151
26575
  async (runId, opts) => {
26152
26576
  const { evaluateRecord: evaluateRecord2 } = await Promise.resolve().then(() => (init_evaluate(), evaluate_exports));