@rpamis/comet 0.4.0-beta.8 → 0.4.0-beta.9

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.
Files changed (128) hide show
  1. package/README.md +1 -1
  2. package/assets/manifest.json +1 -1
  3. package/assets/skills/comet/reference/comet-yaml-fields.md +2 -2
  4. package/assets/skills/comet/scripts/comet-entry-runtime.mjs +95 -4
  5. package/assets/skills/comet/scripts/comet-hook-router.mjs +224 -35
  6. package/assets/skills/comet/scripts/comet-runtime.mjs +218 -102
  7. package/assets/skills/comet-archive/SKILL.md +29 -20
  8. package/assets/skills/comet-native/SKILL.md +23 -15
  9. package/assets/skills/comet-native/reference/artifacts.md +19 -4
  10. package/assets/skills/comet-native/reference/commands.md +8 -2
  11. package/assets/skills/comet-native/reference/recovery.md +1 -1
  12. package/assets/skills/comet-native/scripts/comet-native-runtime.mjs +1052 -458
  13. package/assets/skills-zh/comet/reference/comet-yaml-fields.md +2 -2
  14. package/assets/skills-zh/comet-archive/SKILL.md +29 -20
  15. package/assets/skills-zh/comet-native/SKILL.md +23 -15
  16. package/assets/skills-zh/comet-native/reference/artifacts.md +20 -5
  17. package/assets/skills-zh/comet-native/reference/commands.md +8 -2
  18. package/assets/skills-zh/comet-native/reference/recovery.md +1 -1
  19. package/dist/app/commands/init.d.ts.map +1 -1
  20. package/dist/app/commands/init.js +3 -6
  21. package/dist/app/commands/init.js.map +1 -1
  22. package/dist/domains/comet-entry/current-selection.d.ts.map +1 -1
  23. package/dist/domains/comet-entry/current-selection.js +6 -8
  24. package/dist/domains/comet-entry/current-selection.js.map +1 -1
  25. package/dist/domains/comet-entry/project-status.d.ts.map +1 -1
  26. package/dist/domains/comet-entry/project-status.js +5 -1
  27. package/dist/domains/comet-entry/project-status.js.map +1 -1
  28. package/dist/domains/comet-native/native-build-evidence.js +2 -2
  29. package/dist/domains/comet-native/native-build-evidence.js.map +1 -1
  30. package/dist/domains/comet-native/native-change.d.ts +4 -2
  31. package/dist/domains/comet-native/native-change.d.ts.map +1 -1
  32. package/dist/domains/comet-native/native-change.js +18 -4
  33. package/dist/domains/comet-native/native-change.js.map +1 -1
  34. package/dist/domains/comet-native/native-check-receipt.d.ts.map +1 -1
  35. package/dist/domains/comet-native/native-check-receipt.js +5 -3
  36. package/dist/domains/comet-native/native-check-receipt.js.map +1 -1
  37. package/dist/domains/comet-native/native-cli.d.ts.map +1 -1
  38. package/dist/domains/comet-native/native-cli.js +120 -16
  39. package/dist/domains/comet-native/native-cli.js.map +1 -1
  40. package/dist/domains/comet-native/native-config.d.ts +5 -1
  41. package/dist/domains/comet-native/native-config.d.ts.map +1 -1
  42. package/dist/domains/comet-native/native-config.js +78 -0
  43. package/dist/domains/comet-native/native-config.js.map +1 -1
  44. package/dist/domains/comet-native/native-continuation.d.ts +2 -1
  45. package/dist/domains/comet-native/native-continuation.d.ts.map +1 -1
  46. package/dist/domains/comet-native/native-continuation.js +13 -5
  47. package/dist/domains/comet-native/native-continuation.js.map +1 -1
  48. package/dist/domains/comet-native/native-diagnostics.d.ts +7 -3
  49. package/dist/domains/comet-native/native-diagnostics.d.ts.map +1 -1
  50. package/dist/domains/comet-native/native-diagnostics.js +19 -9
  51. package/dist/domains/comet-native/native-diagnostics.js.map +1 -1
  52. package/dist/domains/comet-native/native-file-identity.d.ts +2 -8
  53. package/dist/domains/comet-native/native-file-identity.d.ts.map +1 -1
  54. package/dist/domains/comet-native/native-file-identity.js +1 -20
  55. package/dist/domains/comet-native/native-file-identity.js.map +1 -1
  56. package/dist/domains/comet-native/native-findings.d.ts.map +1 -1
  57. package/dist/domains/comet-native/native-findings.js +19 -1
  58. package/dist/domains/comet-native/native-findings.js.map +1 -1
  59. package/dist/domains/comet-native/native-guards.d.ts +2 -1
  60. package/dist/domains/comet-native/native-guards.d.ts.map +1 -1
  61. package/dist/domains/comet-native/native-guards.js +14 -0
  62. package/dist/domains/comet-native/native-guards.js.map +1 -1
  63. package/dist/domains/comet-native/native-lock.d.ts.map +1 -1
  64. package/dist/domains/comet-native/native-lock.js +24 -32
  65. package/dist/domains/comet-native/native-lock.js.map +1 -1
  66. package/dist/domains/comet-native/native-root-move.d.ts.map +1 -1
  67. package/dist/domains/comet-native/native-root-move.js +2 -0
  68. package/dist/domains/comet-native/native-root-move.js.map +1 -1
  69. package/dist/domains/comet-native/native-snapshot.d.ts +5 -1
  70. package/dist/domains/comet-native/native-snapshot.d.ts.map +1 -1
  71. package/dist/domains/comet-native/native-snapshot.js +218 -2
  72. package/dist/domains/comet-native/native-snapshot.js.map +1 -1
  73. package/dist/domains/comet-native/native-transitions.d.ts +2 -1
  74. package/dist/domains/comet-native/native-transitions.d.ts.map +1 -1
  75. package/dist/domains/comet-native/native-transitions.js +34 -11
  76. package/dist/domains/comet-native/native-transitions.js.map +1 -1
  77. package/dist/domains/comet-native/native-types.d.ts +16 -0
  78. package/dist/domains/comet-native/native-types.d.ts.map +1 -1
  79. package/dist/domains/comet-native/native-verification-runtime.d.ts.map +1 -1
  80. package/dist/domains/comet-native/native-verification-runtime.js +5 -3
  81. package/dist/domains/comet-native/native-verification-runtime.js.map +1 -1
  82. package/dist/domains/comet-native/native-verification-scope.d.ts +2 -1
  83. package/dist/domains/comet-native/native-verification-scope.d.ts.map +1 -1
  84. package/dist/domains/comet-native/native-verification-scope.js +6 -1
  85. package/dist/domains/comet-native/native-verification-scope.js.map +1 -1
  86. package/dist/domains/dashboard/native-collector.d.ts.map +1 -1
  87. package/dist/domains/dashboard/native-collector.js +4 -1
  88. package/dist/domains/dashboard/native-collector.js.map +1 -1
  89. package/dist/domains/skill/platform-install.d.ts.map +1 -1
  90. package/dist/domains/skill/platform-install.js +9 -0
  91. package/dist/domains/skill/platform-install.js.map +1 -1
  92. package/dist/domains/skill/project-instructions.d.ts.map +1 -1
  93. package/dist/domains/skill/project-instructions.js +4 -2
  94. package/dist/domains/skill/project-instructions.js.map +1 -1
  95. package/dist/domains/workflow-contract/project-config.d.ts +1 -1
  96. package/dist/domains/workflow-contract/project-config.d.ts.map +1 -1
  97. package/dist/domains/workflow-contract/project-config.js +24 -2
  98. package/dist/domains/workflow-contract/project-config.js.map +1 -1
  99. package/dist/platform/fs/file-identity.d.ts +9 -0
  100. package/dist/platform/fs/file-identity.d.ts.map +1 -0
  101. package/dist/platform/fs/file-identity.js +21 -0
  102. package/dist/platform/fs/file-identity.js.map +1 -0
  103. package/dist/platform/fs/file-system.d.ts.map +1 -1
  104. package/dist/platform/fs/file-system.js +13 -9
  105. package/dist/platform/fs/file-system.js.map +1 -1
  106. package/dist/platform/fs/race-safe-read.d.ts +47 -0
  107. package/dist/platform/fs/race-safe-read.d.ts.map +1 -0
  108. package/dist/platform/fs/race-safe-read.js +132 -0
  109. package/dist/platform/fs/race-safe-read.js.map +1 -0
  110. package/eval/README.md +1 -1
  111. package/eval/local/tasks/comet-native-clarification-depth/environment/Dockerfile +27 -0
  112. package/eval/local/tasks/comet-native-clarification-depth/environment/test_wordcount.py +25 -0
  113. package/eval/local/tasks/comet-native-clarification-depth/environment/wordcount.py +36 -0
  114. package/eval/local/tasks/comet-native-clarification-depth/instruction.md +17 -0
  115. package/eval/local/tasks/comet-native-clarification-depth/task.toml +47 -0
  116. package/eval/local/tasks/comet-native-clarification-depth/validation/test_native_clarification_depth.py +618 -0
  117. package/eval/local/tasks/comet-native-clarification-modes/validation/test_native_clarification_modes.py +1 -1
  118. package/eval/local/tasks/index.yaml +6 -0
  119. package/eval/local/tests/conftest.py +146 -2
  120. package/eval/local/tests/scaffold/test_conftest_helpers.py +188 -1
  121. package/eval/local/tests/scaffold/test_tasks.py +440 -8
  122. package/eval/local/tests/scaffold/test_utils.py +114 -1
  123. package/eval/local/tests/tasks/test_tasks.py +2 -0
  124. package/eval/scaffold/python/aligned_comparison.py +1 -0
  125. package/eval/scaffold/python/tasks.py +2 -0
  126. package/eval/scaffold/shell/docker.sh +1 -1
  127. package/eval/scaffold/shell/run-claude-loop.sh +25 -2
  128. package/package.json +1 -1
@@ -6927,14 +6927,14 @@ var require_parser = __commonJS({
6927
6927
  case "scalar":
6928
6928
  case "single-quoted-scalar":
6929
6929
  case "double-quoted-scalar": {
6930
- const fs23 = this.flowScalar(this.type);
6930
+ const fs24 = this.flowScalar(this.type);
6931
6931
  if (atNextItem || it.value) {
6932
- map.items.push({ start, key: fs23, sep: [] });
6932
+ map.items.push({ start, key: fs24, sep: [] });
6933
6933
  this.onKeyLine = true;
6934
6934
  } else if (it.sep) {
6935
- this.stack.push(fs23);
6935
+ this.stack.push(fs24);
6936
6936
  } else {
6937
- Object.assign(it, { key: fs23, sep: [] });
6937
+ Object.assign(it, { key: fs24, sep: [] });
6938
6938
  this.onKeyLine = true;
6939
6939
  }
6940
6940
  return;
@@ -7062,13 +7062,13 @@ var require_parser = __commonJS({
7062
7062
  case "scalar":
7063
7063
  case "single-quoted-scalar":
7064
7064
  case "double-quoted-scalar": {
7065
- const fs23 = this.flowScalar(this.type);
7065
+ const fs24 = this.flowScalar(this.type);
7066
7066
  if (!it || it.value)
7067
- fc.items.push({ start: [], key: fs23, sep: [] });
7067
+ fc.items.push({ start: [], key: fs24, sep: [] });
7068
7068
  else if (it.sep)
7069
- this.stack.push(fs23);
7069
+ this.stack.push(fs24);
7070
7070
  else
7071
- Object.assign(it, { key: fs23, sep: [] });
7071
+ Object.assign(it, { key: fs24, sep: [] });
7072
7072
  return;
7073
7073
  }
7074
7074
  case "flow-map-end":
@@ -7546,7 +7546,7 @@ import { pathToFileURL } from "url";
7546
7546
  // domains/comet-classic/classic-archive.ts
7547
7547
  import { createHash as createHash3 } from "crypto";
7548
7548
  import { spawnSync } from "child_process";
7549
- import { promises as fs14 } from "fs";
7549
+ import { promises as fs15 } from "fs";
7550
7550
  import path15 from "path";
7551
7551
 
7552
7552
  // domains/comet-classic/classic-paths.ts
@@ -9531,13 +9531,133 @@ function applyClassicTransition(current, event, options = {}) {
9531
9531
  }
9532
9532
 
9533
9533
  // domains/comet-classic/classic-current-change.ts
9534
- import { promises as fs13 } from "fs";
9534
+ import { promises as fs14 } from "fs";
9535
9535
  import path14 from "path";
9536
9536
 
9537
9537
  // domains/comet-entry/current-selection.ts
9538
9538
  import { randomUUID as randomUUID6 } from "crypto";
9539
- import { promises as fs11 } from "fs";
9539
+ import { promises as fs12 } from "fs";
9540
9540
  import path12 from "path";
9541
+
9542
+ // platform/fs/race-safe-read.ts
9543
+ import { constants as fsConstants, promises as fs11 } from "fs";
9544
+
9545
+ // platform/fs/file-identity.ts
9546
+ function hasPlatformIdentity(value) {
9547
+ return value !== 0 && value !== 0n && value !== "0";
9548
+ }
9549
+ function hasComparableFileObject(left, right) {
9550
+ return hasPlatformIdentity(left.dev) && hasPlatformIdentity(right.dev) && hasPlatformIdentity(left.ino) && hasPlatformIdentity(right.ino);
9551
+ }
9552
+ function sameFileObject(left, right) {
9553
+ const comparableDevice = hasPlatformIdentity(left.dev) && hasPlatformIdentity(right.dev);
9554
+ if (comparableDevice && left.dev !== right.dev) return false;
9555
+ const comparableInode = hasPlatformIdentity(left.ino) && hasPlatformIdentity(right.ino);
9556
+ if (comparableInode && left.ino !== right.ino) return false;
9557
+ if (comparableDevice && comparableInode) return true;
9558
+ return left.birthtime === right.birthtime;
9559
+ }
9560
+
9561
+ // platform/fs/race-safe-read.ts
9562
+ var RaceSafeReadError = class extends Error {
9563
+ reason;
9564
+ constructor(reason, message, options) {
9565
+ super(message, options);
9566
+ this.name = "RaceSafeReadError";
9567
+ this.reason = reason;
9568
+ }
9569
+ };
9570
+ function birthtimeOf(stat) {
9571
+ return "birthtimeNs" in stat && typeof stat.birthtimeNs === "bigint" ? stat.birthtimeNs : stat.birthtimeMs;
9572
+ }
9573
+ function ctimeOf(stat) {
9574
+ return "ctimeNs" in stat && typeof stat.ctimeNs === "bigint" ? stat.ctimeNs : stat.ctimeMs;
9575
+ }
9576
+ function identityOf(stat) {
9577
+ return { dev: stat.dev, ino: stat.ino, birthtime: birthtimeOf(stat) };
9578
+ }
9579
+ function sameStatIdentity(left, right) {
9580
+ const leftObject = identityOf(left);
9581
+ const rightObject = identityOf(right);
9582
+ if (hasComparableFileObject(leftObject, rightObject)) {
9583
+ return sameFileObject(leftObject, rightObject);
9584
+ }
9585
+ return sameFileObject(leftObject, rightObject) && birthtimeOf(left) === birthtimeOf(right) && ctimeOf(left) === ctimeOf(right) && left.size === right.size;
9586
+ }
9587
+ async function readFileRaceSafe(file, maxBytes, options = {}) {
9588
+ if (!Number.isSafeInteger(maxBytes) || maxBytes < 1) {
9589
+ throw new Error("race-safe read byte limit must be a positive integer");
9590
+ }
9591
+ const label = options.label ?? "file";
9592
+ const bigint = options.bigint === true;
9593
+ const before = await fs11.lstat(file, { bigint });
9594
+ if (!before.isFile() || before.isSymbolicLink()) {
9595
+ throw new RaceSafeReadError("not-regular-file", `${label} must be a regular file`);
9596
+ }
9597
+ if (BigInt(before.size) > BigInt(maxBytes)) {
9598
+ throw new RaceSafeReadError("too-large", `${label} exceeds ${maxBytes} bytes`);
9599
+ }
9600
+ const beforeRealPath = await fs11.realpath(file);
9601
+ await options.verify?.("pre-open", { realPath: beforeRealPath, identity: identityOf(before) });
9602
+ const flags = process.platform === "win32" ? fsConstants.O_RDONLY : fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW | fsConstants.O_NONBLOCK;
9603
+ let handle;
9604
+ try {
9605
+ handle = await fs11.open(file, flags);
9606
+ } catch (error) {
9607
+ if (error.code === "ELOOP") {
9608
+ throw new RaceSafeReadError("not-regular-file", `${label} must be a regular file`, {
9609
+ cause: error
9610
+ });
9611
+ }
9612
+ throw error;
9613
+ }
9614
+ try {
9615
+ const [opened, pathAfterOpen, realPathAfterOpen] = await Promise.all([
9616
+ handle.stat({ bigint }),
9617
+ fs11.lstat(file, { bigint }),
9618
+ fs11.realpath(file)
9619
+ ]);
9620
+ if (!opened.isFile() || !pathAfterOpen.isFile() || pathAfterOpen.isSymbolicLink() || realPathAfterOpen !== beforeRealPath || !sameStatIdentity(before, opened) || !sameStatIdentity(before, pathAfterOpen)) {
9621
+ throw new RaceSafeReadError("changed", `${label} changed while opening`);
9622
+ }
9623
+ await options.verify?.("post-open", {
9624
+ realPath: realPathAfterOpen,
9625
+ identity: identityOf(opened)
9626
+ });
9627
+ await options.hooks?.afterOpen?.();
9628
+ const chunks = [];
9629
+ let total = 0;
9630
+ const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1));
9631
+ for (; ; ) {
9632
+ const remaining = maxBytes + 1 - total;
9633
+ const { bytesRead } = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null);
9634
+ if (bytesRead === 0) break;
9635
+ total += bytesRead;
9636
+ if (total > maxBytes) {
9637
+ throw new RaceSafeReadError("too-large", `${label} exceeds ${maxBytes} bytes`);
9638
+ }
9639
+ chunks.push(Buffer.from(buffer.subarray(0, bytesRead)));
9640
+ }
9641
+ await options.hooks?.beforeFinalCheck?.();
9642
+ const [afterHandle, afterPath, afterRealPath] = await Promise.all([
9643
+ handle.stat({ bigint }),
9644
+ fs11.lstat(file, { bigint }),
9645
+ fs11.realpath(file)
9646
+ ]);
9647
+ if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== beforeRealPath || !sameStatIdentity(before, afterHandle) || !sameStatIdentity(before, afterPath)) {
9648
+ throw new RaceSafeReadError("changed", `${label} changed while reading`);
9649
+ }
9650
+ await options.verify?.("post-read", {
9651
+ realPath: afterRealPath,
9652
+ identity: identityOf(afterHandle)
9653
+ });
9654
+ return { bytes: Buffer.concat(chunks, total), stat: afterHandle, realPath: afterRealPath };
9655
+ } finally {
9656
+ await handle.close();
9657
+ }
9658
+ }
9659
+
9660
+ // domains/comet-entry/current-selection.ts
9541
9661
  var COMET_CURRENT_SELECTION_SCHEMA = "comet.selection.v2";
9542
9662
  var COMET_CURRENT_SELECTION_MAX_BYTES = 16 * 1024;
9543
9663
  function cometCurrentSelectionFile(projectRoot2) {
@@ -9599,16 +9719,11 @@ function parseSelection(source) {
9599
9719
  async function readCometCurrentSelection(projectRoot2) {
9600
9720
  let source;
9601
9721
  try {
9602
- const stat = await fs11.lstat(cometCurrentSelectionFile(projectRoot2));
9603
- if (stat.isSymbolicLink() || !stat.isFile()) {
9604
- throw new Error("current change selection must be a regular file");
9605
- }
9606
- if (stat.size > COMET_CURRENT_SELECTION_MAX_BYTES) {
9607
- throw new Error(
9608
- `current change selection exceeds ${COMET_CURRENT_SELECTION_MAX_BYTES} bytes`
9609
- );
9610
- }
9611
- source = await fs11.readFile(cometCurrentSelectionFile(projectRoot2), "utf8");
9722
+ const file = cometCurrentSelectionFile(projectRoot2);
9723
+ const { bytes } = await readFileRaceSafe(file, COMET_CURRENT_SELECTION_MAX_BYTES, {
9724
+ label: "current change selection"
9725
+ });
9726
+ source = bytes.toString("utf8");
9612
9727
  } catch (error) {
9613
9728
  if (error.code === "ENOENT") return { status: "missing" };
9614
9729
  throw new Error(
@@ -9624,18 +9739,18 @@ async function writeCometCurrentSelection(projectRoot2, selection) {
9624
9739
  if (parsed.legacy) throw new Error("cannot write a legacy current change selection");
9625
9740
  const file = cometCurrentSelectionFile(projectRoot2);
9626
9741
  const temporary = `${file}.${randomUUID6()}.tmp`;
9627
- await fs11.mkdir(path12.dirname(file), { recursive: true });
9742
+ await fs12.mkdir(path12.dirname(file), { recursive: true });
9628
9743
  try {
9629
- await fs11.writeFile(temporary, `${JSON.stringify(parsed.selection, null, 2)}
9744
+ await fs12.writeFile(temporary, `${JSON.stringify(parsed.selection, null, 2)}
9630
9745
  `, "utf8");
9631
- await fs11.rename(temporary, file);
9746
+ await fs12.rename(temporary, file);
9632
9747
  } catch (error) {
9633
- await fs11.rm(temporary, { force: true });
9748
+ await fs12.rm(temporary, { force: true });
9634
9749
  throw error;
9635
9750
  }
9636
9751
  }
9637
9752
  async function clearCometCurrentSelection(projectRoot2) {
9638
- await fs11.rm(cometCurrentSelectionFile(projectRoot2), { force: true });
9753
+ await fs12.rm(cometCurrentSelectionFile(projectRoot2), { force: true });
9639
9754
  }
9640
9755
  async function clearCometCurrentSelectionIf(projectRoot2, workflow, change) {
9641
9756
  const current = await readCometCurrentSelection(projectRoot2);
@@ -9650,7 +9765,7 @@ async function clearCometCurrentSelectionIf(projectRoot2, workflow, change) {
9650
9765
  var import_yaml3 = __toESM(require_dist(), 1);
9651
9766
  import { execFileSync } from "child_process";
9652
9767
  import { randomUUID as randomUUID7 } from "crypto";
9653
- import { promises as fs12 } from "fs";
9768
+ import { promises as fs13 } from "fs";
9654
9769
  import path13 from "path";
9655
9770
  function liveGitBranch(cwd) {
9656
9771
  try {
@@ -9692,7 +9807,7 @@ function evaluateBranchBinding(input) {
9692
9807
  }
9693
9808
  async function resolveBranchBinding(changeDir, options) {
9694
9809
  const file = path13.join(changeDir, ".comet.yaml");
9695
- const document = (0, import_yaml3.parseDocument)(await fs12.readFile(file, "utf8"), { uniqueKeys: false });
9810
+ const document = (0, import_yaml3.parseDocument)(await fs13.readFile(file, "utf8"), { uniqueKeys: false });
9696
9811
  if (document.errors.length > 0) {
9697
9812
  throw new Error(`Invalid .comet.yaml: ${document.errors[0].message}`);
9698
9813
  }
@@ -9711,14 +9826,14 @@ async function resolveBranchBinding(changeDir, options) {
9711
9826
  }
9712
9827
  async function healBoundBranch(changeDir, branch) {
9713
9828
  const file = path13.join(changeDir, ".comet.yaml");
9714
- const document = (0, import_yaml3.parseDocument)(await fs12.readFile(file, "utf8"), { uniqueKeys: false });
9829
+ const document = (0, import_yaml3.parseDocument)(await fs13.readFile(file, "utf8"), { uniqueKeys: false });
9715
9830
  document.set("bound_branch", branch);
9716
9831
  const temporary = `${file}.${randomUUID7()}.tmp`;
9717
9832
  try {
9718
- await fs12.writeFile(temporary, document.toString(), "utf8");
9719
- await fs12.rename(temporary, file);
9833
+ await fs13.writeFile(temporary, document.toString(), "utf8");
9834
+ await fs13.rename(temporary, file);
9720
9835
  } catch (error) {
9721
- await fs12.rm(temporary, { force: true });
9836
+ await fs13.rm(temporary, { force: true });
9722
9837
  throw error;
9723
9838
  }
9724
9839
  }
@@ -9744,7 +9859,7 @@ async function validateActiveChange(projectRoot2, changeName) {
9744
9859
  assertOpenSpecChangeName(changeName);
9745
9860
  const changeDir = changeDirectory(projectRoot2, changeName);
9746
9861
  try {
9747
- await fs13.access(path14.join(changeDir, ".comet.yaml"));
9862
+ await fs14.access(path14.join(changeDir, ".comet.yaml"));
9748
9863
  } catch (error) {
9749
9864
  if (error.code === "ENOENT") {
9750
9865
  throw new Error(
@@ -9882,7 +9997,7 @@ var ArchiveOutput = class {
9882
9997
  };
9883
9998
  async function exists2(file) {
9884
9999
  try {
9885
- await fs14.access(file);
10000
+ await fs15.access(file);
9886
10001
  return true;
9887
10002
  } catch (error) {
9888
10003
  if (error.code === "ENOENT") return false;
@@ -9936,10 +10051,10 @@ async function findArchiveDir(change, preferred) {
9936
10051
  if (await exists2(preferred)) return preferred;
9937
10052
  const archiveRoot = "openspec/changes/archive";
9938
10053
  if (!await exists2(archiveRoot)) return null;
9939
- for (const entry2 of (await fs14.readdir(archiveRoot)).sort()) {
10054
+ for (const entry2 of (await fs15.readdir(archiveRoot)).sort()) {
9940
10055
  if (!entry2.endsWith(`-${change}`)) continue;
9941
10056
  const candidate = `${archiveRoot}/${entry2}`;
9942
- if ((await fs14.stat(candidate)).isDirectory()) return candidate;
10057
+ if ((await fs15.stat(candidate)).isDirectory()) return candidate;
9943
10058
  }
9944
10059
  return null;
9945
10060
  }
@@ -9969,9 +10084,9 @@ async function annotateFrontmatter(output, file, archiveName, extraFields, dryRu
9969
10084
  output.stepsTotal += 1;
9970
10085
  return;
9971
10086
  }
9972
- const original = await fs14.readFile(file, "utf8");
10087
+ const original = await fs15.readFile(file, "utf8");
9973
10088
  const updated = annotatedMarkdown(original, archiveName, extraFields);
9974
- await fs14.writeFile(file, updated);
10089
+ await fs15.writeFile(file, updated);
9975
10090
  output.stderr.push(green(` [OK] Annotated: ${file}`));
9976
10091
  output.stepsOk += 1;
9977
10092
  output.stepsTotal += 1;
@@ -9980,10 +10095,10 @@ async function verifyMainSpecsClean() {
9980
10095
  const specsRoot = "openspec/specs";
9981
10096
  if (!await exists2(specsRoot)) return;
9982
10097
  let found = false;
9983
- for (const entry2 of await fs14.readdir(specsRoot)) {
10098
+ for (const entry2 of await fs15.readdir(specsRoot)) {
9984
10099
  const specFile = `${specsRoot}/${entry2}/spec.md`;
9985
10100
  if (!await exists2(specFile)) continue;
9986
- const matches = (await fs14.readFile(specFile, "utf8")).split(/\r?\n/u).map((line, index) => ({ line, number: index + 1 })).filter((item) => /^## (ADDED|MODIFIED|REMOVED|RENAMED) Requirements$/u.test(item.line));
10101
+ const matches = (await fs15.readFile(specFile, "utf8")).split(/\r?\n/u).map((line, index) => ({ line, number: index + 1 })).filter((item) => /^## (ADDED|MODIFIED|REMOVED|RENAMED) Requirements$/u.test(item.line));
9987
10102
  if (matches.length > 0) {
9988
10103
  found = true;
9989
10104
  process.stderr.write(
@@ -10238,7 +10353,7 @@ var classicArchiveCommand = async (args) => {
10238
10353
  var import_yaml6 = __toESM(require_dist(), 1);
10239
10354
  import { spawnSync as spawnSync2 } from "child_process";
10240
10355
  import { createHash as createHash4 } from "crypto";
10241
- import { existsSync, promises as fs18, readFileSync } from "fs";
10356
+ import { existsSync, promises as fs19, readFileSync } from "fs";
10242
10357
  import path19 from "path";
10243
10358
 
10244
10359
  // domains/comet-classic/classic-command-checks.ts
@@ -10420,7 +10535,7 @@ async function inspectClassicChange(changeDir, name) {
10420
10535
 
10421
10536
  // domains/comet-classic/classic-validate-command.ts
10422
10537
  var import_yaml4 = __toESM(require_dist(), 1);
10423
- import { promises as fs15 } from "fs";
10538
+ import { promises as fs16 } from "fs";
10424
10539
  import path17 from "path";
10425
10540
  var GREEN2 = "\x1B[32m";
10426
10541
  var RED2 = "\x1B[31m";
@@ -10471,7 +10586,7 @@ function color(code, message) {
10471
10586
  }
10472
10587
  async function exists3(file) {
10473
10588
  try {
10474
- await fs15.access(file);
10589
+ await fs16.access(file);
10475
10590
  return true;
10476
10591
  } catch (error) {
10477
10592
  if (error.code === "ENOENT") return false;
@@ -10506,7 +10621,7 @@ var classicValidateCommand = async (args) => {
10506
10621
  };
10507
10622
  let source;
10508
10623
  try {
10509
- source = await fs15.readFile(yamlFile, "utf8");
10624
+ source = await fs16.readFile(yamlFile, "utf8");
10510
10625
  } catch (error) {
10511
10626
  if (error.code === "ENOENT") {
10512
10627
  fail3(".comet.yaml does not exist");
@@ -10580,23 +10695,23 @@ var classicValidateCommand = async (args) => {
10580
10695
  // domains/comet-classic/classic-project-config.ts
10581
10696
  var import_yaml5 = __toESM(require_dist(), 1);
10582
10697
  import os from "os";
10583
- import { promises as fs17 } from "fs";
10698
+ import { promises as fs18 } from "fs";
10584
10699
  import path18 from "path";
10585
10700
 
10586
10701
  // platform/fs/file-system.ts
10587
- import { promises as fs16 } from "fs";
10702
+ import { promises as fs17 } from "fs";
10588
10703
  async function fileExists3(filePath) {
10589
10704
  try {
10590
- await fs16.access(filePath);
10705
+ await fs17.access(filePath);
10591
10706
  return true;
10592
10707
  } catch (error) {
10593
- if (isNotFoundError(error)) return false;
10708
+ if (isMissingPathError(error)) return false;
10594
10709
  throw error;
10595
10710
  }
10596
10711
  }
10597
10712
  async function readDir(dirPath) {
10598
10713
  try {
10599
- return await fs16.readdir(dirPath);
10714
+ return await fs17.readdir(dirPath);
10600
10715
  } catch (error) {
10601
10716
  const code = error?.code;
10602
10717
  if (code === "ENOENT" || code === "ENOTDIR") {
@@ -10605,8 +10720,9 @@ async function readDir(dirPath) {
10605
10720
  throw error;
10606
10721
  }
10607
10722
  }
10608
- function isNotFoundError(error) {
10609
- return error?.code === "ENOENT";
10723
+ function isMissingPathError(error) {
10724
+ const code = error?.code;
10725
+ return code === "ENOENT" || code === "ENOTDIR";
10610
10726
  }
10611
10727
 
10612
10728
  // domains/comet-classic/classic-project-config.ts
@@ -10627,7 +10743,7 @@ function configCandidates(options = {}) {
10627
10743
  async function readClassicConfigValue(field2, options = {}) {
10628
10744
  for (const candidate of configCandidates(options)) {
10629
10745
  if (!await fileExists3(candidate.file)) continue;
10630
- const document = (0, import_yaml5.parseDocument)(await fs17.readFile(candidate.file, "utf8"), {
10746
+ const document = (0, import_yaml5.parseDocument)(await fs18.readFile(candidate.file, "utf8"), {
10631
10747
  uniqueKeys: false
10632
10748
  });
10633
10749
  const root = document.toJS();
@@ -10705,7 +10821,7 @@ var GuardOutput = class {
10705
10821
  };
10706
10822
  async function exists4(file) {
10707
10823
  try {
10708
- await fs18.access(file);
10824
+ await fs19.access(file);
10709
10825
  return true;
10710
10826
  } catch (error) {
10711
10827
  if (error.code === "ENOENT") return false;
@@ -10714,7 +10830,7 @@ async function exists4(file) {
10714
10830
  }
10715
10831
  async function nonempty(file) {
10716
10832
  try {
10717
- return (await fs18.stat(file)).size > 0;
10833
+ return (await fs19.stat(file)).size > 0;
10718
10834
  } catch (error) {
10719
10835
  if (error.code === "ENOENT") return false;
10720
10836
  throw error;
@@ -10729,7 +10845,7 @@ async function resolveChangeDir(name) {
10729
10845
  }
10730
10846
  async function readField(changeDir, field2) {
10731
10847
  const file = path19.join(changeDir, ".comet.yaml");
10732
- const document = (0, import_yaml6.parseDocument)(await fs18.readFile(file, "utf8"), { uniqueKeys: false });
10848
+ const document = (0, import_yaml6.parseDocument)(await fs19.readFile(file, "utf8"), { uniqueKeys: false });
10733
10849
  if (document.errors.length > 0) {
10734
10850
  throw new GuardFailure(`ERROR: Invalid .comet.yaml: ${document.errors[0].message}`);
10735
10851
  }
@@ -10770,7 +10886,7 @@ function countEnglishWords(source) {
10770
10886
  }
10771
10887
  async function documentLanguageMatchesConfigured(changeDir, file) {
10772
10888
  const language = await configuredLanguage(changeDir);
10773
- const source = stripFencedCodeBlocks(await fs18.readFile(file, "utf8"));
10889
+ const source = stripFencedCodeBlocks(await fs19.readFile(file, "utf8"));
10774
10890
  const cjk = countCjkChars(source);
10775
10891
  const englishWords = countEnglishWords(source);
10776
10892
  if (language === "zh-CN" && cjk < 20 && englishWords >= 20) {
@@ -10794,7 +10910,7 @@ async function handoffSourceFiles(changeDir) {
10794
10910
  const files = [`${changeDir}/proposal.md`, `${changeDir}/design.md`, `${changeDir}/tasks.md`];
10795
10911
  const specs = `${changeDir}/specs`;
10796
10912
  if (await exists4(specs)) {
10797
- for (const entry2 of (await fs18.readdir(specs)).sort()) {
10913
+ for (const entry2 of (await fs19.readdir(specs)).sort()) {
10798
10914
  const spec = `${specs}/${entry2}/spec.md`;
10799
10915
  if (await exists4(spec)) files.push(spec);
10800
10916
  }
@@ -10886,7 +11002,7 @@ var INFERRED_COMMAND_SOURCES = [
10886
11002
  async function removedProjectCommandField(field2) {
10887
11003
  const config = path19.join(".comet", "config.yaml");
10888
11004
  if (!await exists4(config)) return false;
10889
- const document = (0, import_yaml6.parseDocument)(await fs18.readFile(config, "utf8"));
11005
+ const document = (0, import_yaml6.parseDocument)(await fs19.readFile(config, "utf8"));
10890
11006
  if (document.errors.length > 0) {
10891
11007
  throw new Error(
10892
11008
  `.comet/config.yaml is invalid YAML (${document.errors[0].message}); cannot check for removed "${field2}" field. Fix the config and retry.`
@@ -10974,7 +11090,7 @@ async function tasksAllDone(changeDir) {
10974
11090
  Next: restore or create tasks.md for this change before leaving build.`
10975
11091
  );
10976
11092
  }
10977
- const source = await fs18.readFile(tasks, "utf8");
11093
+ const source = await fs19.readFile(tasks, "utf8");
10978
11094
  if (!/- \[x\]/u.test(source)) {
10979
11095
  return fail(
10980
11096
  "tasks.md has no completed tasks.\nNext: complete implementation tasks and mark them with '- [x]'."
@@ -10993,7 +11109,7 @@ Next: complete or explicitly remove unfinished tasks, then mark tasks.md with '-
10993
11109
  async function tasksHasAny(changeDir) {
10994
11110
  const tasks = path19.join(changeDir, "tasks.md");
10995
11111
  if (!await exists4(tasks)) return false;
10996
- return /- \[/u.test(await fs18.readFile(tasks, "utf8"));
11112
+ return /- \[/u.test(await fs19.readFile(tasks, "utf8"));
10997
11113
  }
10998
11114
  async function planTasksAllDone(changeDir) {
10999
11115
  const plan = await readField(changeDir, "plan");
@@ -11004,7 +11120,7 @@ async function planTasksAllDone(changeDir) {
11004
11120
  Next: restore the Superpowers plan file or update .comet.yaml plan before leaving build.`
11005
11121
  );
11006
11122
  }
11007
- const source = await fs18.readFile(plan, "utf8");
11123
+ const source = await fs19.readFile(plan, "utf8");
11008
11124
  const unfinished = source.split(/\r?\n/u).map((line, index) => ({ line, number: index + 1 })).filter((entry2) => /^\s*- \[ \]/u.test(entry2.line));
11009
11125
  if (unfinished.length > 0) {
11010
11126
  return fail(
@@ -11118,7 +11234,7 @@ async function archivedIsTrue(changeDir) {
11118
11234
  return await readField(changeDir, "archived") === "true";
11119
11235
  }
11120
11236
  async function designDocFrontmatterHas(designDoc, field2, expected) {
11121
- const source = (await fs18.readFile(designDoc, "utf8")).replace(/^\uFEFF/u, "");
11237
+ const source = (await fs19.readFile(designDoc, "utf8")).replace(/^\uFEFF/u, "");
11122
11238
  let inFrontmatter = false;
11123
11239
  for (const line of source.split(/\r?\n/u)) {
11124
11240
  if (!inFrontmatter) {
@@ -11183,7 +11299,7 @@ async function designHandoffMarkdownTraceable(changeDir) {
11183
11299
  const markdown = `${context.replace(/\.json$/u, "")}.md`;
11184
11300
  if (!await nonempty(markdown))
11185
11301
  return fail(`design handoff markdown is missing or empty: ${markdown}`);
11186
- const source = await fs18.readFile(markdown, "utf8");
11302
+ const source = await fs19.readFile(markdown, "utf8");
11187
11303
  const lines = new Set(source.split(/\r?\n/u));
11188
11304
  const problems = [];
11189
11305
  if (!/^Generated-by: comet-handoff\.sh$/mu.test(source)) {
@@ -11211,7 +11327,7 @@ async function betaSpecJsonStructurallyValid(changeDir) {
11211
11327
  const context = await readField(changeDir, "handoff_context");
11212
11328
  if (!context || context === "null") return fail("handoff_context is missing from .comet.yaml");
11213
11329
  if (!await nonempty(context)) return fail(`spec-context.json is missing or empty: ${context}`);
11214
- const source = await fs18.readFile(context, "utf8");
11330
+ const source = await fs19.readFile(context, "utf8");
11215
11331
  const problems = [];
11216
11332
  let parsed;
11217
11333
  try {
@@ -11511,7 +11627,7 @@ Valid phases: open, design, build, verify, archive`
11511
11627
  // domains/comet-classic/classic-handoff.ts
11512
11628
  var import_yaml7 = __toESM(require_dist(), 1);
11513
11629
  import { createHash as createHash5 } from "crypto";
11514
- import { promises as fs19, readFileSync as readFileSync2 } from "fs";
11630
+ import { promises as fs20, readFileSync as readFileSync2 } from "fs";
11515
11631
  import path20 from "path";
11516
11632
  var GREEN4 = "\x1B[32m";
11517
11633
  var RED4 = "\x1B[31m";
@@ -11546,7 +11662,7 @@ var HandoffOutput = class {
11546
11662
  };
11547
11663
  async function exists5(file) {
11548
11664
  try {
11549
- await fs19.access(file);
11665
+ await fs20.access(file);
11550
11666
  return true;
11551
11667
  } catch (error) {
11552
11668
  if (error.code === "ENOENT") return false;
@@ -11555,7 +11671,7 @@ async function exists5(file) {
11555
11671
  }
11556
11672
  async function nonempty2(file) {
11557
11673
  try {
11558
- return (await fs19.stat(file)).size > 0;
11674
+ return (await fs20.stat(file)).size > 0;
11559
11675
  } catch (error) {
11560
11676
  if (error.code === "ENOENT") return false;
11561
11677
  throw error;
@@ -11584,7 +11700,7 @@ async function handoffSourceFiles2(changeDir) {
11584
11700
  const files = [`${changeDir}/proposal.md`, `${changeDir}/design.md`, `${changeDir}/tasks.md`];
11585
11701
  const specs = `${changeDir}/specs`;
11586
11702
  if (await exists5(specs)) {
11587
- for (const entry2 of (await fs19.readdir(specs)).sort()) {
11703
+ for (const entry2 of (await fs20.readdir(specs)).sort()) {
11588
11704
  const spec = `${specs}/${entry2}/spec.md`;
11589
11705
  if (await exists5(spec)) files.push(spec);
11590
11706
  }
@@ -11632,7 +11748,7 @@ async function writeMarkdownContext(changeDir, change, mode, contextHash, output
11632
11748
  ];
11633
11749
  for (const file of await handoffSourceFiles2(changeDir)) {
11634
11750
  if (!await exists5(file)) continue;
11635
- const content = await fs19.readFile(file, "utf8");
11751
+ const content = await fs20.readFile(file, "utf8");
11636
11752
  const total = lineCount(content);
11637
11753
  lines.push(
11638
11754
  `## ${file}`,
@@ -11657,7 +11773,7 @@ async function writeMarkdownContext(changeDir, change, mode, contextHash, output
11657
11773
  }
11658
11774
  lines.push("");
11659
11775
  }
11660
- await fs19.writeFile(output, lines.join("\n"));
11776
+ await fs20.writeFile(output, lines.join("\n"));
11661
11777
  }
11662
11778
  async function writeJsonContext(changeDir, change, mode, contextHash, output) {
11663
11779
  const entries = [];
@@ -11680,7 +11796,7 @@ async function writeJsonContext(changeDir, change, mode, contextHash, output) {
11680
11796
  "}",
11681
11797
  ""
11682
11798
  ].join("\n");
11683
- await fs19.writeFile(output, document);
11799
+ await fs20.writeFile(output, document);
11684
11800
  }
11685
11801
  async function writeSpecProjectionForFile(file, content) {
11686
11802
  return [
@@ -11720,11 +11836,11 @@ async function writeSpecMarkdownContext(changeDir, change, contextHash, output)
11720
11836
  const specs = `${changeDir}/specs`;
11721
11837
  let projected = false;
11722
11838
  if (await exists5(specs)) {
11723
- for (const entry2 of (await fs19.readdir(specs)).sort()) {
11839
+ for (const entry2 of (await fs20.readdir(specs)).sort()) {
11724
11840
  const spec = `${specs}/${entry2}/spec.md`;
11725
11841
  if (!await exists5(spec)) continue;
11726
11842
  projected = true;
11727
- lines.push(...await writeSpecProjectionForFile(spec, await fs19.readFile(spec, "utf8")));
11843
+ lines.push(...await writeSpecProjectionForFile(spec, await fs20.readFile(spec, "utf8")));
11728
11844
  }
11729
11845
  }
11730
11846
  if (!projected) {
@@ -11733,7 +11849,7 @@ async function writeSpecMarkdownContext(changeDir, change, contextHash, output)
11733
11849
  lines.push(
11734
11850
  "Full source files remain canonical. If a required heading or scenario is missing here, regenerate the handoff or read the source spec directly. Supporting files (proposal, design, tasks) are referenced by hash only."
11735
11851
  );
11736
- await fs19.writeFile(output, lines.join("\n"));
11852
+ await fs20.writeFile(output, lines.join("\n"));
11737
11853
  }
11738
11854
  async function writeSpecJsonContext(changeDir, change, contextHash, output) {
11739
11855
  const entries = [];
@@ -11742,7 +11858,7 @@ async function writeSpecJsonContext(changeDir, change, contextHash, output) {
11742
11858
  const role = /\/specs\/[^/]+\/spec\.md$/u.test(file) ? "spec" : "supporting";
11743
11859
  entries.push({ path: file, sha256: hashFile2(file), role });
11744
11860
  }
11745
- await fs19.writeFile(
11861
+ await fs20.writeFile(
11746
11862
  output,
11747
11863
  `${JSON.stringify(
11748
11864
  {
@@ -11762,7 +11878,7 @@ async function writeSpecJsonContext(changeDir, change, contextHash, output) {
11762
11878
  }
11763
11879
  async function readField2(changeDir, field2) {
11764
11880
  const file = path20.join(changeDir, ".comet.yaml");
11765
- const document = (0, import_yaml7.parseDocument)(await fs19.readFile(file, "utf8"), { uniqueKeys: false });
11881
+ const document = (0, import_yaml7.parseDocument)(await fs20.readFile(file, "utf8"), { uniqueKeys: false });
11766
11882
  if (document.errors.length > 0) {
11767
11883
  throw new HandoffFailure(`ERROR: Invalid .comet.yaml: ${document.errors[0].message}`);
11768
11884
  }
@@ -11796,7 +11912,7 @@ async function completedHandoffIsCurrent(changeDir, run, contextHash, contextJso
11796
11912
  readCheckpoint(changeDir, run.checkpointRef)
11797
11913
  ]);
11798
11914
  if (!await exists5(contextJson) || !await exists5(contextMd)) return false;
11799
- if (context !== await fs19.readFile(contextMd, "utf8")) return false;
11915
+ if (context !== await fs20.readFile(contextMd, "utf8")) return false;
11800
11916
  if (artifacts.handoff_context !== contextJson || artifacts.handoff_markdown !== contextMd) {
11801
11917
  return false;
11802
11918
  }
@@ -11919,7 +12035,7 @@ var classicHandoffCommand = async (args) => {
11919
12035
  run: pendingRun,
11920
12036
  unknownKeys: (await readClassicState(changeDir)).unknownKeys
11921
12037
  });
11922
- await fs19.mkdir(handoffDir, { recursive: true });
12038
+ await fs20.mkdir(handoffDir, { recursive: true });
11923
12039
  if (handoffMode === "beta") {
11924
12040
  await writeSpecMarkdownContext(changeDir, change, contextHash, contextMd);
11925
12041
  await writeSpecJsonContext(changeDir, change, contextHash, contextJson);
@@ -11927,7 +12043,7 @@ var classicHandoffCommand = async (args) => {
11927
12043
  await writeMarkdownContext(changeDir, change, handoffMode, contextHash, contextMd);
11928
12044
  await writeJsonContext(changeDir, change, handoffMode, contextHash, contextJson);
11929
12045
  }
11930
- const context = await fs19.readFile(contextMd, "utf8");
12046
+ const context = await fs20.readFile(contextMd, "utf8");
11931
12047
  await writeContext(changeDir, pendingRun.contextRef, context);
11932
12048
  const artifacts = {
11933
12049
  ...await readArtifacts(changeDir, pendingRun.artifactsRef),
@@ -11984,7 +12100,7 @@ var classicHandoffCommand = async (args) => {
11984
12100
  };
11985
12101
 
11986
12102
  // domains/comet-classic/classic-hook-guard.ts
11987
- import { existsSync as existsSync2, promises as fs20, readFileSync as readFileSync3 } from "fs";
12103
+ import { existsSync as existsSync2, promises as fs21, readFileSync as readFileSync3 } from "fs";
11988
12104
  import path21 from "path";
11989
12105
  function result(exitCode, message) {
11990
12106
  return { exitCode, stderr: message + "\n" };
@@ -12029,7 +12145,7 @@ async function physicalPathForPossiblyMissingTarget(target) {
12029
12145
  let cursor = resolved;
12030
12146
  while (cursor && cursor !== root) {
12031
12147
  try {
12032
- const physicalBase = await fs20.realpath(cursor);
12148
+ const physicalBase = await fs21.realpath(cursor);
12033
12149
  return path21.join(physicalBase, ...missingSegments.reverse());
12034
12150
  } catch (error) {
12035
12151
  const code = error.code;
@@ -12039,7 +12155,7 @@ async function physicalPathForPossiblyMissingTarget(target) {
12039
12155
  }
12040
12156
  }
12041
12157
  try {
12042
- const physicalRoot = await fs20.realpath(root);
12158
+ const physicalRoot = await fs21.realpath(root);
12043
12159
  return path21.join(physicalRoot, ...missingSegments.reverse());
12044
12160
  } catch {
12045
12161
  return null;
@@ -12052,7 +12168,7 @@ async function projectRelative(target, projectRoot2) {
12052
12168
  if (rootRelative !== null) return rootRelative;
12053
12169
  try {
12054
12170
  const physicalCandidate = await physicalPathForPossiblyMissingTarget(rawCandidate);
12055
- const physicalRoot = await fs20.realpath(projectRoot2);
12171
+ const physicalRoot = await fs21.realpath(projectRoot2);
12056
12172
  if (physicalCandidate) {
12057
12173
  const physicalRootRelative = relativeToProjectRoot(physicalCandidate, physicalRoot);
12058
12174
  if (physicalRootRelative !== null) return physicalRootRelative;
@@ -12092,7 +12208,7 @@ async function activeChanges(projectRoot2) {
12092
12208
  const changesDir = path21.join(projectRoot2, "openspec", "changes");
12093
12209
  const governingChanges = [];
12094
12210
  if (!existsSync2(changesDir)) return governingChanges;
12095
- for (const entry2 of (await fs20.readdir(changesDir, { withFileTypes: true })).sort(
12211
+ for (const entry2 of (await fs21.readdir(changesDir, { withFileTypes: true })).sort(
12096
12212
  (left, right) => left.name.localeCompare(right.name)
12097
12213
  )) {
12098
12214
  if (!entry2.isDirectory() || entry2.name === "archive") continue;
@@ -12877,7 +12993,7 @@ var classicIntentCommand = async (args, _options) => {
12877
12993
 
12878
12994
  // domains/comet-classic/classic-resume-probe.ts
12879
12995
  import path22 from "path";
12880
- import { promises as fs21 } from "fs";
12996
+ import { promises as fs22 } from "fs";
12881
12997
  import { spawn } from "child_process";
12882
12998
  var COMET_RESUME_PROBE_SCHEMA_VERSION = "comet.resume_probe.v1";
12883
12999
  function isRecord3(value) {
@@ -12920,7 +13036,7 @@ function result3(action, change, confidence, reason, evidence = []) {
12920
13036
  }
12921
13037
  async function readIfExists(filePath) {
12922
13038
  if (!await fileExists3(filePath)) return "";
12923
- return fs21.readFile(filePath, "utf8");
13039
+ return fs22.readFile(filePath, "utf8");
12924
13040
  }
12925
13041
  async function changeSearchText(changeDir, classic) {
12926
13042
  const files = ["proposal.md", "design.md", "tasks.md"];
@@ -13000,7 +13116,7 @@ async function discoverActiveChanges(projectRoot2) {
13000
13116
  for (const entry2 of entries) {
13001
13117
  if (entry2 === "archive") continue;
13002
13118
  const changeDir = path22.join(changesDir, entry2);
13003
- const stat = await fs21.stat(changeDir).catch(() => null);
13119
+ const stat = await fs22.stat(changeDir).catch(() => null);
13004
13120
  if (!stat?.isDirectory()) continue;
13005
13121
  const hasCometState = await fileExists3(path22.join(changeDir, ".comet.yaml"));
13006
13122
  if (!hasCometState) {
@@ -13305,7 +13421,7 @@ var classicResumeProbeCommand = async (args) => {
13305
13421
  var import_yaml8 = __toESM(require_dist(), 1);
13306
13422
  import { spawnSync as spawnSync3 } from "child_process";
13307
13423
  import { randomUUID as randomUUID8 } from "crypto";
13308
- import { existsSync as existsSync3, promises as fs22 } from "fs";
13424
+ import { existsSync as existsSync3, promises as fs23 } from "fs";
13309
13425
  import path23 from "path";
13310
13426
  init_state();
13311
13427
  var GREEN5 = "\x1B[32m";
@@ -13420,7 +13536,7 @@ function validateRelativePath(value, field2) {
13420
13536
  }
13421
13537
  async function exists6(file) {
13422
13538
  try {
13423
- await fs22.access(file);
13539
+ await fs23.access(file);
13424
13540
  return true;
13425
13541
  } catch (error) {
13426
13542
  if (error.code === "ENOENT") return false;
@@ -13429,7 +13545,7 @@ async function exists6(file) {
13429
13545
  }
13430
13546
  async function nonempty3(file) {
13431
13547
  try {
13432
- return (await fs22.stat(file)).size > 0;
13548
+ return (await fs23.stat(file)).size > 0;
13433
13549
  } catch (error) {
13434
13550
  if (error.code === "ENOENT") return false;
13435
13551
  throw error;
@@ -13441,7 +13557,7 @@ async function changeDirectory2(name) {
13441
13557
  async function readDocument2(file) {
13442
13558
  let source;
13443
13559
  try {
13444
- source = await fs22.readFile(file, "utf8");
13560
+ source = await fs23.readFile(file, "utf8");
13445
13561
  } catch (error) {
13446
13562
  if (error.code === "ENOENT") {
13447
13563
  fail2(
@@ -13455,13 +13571,13 @@ async function readDocument2(file) {
13455
13571
  return document;
13456
13572
  }
13457
13573
  async function atomicWrite2(file, content) {
13458
- await fs22.mkdir(path23.dirname(file), { recursive: true });
13574
+ await fs23.mkdir(path23.dirname(file), { recursive: true });
13459
13575
  const temporary = `${file}.${randomUUID8()}.tmp`;
13460
13576
  try {
13461
- await fs22.writeFile(temporary, content, "utf8");
13462
- await fs22.rename(temporary, file);
13577
+ await fs23.writeFile(temporary, content, "utf8");
13578
+ await fs23.rename(temporary, file);
13463
13579
  } catch (error) {
13464
- await fs22.rm(temporary, { force: true });
13580
+ await fs23.rm(temporary, { force: true });
13465
13581
  throw error;
13466
13582
  }
13467
13583
  }
@@ -13735,7 +13851,7 @@ async function init(output, name, workflow) {
13735
13851
  validateEnum(workflow, PROFILES);
13736
13852
  const { file, label, directory } = await stateFile(name);
13737
13853
  if (await exists6(file)) fail2(`ERROR: .comet.yaml already exists at ${label}/.comet.yaml`);
13738
- await fs22.mkdir(directory, { recursive: true });
13854
+ await fs23.mkdir(directory, { recursive: true });
13739
13855
  const preset = workflow !== "full";
13740
13856
  const reviewMode = preset ? "off" : await reviewModeDefault();
13741
13857
  const document = new import_yaml8.Document({
@@ -13965,7 +14081,7 @@ async function taskCheckoff(output, taskFile, taskText) {
13965
14081
  if (!taskText) fail2("ERROR: Task text cannot be empty");
13966
14082
  const file = path23.resolve(taskFile);
13967
14083
  if (!await exists6(file)) fail2(`ERROR: Task file not found: ${taskFile}`);
13968
- const lines = (await fs22.readFile(file, "utf8")).split(/\r?\n/u);
14084
+ const lines = (await fs23.readFile(file, "utf8")).split(/\r?\n/u);
13969
14085
  const matches = lines.filter(
13970
14086
  (line) => [`- [ ] ${taskText}`, `- [x] ${taskText}`, `- [X] ${taskText}`].includes(line)
13971
14087
  );
@@ -14141,14 +14257,14 @@ async function recoverBuild(output, name, directory, workflow) {
14141
14257
  );
14142
14258
  return;
14143
14259
  }
14144
- const lines = (await fs22.readFile(tasks, "utf8")).split(/\r?\n/u);
14260
+ const lines = (await fs23.readFile(tasks, "utf8")).split(/\r?\n/u);
14145
14261
  const total = lines.filter((line) => /^\s*- \[[ xX]\] /u.test(line)).length;
14146
14262
  const done = lines.filter((line) => /^\s*- \[[xX]\] /u.test(line)).length;
14147
14263
  const pending = total - done;
14148
14264
  let planTotal = 0;
14149
14265
  let planDone = 0;
14150
14266
  if (plan && plan !== "null" && await exists6(path23.resolve(plan))) {
14151
- const planLines = (await fs22.readFile(path23.resolve(plan), "utf8")).split(/\r?\n/u);
14267
+ const planLines = (await fs23.readFile(path23.resolve(plan), "utf8")).split(/\r?\n/u);
14152
14268
  planTotal = planLines.filter((line) => /^\s*- \[[ xX]\] /u.test(line)).length;
14153
14269
  planDone = planLines.filter((line) => /^\s*- \[[xX]\] /u.test(line)).length;
14154
14270
  }
@@ -14280,18 +14396,18 @@ async function scale(output, name) {
14280
14396
  const { file, directory, label } = await stateFile(name);
14281
14397
  if (!await exists6(file)) fail2(`ERROR: .comet.yaml not found at ${label}/.comet.yaml`);
14282
14398
  const tasksFile = path23.join(directory, "tasks.md");
14283
- const taskCount = await exists6(tasksFile) ? (await fs22.readFile(tasksFile, "utf8")).split(/\r?\n/u).filter((line) => /^- \[/u.test(line)).length : 0;
14399
+ const taskCount = await exists6(tasksFile) ? (await fs23.readFile(tasksFile, "utf8")).split(/\r?\n/u).filter((line) => /^- \[/u.test(line)).length : 0;
14284
14400
  const specs = path23.join(directory, "specs");
14285
14401
  let deltaSpecs = 0;
14286
14402
  if (await exists6(specs)) {
14287
- for (const entry2 of await fs22.readdir(specs)) {
14403
+ for (const entry2 of await fs23.readdir(specs)) {
14288
14404
  if (await exists6(path23.join(specs, entry2, "spec.md"))) deltaSpecs += 1;
14289
14405
  }
14290
14406
  }
14291
14407
  const plan = await readField3(name, "plan");
14292
14408
  let baseRef = "";
14293
14409
  if (plan && plan !== "null" && await exists6(path23.resolve(plan))) {
14294
- const match = (await fs22.readFile(path23.resolve(plan), "utf8")).match(/^base-ref:\s*(.+)$/mu);
14410
+ const match = (await fs23.readFile(path23.resolve(plan), "utf8")).match(/^base-ref:\s*(.+)$/mu);
14295
14411
  baseRef = match?.[1].trim() ?? "";
14296
14412
  }
14297
14413
  if (!baseRef) baseRef = await readField3(name, "base_ref");