@rpamis/comet 0.4.0-beta.7 → 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 (161) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +3 -3
  3. package/assets/manifest.json +1 -1
  4. package/assets/skills/comet/reference/comet-yaml-fields.md +2 -2
  5. package/assets/skills/comet/scripts/comet-entry-runtime.mjs +118 -6
  6. package/assets/skills/comet/scripts/comet-hook-router.mjs +244 -26
  7. package/assets/skills/comet/scripts/comet-runtime.mjs +221 -104
  8. package/assets/skills/comet-archive/SKILL.md +29 -20
  9. package/assets/skills/comet-native/SKILL.md +23 -15
  10. package/assets/skills/comet-native/reference/artifacts.md +19 -4
  11. package/assets/skills/comet-native/reference/commands.md +8 -2
  12. package/assets/skills/comet-native/reference/recovery.md +1 -1
  13. package/assets/skills/comet-native/scripts/comet-native-runtime.mjs +1136 -471
  14. package/assets/skills-zh/comet/reference/comet-yaml-fields.md +2 -2
  15. package/assets/skills-zh/comet-archive/SKILL.md +29 -20
  16. package/assets/skills-zh/comet-native/SKILL.md +23 -15
  17. package/assets/skills-zh/comet-native/reference/artifacts.md +20 -5
  18. package/assets/skills-zh/comet-native/reference/commands.md +8 -2
  19. package/assets/skills-zh/comet-native/reference/recovery.md +1 -1
  20. package/dist/app/commands/init.d.ts.map +1 -1
  21. package/dist/app/commands/init.js +3 -6
  22. package/dist/app/commands/init.js.map +1 -1
  23. package/dist/domains/bundle/bundle-platform.d.ts.map +1 -1
  24. package/dist/domains/bundle/bundle-platform.js +1 -1
  25. package/dist/domains/bundle/bundle-platform.js.map +1 -1
  26. package/dist/domains/comet-classic/classic-guard.d.ts.map +1 -1
  27. package/dist/domains/comet-classic/classic-guard.js +3 -2
  28. package/dist/domains/comet-classic/classic-guard.js.map +1 -1
  29. package/dist/domains/comet-entry/current-selection.d.ts.map +1 -1
  30. package/dist/domains/comet-entry/current-selection.js +6 -8
  31. package/dist/domains/comet-entry/current-selection.js.map +1 -1
  32. package/dist/domains/comet-entry/project-status.d.ts.map +1 -1
  33. package/dist/domains/comet-entry/project-status.js +5 -1
  34. package/dist/domains/comet-entry/project-status.js.map +1 -1
  35. package/dist/domains/comet-native/native-archive-transaction.d.ts.map +1 -1
  36. package/dist/domains/comet-native/native-archive-transaction.js +5 -6
  37. package/dist/domains/comet-native/native-archive-transaction.js.map +1 -1
  38. package/dist/domains/comet-native/native-atomic-file.d.ts.map +1 -1
  39. package/dist/domains/comet-native/native-atomic-file.js +11 -7
  40. package/dist/domains/comet-native/native-atomic-file.js.map +1 -1
  41. package/dist/domains/comet-native/native-bounded-file.d.ts.map +1 -1
  42. package/dist/domains/comet-native/native-bounded-file.js +11 -7
  43. package/dist/domains/comet-native/native-bounded-file.js.map +1 -1
  44. package/dist/domains/comet-native/native-build-evidence.js +2 -2
  45. package/dist/domains/comet-native/native-build-evidence.js.map +1 -1
  46. package/dist/domains/comet-native/native-change.d.ts +4 -2
  47. package/dist/domains/comet-native/native-change.d.ts.map +1 -1
  48. package/dist/domains/comet-native/native-change.js +18 -4
  49. package/dist/domains/comet-native/native-change.js.map +1 -1
  50. package/dist/domains/comet-native/native-check-receipt-storage.d.ts.map +1 -1
  51. package/dist/domains/comet-native/native-check-receipt-storage.js +11 -7
  52. package/dist/domains/comet-native/native-check-receipt-storage.js.map +1 -1
  53. package/dist/domains/comet-native/native-check-receipt.d.ts.map +1 -1
  54. package/dist/domains/comet-native/native-check-receipt.js +16 -11
  55. package/dist/domains/comet-native/native-check-receipt.js.map +1 -1
  56. package/dist/domains/comet-native/native-cli.d.ts.map +1 -1
  57. package/dist/domains/comet-native/native-cli.js +120 -16
  58. package/dist/domains/comet-native/native-cli.js.map +1 -1
  59. package/dist/domains/comet-native/native-config.d.ts +5 -1
  60. package/dist/domains/comet-native/native-config.d.ts.map +1 -1
  61. package/dist/domains/comet-native/native-config.js +78 -0
  62. package/dist/domains/comet-native/native-config.js.map +1 -1
  63. package/dist/domains/comet-native/native-continuation.d.ts +2 -1
  64. package/dist/domains/comet-native/native-continuation.d.ts.map +1 -1
  65. package/dist/domains/comet-native/native-continuation.js +13 -5
  66. package/dist/domains/comet-native/native-continuation.js.map +1 -1
  67. package/dist/domains/comet-native/native-diagnostics.d.ts +7 -3
  68. package/dist/domains/comet-native/native-diagnostics.d.ts.map +1 -1
  69. package/dist/domains/comet-native/native-diagnostics.js +19 -9
  70. package/dist/domains/comet-native/native-diagnostics.js.map +1 -1
  71. package/dist/domains/comet-native/native-evidence-retention.d.ts.map +1 -1
  72. package/dist/domains/comet-native/native-evidence-retention.js +5 -3
  73. package/dist/domains/comet-native/native-evidence-retention.js.map +1 -1
  74. package/dist/domains/comet-native/native-evidence-storage.d.ts.map +1 -1
  75. package/dist/domains/comet-native/native-evidence-storage.js +11 -7
  76. package/dist/domains/comet-native/native-evidence-storage.js.map +1 -1
  77. package/dist/domains/comet-native/native-file-identity.d.ts +3 -0
  78. package/dist/domains/comet-native/native-file-identity.d.ts.map +1 -0
  79. package/dist/domains/comet-native/native-file-identity.js +2 -0
  80. package/dist/domains/comet-native/native-file-identity.js.map +1 -0
  81. package/dist/domains/comet-native/native-findings.d.ts.map +1 -1
  82. package/dist/domains/comet-native/native-findings.js +19 -1
  83. package/dist/domains/comet-native/native-findings.js.map +1 -1
  84. package/dist/domains/comet-native/native-guards.d.ts +2 -1
  85. package/dist/domains/comet-native/native-guards.d.ts.map +1 -1
  86. package/dist/domains/comet-native/native-guards.js +14 -0
  87. package/dist/domains/comet-native/native-guards.js.map +1 -1
  88. package/dist/domains/comet-native/native-lock.d.ts.map +1 -1
  89. package/dist/domains/comet-native/native-lock.js +30 -35
  90. package/dist/domains/comet-native/native-lock.js.map +1 -1
  91. package/dist/domains/comet-native/native-protected-file.d.ts.map +1 -1
  92. package/dist/domains/comet-native/native-protected-file.js +9 -8
  93. package/dist/domains/comet-native/native-protected-file.js.map +1 -1
  94. package/dist/domains/comet-native/native-root-move.d.ts.map +1 -1
  95. package/dist/domains/comet-native/native-root-move.js +2 -0
  96. package/dist/domains/comet-native/native-root-move.js.map +1 -1
  97. package/dist/domains/comet-native/native-run-store.d.ts.map +1 -1
  98. package/dist/domains/comet-native/native-run-store.js +9 -8
  99. package/dist/domains/comet-native/native-run-store.js.map +1 -1
  100. package/dist/domains/comet-native/native-snapshot.d.ts +5 -1
  101. package/dist/domains/comet-native/native-snapshot.d.ts.map +1 -1
  102. package/dist/domains/comet-native/native-snapshot.js +225 -5
  103. package/dist/domains/comet-native/native-snapshot.js.map +1 -1
  104. package/dist/domains/comet-native/native-transitions.d.ts +2 -1
  105. package/dist/domains/comet-native/native-transitions.d.ts.map +1 -1
  106. package/dist/domains/comet-native/native-transitions.js +34 -11
  107. package/dist/domains/comet-native/native-transitions.js.map +1 -1
  108. package/dist/domains/comet-native/native-types.d.ts +16 -0
  109. package/dist/domains/comet-native/native-types.d.ts.map +1 -1
  110. package/dist/domains/comet-native/native-verification-runtime.d.ts.map +1 -1
  111. package/dist/domains/comet-native/native-verification-runtime.js +5 -3
  112. package/dist/domains/comet-native/native-verification-runtime.js.map +1 -1
  113. package/dist/domains/comet-native/native-verification-scope.d.ts +2 -1
  114. package/dist/domains/comet-native/native-verification-scope.d.ts.map +1 -1
  115. package/dist/domains/comet-native/native-verification-scope.js +6 -1
  116. package/dist/domains/comet-native/native-verification-scope.js.map +1 -1
  117. package/dist/domains/dashboard/native-collector.d.ts.map +1 -1
  118. package/dist/domains/dashboard/native-collector.js +4 -1
  119. package/dist/domains/dashboard/native-collector.js.map +1 -1
  120. package/dist/domains/dashboard/web/index.html +14 -14
  121. package/dist/domains/skill/platform-install.d.ts.map +1 -1
  122. package/dist/domains/skill/platform-install.js +9 -0
  123. package/dist/domains/skill/platform-install.js.map +1 -1
  124. package/dist/domains/skill/project-instructions.d.ts.map +1 -1
  125. package/dist/domains/skill/project-instructions.js +4 -2
  126. package/dist/domains/skill/project-instructions.js.map +1 -1
  127. package/dist/domains/workflow-contract/project-config.d.ts +1 -1
  128. package/dist/domains/workflow-contract/project-config.d.ts.map +1 -1
  129. package/dist/domains/workflow-contract/project-config.js +24 -2
  130. package/dist/domains/workflow-contract/project-config.js.map +1 -1
  131. package/dist/platform/fs/file-identity.d.ts +9 -0
  132. package/dist/platform/fs/file-identity.d.ts.map +1 -0
  133. package/dist/platform/fs/file-identity.js +21 -0
  134. package/dist/platform/fs/file-identity.js.map +1 -0
  135. package/dist/platform/fs/file-system.d.ts.map +1 -1
  136. package/dist/platform/fs/file-system.js +13 -9
  137. package/dist/platform/fs/file-system.js.map +1 -1
  138. package/dist/platform/fs/race-safe-read.d.ts +47 -0
  139. package/dist/platform/fs/race-safe-read.d.ts.map +1 -0
  140. package/dist/platform/fs/race-safe-read.js +132 -0
  141. package/dist/platform/fs/race-safe-read.js.map +1 -0
  142. package/eval/README.md +1 -1
  143. package/eval/local/tasks/comet-native-clarification-depth/environment/Dockerfile +27 -0
  144. package/eval/local/tasks/comet-native-clarification-depth/environment/test_wordcount.py +25 -0
  145. package/eval/local/tasks/comet-native-clarification-depth/environment/wordcount.py +36 -0
  146. package/eval/local/tasks/comet-native-clarification-depth/instruction.md +17 -0
  147. package/eval/local/tasks/comet-native-clarification-depth/task.toml +47 -0
  148. package/eval/local/tasks/comet-native-clarification-depth/validation/test_native_clarification_depth.py +618 -0
  149. package/eval/local/tasks/comet-native-clarification-modes/validation/test_native_clarification_modes.py +1 -1
  150. package/eval/local/tasks/index.yaml +6 -0
  151. package/eval/local/tests/conftest.py +146 -2
  152. package/eval/local/tests/scaffold/test_conftest_helpers.py +188 -1
  153. package/eval/local/tests/scaffold/test_tasks.py +440 -8
  154. package/eval/local/tests/scaffold/test_utils.py +114 -1
  155. package/eval/local/tests/tasks/test_tasks.py +2 -0
  156. package/eval/pyproject.toml +4 -4
  157. package/eval/scaffold/python/aligned_comparison.py +1 -0
  158. package/eval/scaffold/python/tasks.py +2 -0
  159. package/eval/scaffold/shell/docker.sh +1 -1
  160. package/eval/scaffold/shell/run-claude-loop.sh +25 -2
  161. package/package.json +8 -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,8 @@ 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");
11303
+ const lines = new Set(source.split(/\r?\n/u));
11187
11304
  const problems = [];
11188
11305
  if (!/^Generated-by: comet-handoff\.sh$/mu.test(source)) {
11189
11306
  problems.push("handoff markdown is missing Generated-by marker");
@@ -11193,10 +11310,10 @@ async function designHandoffMarkdownTraceable(changeDir) {
11193
11310
  }
11194
11311
  for (const file of await handoffSourceFiles(changeDir)) {
11195
11312
  if (!await exists4(file)) continue;
11196
- if (!new RegExp(`^- Source: ${file}$`, "mu").test(source)) {
11313
+ if (!lines.has(`- Source: ${file}`)) {
11197
11314
  problems.push(`handoff markdown is missing source reference: ${file}`);
11198
11315
  }
11199
- if (!new RegExp(`^- SHA256: ${hashFile(file)}$`, "mu").test(source)) {
11316
+ if (!lines.has(`- SHA256: ${hashFile(file)}`)) {
11200
11317
  problems.push(`handoff markdown is missing current sha256 for: ${file}`);
11201
11318
  }
11202
11319
  }
@@ -11210,7 +11327,7 @@ async function betaSpecJsonStructurallyValid(changeDir) {
11210
11327
  const context = await readField(changeDir, "handoff_context");
11211
11328
  if (!context || context === "null") return fail("handoff_context is missing from .comet.yaml");
11212
11329
  if (!await nonempty(context)) return fail(`spec-context.json is missing or empty: ${context}`);
11213
- const source = await fs18.readFile(context, "utf8");
11330
+ const source = await fs19.readFile(context, "utf8");
11214
11331
  const problems = [];
11215
11332
  let parsed;
11216
11333
  try {
@@ -11510,7 +11627,7 @@ Valid phases: open, design, build, verify, archive`
11510
11627
  // domains/comet-classic/classic-handoff.ts
11511
11628
  var import_yaml7 = __toESM(require_dist(), 1);
11512
11629
  import { createHash as createHash5 } from "crypto";
11513
- import { promises as fs19, readFileSync as readFileSync2 } from "fs";
11630
+ import { promises as fs20, readFileSync as readFileSync2 } from "fs";
11514
11631
  import path20 from "path";
11515
11632
  var GREEN4 = "\x1B[32m";
11516
11633
  var RED4 = "\x1B[31m";
@@ -11545,7 +11662,7 @@ var HandoffOutput = class {
11545
11662
  };
11546
11663
  async function exists5(file) {
11547
11664
  try {
11548
- await fs19.access(file);
11665
+ await fs20.access(file);
11549
11666
  return true;
11550
11667
  } catch (error) {
11551
11668
  if (error.code === "ENOENT") return false;
@@ -11554,7 +11671,7 @@ async function exists5(file) {
11554
11671
  }
11555
11672
  async function nonempty2(file) {
11556
11673
  try {
11557
- return (await fs19.stat(file)).size > 0;
11674
+ return (await fs20.stat(file)).size > 0;
11558
11675
  } catch (error) {
11559
11676
  if (error.code === "ENOENT") return false;
11560
11677
  throw error;
@@ -11583,7 +11700,7 @@ async function handoffSourceFiles2(changeDir) {
11583
11700
  const files = [`${changeDir}/proposal.md`, `${changeDir}/design.md`, `${changeDir}/tasks.md`];
11584
11701
  const specs = `${changeDir}/specs`;
11585
11702
  if (await exists5(specs)) {
11586
- for (const entry2 of (await fs19.readdir(specs)).sort()) {
11703
+ for (const entry2 of (await fs20.readdir(specs)).sort()) {
11587
11704
  const spec = `${specs}/${entry2}/spec.md`;
11588
11705
  if (await exists5(spec)) files.push(spec);
11589
11706
  }
@@ -11631,7 +11748,7 @@ async function writeMarkdownContext(changeDir, change, mode, contextHash, output
11631
11748
  ];
11632
11749
  for (const file of await handoffSourceFiles2(changeDir)) {
11633
11750
  if (!await exists5(file)) continue;
11634
- const content = await fs19.readFile(file, "utf8");
11751
+ const content = await fs20.readFile(file, "utf8");
11635
11752
  const total = lineCount(content);
11636
11753
  lines.push(
11637
11754
  `## ${file}`,
@@ -11656,7 +11773,7 @@ async function writeMarkdownContext(changeDir, change, mode, contextHash, output
11656
11773
  }
11657
11774
  lines.push("");
11658
11775
  }
11659
- await fs19.writeFile(output, lines.join("\n"));
11776
+ await fs20.writeFile(output, lines.join("\n"));
11660
11777
  }
11661
11778
  async function writeJsonContext(changeDir, change, mode, contextHash, output) {
11662
11779
  const entries = [];
@@ -11679,7 +11796,7 @@ async function writeJsonContext(changeDir, change, mode, contextHash, output) {
11679
11796
  "}",
11680
11797
  ""
11681
11798
  ].join("\n");
11682
- await fs19.writeFile(output, document);
11799
+ await fs20.writeFile(output, document);
11683
11800
  }
11684
11801
  async function writeSpecProjectionForFile(file, content) {
11685
11802
  return [
@@ -11719,11 +11836,11 @@ async function writeSpecMarkdownContext(changeDir, change, contextHash, output)
11719
11836
  const specs = `${changeDir}/specs`;
11720
11837
  let projected = false;
11721
11838
  if (await exists5(specs)) {
11722
- for (const entry2 of (await fs19.readdir(specs)).sort()) {
11839
+ for (const entry2 of (await fs20.readdir(specs)).sort()) {
11723
11840
  const spec = `${specs}/${entry2}/spec.md`;
11724
11841
  if (!await exists5(spec)) continue;
11725
11842
  projected = true;
11726
- lines.push(...await writeSpecProjectionForFile(spec, await fs19.readFile(spec, "utf8")));
11843
+ lines.push(...await writeSpecProjectionForFile(spec, await fs20.readFile(spec, "utf8")));
11727
11844
  }
11728
11845
  }
11729
11846
  if (!projected) {
@@ -11732,7 +11849,7 @@ async function writeSpecMarkdownContext(changeDir, change, contextHash, output)
11732
11849
  lines.push(
11733
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."
11734
11851
  );
11735
- await fs19.writeFile(output, lines.join("\n"));
11852
+ await fs20.writeFile(output, lines.join("\n"));
11736
11853
  }
11737
11854
  async function writeSpecJsonContext(changeDir, change, contextHash, output) {
11738
11855
  const entries = [];
@@ -11741,7 +11858,7 @@ async function writeSpecJsonContext(changeDir, change, contextHash, output) {
11741
11858
  const role = /\/specs\/[^/]+\/spec\.md$/u.test(file) ? "spec" : "supporting";
11742
11859
  entries.push({ path: file, sha256: hashFile2(file), role });
11743
11860
  }
11744
- await fs19.writeFile(
11861
+ await fs20.writeFile(
11745
11862
  output,
11746
11863
  `${JSON.stringify(
11747
11864
  {
@@ -11761,7 +11878,7 @@ async function writeSpecJsonContext(changeDir, change, contextHash, output) {
11761
11878
  }
11762
11879
  async function readField2(changeDir, field2) {
11763
11880
  const file = path20.join(changeDir, ".comet.yaml");
11764
- 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 });
11765
11882
  if (document.errors.length > 0) {
11766
11883
  throw new HandoffFailure(`ERROR: Invalid .comet.yaml: ${document.errors[0].message}`);
11767
11884
  }
@@ -11795,7 +11912,7 @@ async function completedHandoffIsCurrent(changeDir, run, contextHash, contextJso
11795
11912
  readCheckpoint(changeDir, run.checkpointRef)
11796
11913
  ]);
11797
11914
  if (!await exists5(contextJson) || !await exists5(contextMd)) return false;
11798
- if (context !== await fs19.readFile(contextMd, "utf8")) return false;
11915
+ if (context !== await fs20.readFile(contextMd, "utf8")) return false;
11799
11916
  if (artifacts.handoff_context !== contextJson || artifacts.handoff_markdown !== contextMd) {
11800
11917
  return false;
11801
11918
  }
@@ -11918,7 +12035,7 @@ var classicHandoffCommand = async (args) => {
11918
12035
  run: pendingRun,
11919
12036
  unknownKeys: (await readClassicState(changeDir)).unknownKeys
11920
12037
  });
11921
- await fs19.mkdir(handoffDir, { recursive: true });
12038
+ await fs20.mkdir(handoffDir, { recursive: true });
11922
12039
  if (handoffMode === "beta") {
11923
12040
  await writeSpecMarkdownContext(changeDir, change, contextHash, contextMd);
11924
12041
  await writeSpecJsonContext(changeDir, change, contextHash, contextJson);
@@ -11926,7 +12043,7 @@ var classicHandoffCommand = async (args) => {
11926
12043
  await writeMarkdownContext(changeDir, change, handoffMode, contextHash, contextMd);
11927
12044
  await writeJsonContext(changeDir, change, handoffMode, contextHash, contextJson);
11928
12045
  }
11929
- const context = await fs19.readFile(contextMd, "utf8");
12046
+ const context = await fs20.readFile(contextMd, "utf8");
11930
12047
  await writeContext(changeDir, pendingRun.contextRef, context);
11931
12048
  const artifacts = {
11932
12049
  ...await readArtifacts(changeDir, pendingRun.artifactsRef),
@@ -11983,7 +12100,7 @@ var classicHandoffCommand = async (args) => {
11983
12100
  };
11984
12101
 
11985
12102
  // domains/comet-classic/classic-hook-guard.ts
11986
- 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";
11987
12104
  import path21 from "path";
11988
12105
  function result(exitCode, message) {
11989
12106
  return { exitCode, stderr: message + "\n" };
@@ -12028,7 +12145,7 @@ async function physicalPathForPossiblyMissingTarget(target) {
12028
12145
  let cursor = resolved;
12029
12146
  while (cursor && cursor !== root) {
12030
12147
  try {
12031
- const physicalBase = await fs20.realpath(cursor);
12148
+ const physicalBase = await fs21.realpath(cursor);
12032
12149
  return path21.join(physicalBase, ...missingSegments.reverse());
12033
12150
  } catch (error) {
12034
12151
  const code = error.code;
@@ -12038,7 +12155,7 @@ async function physicalPathForPossiblyMissingTarget(target) {
12038
12155
  }
12039
12156
  }
12040
12157
  try {
12041
- const physicalRoot = await fs20.realpath(root);
12158
+ const physicalRoot = await fs21.realpath(root);
12042
12159
  return path21.join(physicalRoot, ...missingSegments.reverse());
12043
12160
  } catch {
12044
12161
  return null;
@@ -12051,7 +12168,7 @@ async function projectRelative(target, projectRoot2) {
12051
12168
  if (rootRelative !== null) return rootRelative;
12052
12169
  try {
12053
12170
  const physicalCandidate = await physicalPathForPossiblyMissingTarget(rawCandidate);
12054
- const physicalRoot = await fs20.realpath(projectRoot2);
12171
+ const physicalRoot = await fs21.realpath(projectRoot2);
12055
12172
  if (physicalCandidate) {
12056
12173
  const physicalRootRelative = relativeToProjectRoot(physicalCandidate, physicalRoot);
12057
12174
  if (physicalRootRelative !== null) return physicalRootRelative;
@@ -12091,7 +12208,7 @@ async function activeChanges(projectRoot2) {
12091
12208
  const changesDir = path21.join(projectRoot2, "openspec", "changes");
12092
12209
  const governingChanges = [];
12093
12210
  if (!existsSync2(changesDir)) return governingChanges;
12094
- for (const entry2 of (await fs20.readdir(changesDir, { withFileTypes: true })).sort(
12211
+ for (const entry2 of (await fs21.readdir(changesDir, { withFileTypes: true })).sort(
12095
12212
  (left, right) => left.name.localeCompare(right.name)
12096
12213
  )) {
12097
12214
  if (!entry2.isDirectory() || entry2.name === "archive") continue;
@@ -12876,7 +12993,7 @@ var classicIntentCommand = async (args, _options) => {
12876
12993
 
12877
12994
  // domains/comet-classic/classic-resume-probe.ts
12878
12995
  import path22 from "path";
12879
- import { promises as fs21 } from "fs";
12996
+ import { promises as fs22 } from "fs";
12880
12997
  import { spawn } from "child_process";
12881
12998
  var COMET_RESUME_PROBE_SCHEMA_VERSION = "comet.resume_probe.v1";
12882
12999
  function isRecord3(value) {
@@ -12919,7 +13036,7 @@ function result3(action, change, confidence, reason, evidence = []) {
12919
13036
  }
12920
13037
  async function readIfExists(filePath) {
12921
13038
  if (!await fileExists3(filePath)) return "";
12922
- return fs21.readFile(filePath, "utf8");
13039
+ return fs22.readFile(filePath, "utf8");
12923
13040
  }
12924
13041
  async function changeSearchText(changeDir, classic) {
12925
13042
  const files = ["proposal.md", "design.md", "tasks.md"];
@@ -12999,7 +13116,7 @@ async function discoverActiveChanges(projectRoot2) {
12999
13116
  for (const entry2 of entries) {
13000
13117
  if (entry2 === "archive") continue;
13001
13118
  const changeDir = path22.join(changesDir, entry2);
13002
- const stat = await fs21.stat(changeDir).catch(() => null);
13119
+ const stat = await fs22.stat(changeDir).catch(() => null);
13003
13120
  if (!stat?.isDirectory()) continue;
13004
13121
  const hasCometState = await fileExists3(path22.join(changeDir, ".comet.yaml"));
13005
13122
  if (!hasCometState) {
@@ -13304,7 +13421,7 @@ var classicResumeProbeCommand = async (args) => {
13304
13421
  var import_yaml8 = __toESM(require_dist(), 1);
13305
13422
  import { spawnSync as spawnSync3 } from "child_process";
13306
13423
  import { randomUUID as randomUUID8 } from "crypto";
13307
- import { existsSync as existsSync3, promises as fs22 } from "fs";
13424
+ import { existsSync as existsSync3, promises as fs23 } from "fs";
13308
13425
  import path23 from "path";
13309
13426
  init_state();
13310
13427
  var GREEN5 = "\x1B[32m";
@@ -13419,7 +13536,7 @@ function validateRelativePath(value, field2) {
13419
13536
  }
13420
13537
  async function exists6(file) {
13421
13538
  try {
13422
- await fs22.access(file);
13539
+ await fs23.access(file);
13423
13540
  return true;
13424
13541
  } catch (error) {
13425
13542
  if (error.code === "ENOENT") return false;
@@ -13428,7 +13545,7 @@ async function exists6(file) {
13428
13545
  }
13429
13546
  async function nonempty3(file) {
13430
13547
  try {
13431
- return (await fs22.stat(file)).size > 0;
13548
+ return (await fs23.stat(file)).size > 0;
13432
13549
  } catch (error) {
13433
13550
  if (error.code === "ENOENT") return false;
13434
13551
  throw error;
@@ -13440,7 +13557,7 @@ async function changeDirectory2(name) {
13440
13557
  async function readDocument2(file) {
13441
13558
  let source;
13442
13559
  try {
13443
- source = await fs22.readFile(file, "utf8");
13560
+ source = await fs23.readFile(file, "utf8");
13444
13561
  } catch (error) {
13445
13562
  if (error.code === "ENOENT") {
13446
13563
  fail2(
@@ -13454,13 +13571,13 @@ async function readDocument2(file) {
13454
13571
  return document;
13455
13572
  }
13456
13573
  async function atomicWrite2(file, content) {
13457
- await fs22.mkdir(path23.dirname(file), { recursive: true });
13574
+ await fs23.mkdir(path23.dirname(file), { recursive: true });
13458
13575
  const temporary = `${file}.${randomUUID8()}.tmp`;
13459
13576
  try {
13460
- await fs22.writeFile(temporary, content, "utf8");
13461
- await fs22.rename(temporary, file);
13577
+ await fs23.writeFile(temporary, content, "utf8");
13578
+ await fs23.rename(temporary, file);
13462
13579
  } catch (error) {
13463
- await fs22.rm(temporary, { force: true });
13580
+ await fs23.rm(temporary, { force: true });
13464
13581
  throw error;
13465
13582
  }
13466
13583
  }
@@ -13734,7 +13851,7 @@ async function init(output, name, workflow) {
13734
13851
  validateEnum(workflow, PROFILES);
13735
13852
  const { file, label, directory } = await stateFile(name);
13736
13853
  if (await exists6(file)) fail2(`ERROR: .comet.yaml already exists at ${label}/.comet.yaml`);
13737
- await fs22.mkdir(directory, { recursive: true });
13854
+ await fs23.mkdir(directory, { recursive: true });
13738
13855
  const preset = workflow !== "full";
13739
13856
  const reviewMode = preset ? "off" : await reviewModeDefault();
13740
13857
  const document = new import_yaml8.Document({
@@ -13964,7 +14081,7 @@ async function taskCheckoff(output, taskFile, taskText) {
13964
14081
  if (!taskText) fail2("ERROR: Task text cannot be empty");
13965
14082
  const file = path23.resolve(taskFile);
13966
14083
  if (!await exists6(file)) fail2(`ERROR: Task file not found: ${taskFile}`);
13967
- const lines = (await fs22.readFile(file, "utf8")).split(/\r?\n/u);
14084
+ const lines = (await fs23.readFile(file, "utf8")).split(/\r?\n/u);
13968
14085
  const matches = lines.filter(
13969
14086
  (line) => [`- [ ] ${taskText}`, `- [x] ${taskText}`, `- [X] ${taskText}`].includes(line)
13970
14087
  );
@@ -14140,14 +14257,14 @@ async function recoverBuild(output, name, directory, workflow) {
14140
14257
  );
14141
14258
  return;
14142
14259
  }
14143
- const lines = (await fs22.readFile(tasks, "utf8")).split(/\r?\n/u);
14260
+ const lines = (await fs23.readFile(tasks, "utf8")).split(/\r?\n/u);
14144
14261
  const total = lines.filter((line) => /^\s*- \[[ xX]\] /u.test(line)).length;
14145
14262
  const done = lines.filter((line) => /^\s*- \[[xX]\] /u.test(line)).length;
14146
14263
  const pending = total - done;
14147
14264
  let planTotal = 0;
14148
14265
  let planDone = 0;
14149
14266
  if (plan && plan !== "null" && await exists6(path23.resolve(plan))) {
14150
- 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);
14151
14268
  planTotal = planLines.filter((line) => /^\s*- \[[ xX]\] /u.test(line)).length;
14152
14269
  planDone = planLines.filter((line) => /^\s*- \[[xX]\] /u.test(line)).length;
14153
14270
  }
@@ -14279,18 +14396,18 @@ async function scale(output, name) {
14279
14396
  const { file, directory, label } = await stateFile(name);
14280
14397
  if (!await exists6(file)) fail2(`ERROR: .comet.yaml not found at ${label}/.comet.yaml`);
14281
14398
  const tasksFile = path23.join(directory, "tasks.md");
14282
- 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;
14283
14400
  const specs = path23.join(directory, "specs");
14284
14401
  let deltaSpecs = 0;
14285
14402
  if (await exists6(specs)) {
14286
- for (const entry2 of await fs22.readdir(specs)) {
14403
+ for (const entry2 of await fs23.readdir(specs)) {
14287
14404
  if (await exists6(path23.join(specs, entry2, "spec.md"))) deltaSpecs += 1;
14288
14405
  }
14289
14406
  }
14290
14407
  const plan = await readField3(name, "plan");
14291
14408
  let baseRef = "";
14292
14409
  if (plan && plan !== "null" && await exists6(path23.resolve(plan))) {
14293
- 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);
14294
14411
  baseRef = match?.[1].trim() ?? "";
14295
14412
  }
14296
14413
  if (!baseRef) baseRef = await readField3(name, "base_ref");