oh-my-opencode 4.16.0 → 4.16.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/.agents/skills/codex-qa/SKILL.md +9 -7
  2. package/.agents/skills/codex-qa/references/logging-debug.md +6 -4
  3. package/.agents/skills/opencode-qa/SKILL.md +9 -9
  4. package/.agents/skills/opencode-qa/references/tui-tmux.md +10 -8
  5. package/dist/cli/codex-ulw-loop.d.ts +8 -0
  6. package/dist/cli/get-local-version/types.d.ts +1 -1
  7. package/dist/cli/index.js +260 -207
  8. package/dist/cli-node/index.js +260 -207
  9. package/dist/features/background-agent/parent-wake-dedupe.d.ts +1 -0
  10. package/dist/features/background-agent/parent-wake-flush-runner.d.ts +2 -0
  11. package/dist/features/background-agent/parent-wake-prompt-dispatch.d.ts +1 -0
  12. package/dist/features/builtin-commands/templates/refactor-sections/intro-and-analysis.d.ts +1 -1
  13. package/dist/features/builtin-commands/templates/remove-ai-slops.d.ts +1 -1
  14. package/dist/features/builtin-commands/templates/start-work.d.ts +1 -1
  15. package/dist/features/monitor/process.d.ts +17 -1
  16. package/dist/index.js +1098 -1020
  17. package/dist/skills/frontend/SKILL.md +9 -9
  18. package/dist/skills/frontend/references/design/README.md +7 -3
  19. package/dist/skills/frontend/references/design/_INDEX.md +1 -1
  20. package/dist/skills/frontend/references/design/design-system-architecture.md +24 -2
  21. package/dist/skills/frontend/references/designpowers/README.md +2 -2
  22. package/dist/skills/frontend/references/designpowers/lane-b-execution.md +1 -1
  23. package/dist/skills/review-work/SKILL.md +3 -1
  24. package/dist/skills/start-work/SKILL.md +4 -2
  25. package/dist/skills/ulw-research/SKILL.md +3 -1
  26. package/dist/skills/visual-qa/SKILL.md +13 -17
  27. package/docs/reference/web-terminal-visual-qa.md +39 -45
  28. package/package.json +31 -25
  29. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  30. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  31. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  32. package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
  33. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  34. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  35. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  36. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  37. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  38. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  39. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  40. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  41. package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus.md +1 -1
  42. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  43. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  44. package/packages/omo-codex/plugin/components/start-work-continuation/directive.md +3 -3
  45. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
  46. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
  47. package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +1 -1
  48. package/packages/omo-codex/plugin/components/teammode/AGENTS.md +1 -1
  49. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  50. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  51. package/packages/omo-codex/plugin/components/teammode/skills/teammode/SKILL.md +3 -3
  52. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  53. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  54. package/packages/omo-codex/plugin/components/ultrawork/directive.md +19 -10
  55. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  56. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  57. package/packages/omo-codex/plugin/components/ultrawork/skills/ultrawork/SKILL.md +19 -10
  58. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +13 -0
  59. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +2 -0
  60. package/packages/omo-codex/plugin/components/ultrawork/test/codex-hook.test.ts +22 -1
  61. package/packages/omo-codex/plugin/components/ulw-loop/CHANGELOG.md +4 -0
  62. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +19 -10
  63. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-commands.js +15 -2
  64. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-steering.js +2 -1
  65. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +89 -27
  66. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.d.ts +6 -0
  67. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.js +55 -9
  68. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-snapshot.d.ts +15 -0
  69. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-snapshot.js +33 -0
  70. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-types.d.ts +10 -3
  71. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering.js +15 -11
  72. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +2 -2
  73. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  74. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +3 -1
  75. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +7 -7
  76. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-commands.ts +17 -2
  77. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-steering.ts +2 -1
  78. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-io.ts +59 -11
  79. package/packages/omo-codex/plugin/components/ulw-loop/src/steering-snapshot.ts +38 -0
  80. package/packages/omo-codex/plugin/components/ulw-loop/src/steering-types.ts +11 -3
  81. package/packages/omo-codex/plugin/components/ulw-loop/src/steering.ts +15 -7
  82. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-create-goals.test.ts +16 -0
  83. package/packages/omo-codex/plugin/components/ulw-loop/test/plan-io.test.ts +260 -2
  84. package/packages/omo-codex/plugin/components/ulw-loop/test/skill-contract.test.ts +1 -1
  85. package/packages/omo-codex/plugin/components/ulw-loop/test/steering-snapshot.test.ts +124 -0
  86. package/packages/omo-codex/plugin/components/ulw-loop/test/steering.test.ts +101 -2
  87. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  88. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  89. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  90. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
  91. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  92. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  93. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  94. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  95. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  96. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  97. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  98. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  99. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
  100. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  101. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  102. package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
  103. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
  104. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  105. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  106. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  107. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  108. package/packages/omo-codex/plugin/package-lock.json +13 -13
  109. package/packages/omo-codex/plugin/package.json +1 -1
  110. package/packages/omo-codex/plugin/scripts/auto-update.mjs +64 -17
  111. package/packages/omo-codex/plugin/scripts/hook-status-message.mjs +10 -6
  112. package/packages/omo-codex/plugin/scripts/migrate-codex-config/multi-agent-v2-guard.mjs +186 -20
  113. package/packages/omo-codex/plugin/scripts/migrate-codex-config/subagent-limit-guard.mjs +51 -3
  114. package/packages/omo-codex/plugin/scripts/migrate-codex-config.mjs +33 -5
  115. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +1 -1
  116. package/packages/omo-codex/plugin/skills/frontend/SKILL.md +9 -9
  117. package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +7 -3
  118. package/packages/omo-codex/plugin/skills/frontend/references/design/_INDEX.md +1 -1
  119. package/packages/omo-codex/plugin/skills/frontend/references/design/design-system-architecture.md +24 -2
  120. package/packages/omo-codex/plugin/skills/frontend/references/designpowers/README.md +2 -2
  121. package/packages/omo-codex/plugin/skills/frontend/references/designpowers/lane-b-execution.md +1 -1
  122. package/packages/omo-codex/plugin/skills/init-deep/SKILL.md +1 -1
  123. package/packages/omo-codex/plugin/skills/refactor/SKILL.md +1 -1
  124. package/packages/omo-codex/plugin/skills/remove-ai-slops/SKILL.md +1 -1
  125. package/packages/omo-codex/plugin/skills/review-work/SKILL.md +1 -1
  126. package/packages/omo-codex/plugin/skills/start-work/SKILL.md +3 -3
  127. package/packages/omo-codex/plugin/skills/teammode/SKILL.md +3 -3
  128. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +19 -10
  129. package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +3 -1
  130. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +7 -7
  131. package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +13 -0
  132. package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +2 -0
  133. package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +1 -1
  134. package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +14 -18
  135. package/packages/omo-codex/plugin/test/aggregate-hooks.test.mjs +4 -4
  136. package/packages/omo-codex/plugin/test/aggregate-plugin-fixture.mjs +1 -1
  137. package/packages/omo-codex/plugin/test/auto-update.test.mjs +35 -1
  138. package/packages/omo-codex/plugin/test/bootstrap-hooks.test.mjs +1 -1
  139. package/packages/omo-codex/plugin/test/hook-status-message.test.mjs +22 -9
  140. package/packages/omo-codex/plugin/test/migrate-codex-config.test.mjs +275 -19
  141. package/packages/omo-codex/plugin/test/subagent-limit-migration.test.mjs +33 -0
  142. package/packages/omo-codex/plugin/test/sync-hook-status-messages.test.mjs +6 -6
  143. package/packages/omo-codex/plugin/test/sync-skills-orchestration.test.mjs +4 -2
  144. package/packages/omo-codex/plugin/test/ulw-plan-skill-contract.test.mjs +52 -0
  145. package/packages/omo-codex/scripts/install-dist/install-local.mjs +65 -39
  146. package/packages/omo-codex/scripts/install-lazycodex-version-stamp.test.mjs +2 -2
  147. package/packages/shared-skills/skills/frontend/SKILL.md +9 -9
  148. package/packages/shared-skills/skills/frontend/references/design/README.md +7 -3
  149. package/packages/shared-skills/skills/frontend/references/design/_INDEX.md +1 -1
  150. package/packages/shared-skills/skills/frontend/references/design/design-system-architecture.md +24 -2
  151. package/packages/shared-skills/skills/frontend/references/designpowers/README.md +2 -2
  152. package/packages/shared-skills/skills/frontend/references/designpowers/lane-b-execution.md +1 -1
  153. package/packages/shared-skills/skills/review-work/SKILL.md +3 -1
  154. package/packages/shared-skills/skills/start-work/SKILL.md +4 -2
  155. package/packages/shared-skills/skills/ulw-research/SKILL.md +3 -1
  156. package/packages/shared-skills/skills/visual-qa/SKILL.md +13 -17
  157. package/script/qa/strip-ansi.mjs +10 -0
  158. package/script/qa/web-terminal-visual-qa.mjs +112 -195
  159. package/script/qa/xterm-live-terminal.mjs +180 -0
  160. package/script/qa/web-terminal-renderer.mjs +0 -218
package/dist/index.js CHANGED
@@ -7945,7 +7945,10 @@ function finishPromptReservation(sessionID, reservation, dispatchAttempted, post
7945
7945
  function deletePromptReservation(sessionID) {
7946
7946
  promptAsyncReservations.delete(sessionID);
7947
7947
  }
7948
- function reservationSourceMatches(reservationSource, expectedSource, expectedPrefix) {
7948
+ function isTransientRetryReservationOwner(reservationSource) {
7949
+ return reservationSource === TRANSIENT_RETRY_RESERVATION_OWNER || reservationSource.startsWith(`${TRANSIENT_RETRY_RESERVATION_OWNER}:`);
7950
+ }
7951
+ function reservationSourceMatches(reservationSource, expectedSource, expectedPrefix, supersedeTransientRetryOwners) {
7949
7952
  if (typeof expectedSource === "string") {
7950
7953
  if (reservationSource === expectedSource) {
7951
7954
  return true;
@@ -7953,13 +7956,16 @@ function reservationSourceMatches(reservationSource, expectedSource, expectedPre
7953
7956
  } else if (expectedSource.includes(reservationSource)) {
7954
7957
  return true;
7955
7958
  }
7959
+ if (supersedeTransientRetryOwners && isTransientRetryReservationOwner(reservationSource)) {
7960
+ return true;
7961
+ }
7956
7962
  if (expectedPrefix === undefined) {
7957
7963
  return false;
7958
7964
  }
7959
7965
  const prefixes = typeof expectedPrefix === "string" ? [expectedPrefix] : expectedPrefix;
7960
7966
  return prefixes.filter((prefix) => prefix.length > 0 && prefix.endsWith(":")).some((prefix) => reservationSource.startsWith(prefix));
7961
7967
  }
7962
- var promptAsyncReservations, expiredReservationHandler;
7968
+ var promptAsyncReservations, expiredReservationHandler, TRANSIENT_RETRY_RESERVATION_OWNER = "model-suggestion-retry";
7963
7969
  var init_reservations = __esm(() => {
7964
7970
  promptAsyncReservations = new Map;
7965
7971
  });
@@ -8558,7 +8564,7 @@ function releasePromptAsyncReservation(sessionID, source, options) {
8558
8564
  return false;
8559
8565
  }
8560
8566
  const expectedSource = options?.reservedBy ?? source;
8561
- if (!reservationSourceMatches(existing.source, expectedSource, options?.reservedByPrefix)) {
8567
+ if (!reservationSourceMatches(existing.source, expectedSource, options?.reservedByPrefix, options?.supersedeTransientRetryOwners)) {
8562
8568
  log("[prompt-async-gate] promptAsync reservation release skipped for different source", {
8563
8569
  sessionID,
8564
8570
  source,
@@ -13173,10 +13179,10 @@ var init_tolerant_fsync = __esm(() => {
13173
13179
  // packages/team-core/src/team-state-store/locks.ts
13174
13180
  import { randomUUID as randomUUID5 } from "crypto";
13175
13181
  import { access as access3, open, readFile as readFile4, rename as rename3, rm as rm3, unlink as unlink2 } from "fs/promises";
13176
- import { dirname as dirname23 } from "path";
13182
+ import { dirname as dirname24 } from "path";
13177
13183
  function delay4(ms) {
13178
- return new Promise((resolve19) => {
13179
- setTimeout(resolve19, ms);
13184
+ return new Promise((resolve20) => {
13185
+ setTimeout(resolve20, ms);
13180
13186
  });
13181
13187
  }
13182
13188
  function buildOwnerContent(ownerTag) {
@@ -13228,7 +13234,7 @@ async function assertRetryableLockOpenError(lockPath, error, deps) {
13228
13234
  if (code === "EPERM") {
13229
13235
  if (await pathMayExist(lockPath, deps))
13230
13236
  return;
13231
- if ((deps?.platform ?? process.platform) === "win32" && await pathMayExist(dirname23(lockPath), deps))
13237
+ if ((deps?.platform ?? process.platform) === "win32" && await pathMayExist(dirname24(lockPath), deps))
13232
13238
  return;
13233
13239
  }
13234
13240
  throw error;
@@ -13560,18 +13566,18 @@ __export(exports_storage, {
13560
13566
  getStateFilePath: () => getStateFilePath,
13561
13567
  clearState: () => clearState
13562
13568
  });
13563
- import { existsSync as existsSync69, readFileSync as readFileSync45, writeFileSync as writeFileSync17, unlinkSync as unlinkSync12, mkdirSync as mkdirSync17 } from "fs";
13564
- import { dirname as dirname24, join as join83 } from "path";
13569
+ import { existsSync as existsSync74, readFileSync as readFileSync48, writeFileSync as writeFileSync18, unlinkSync as unlinkSync13, mkdirSync as mkdirSync18 } from "fs";
13570
+ import { dirname as dirname25, join as join86 } from "path";
13565
13571
  function getStateFilePath(directory, customPath) {
13566
- return customPath ? join83(directory, customPath) : join83(directory, DEFAULT_STATE_FILE);
13572
+ return customPath ? join86(directory, customPath) : join86(directory, DEFAULT_STATE_FILE);
13567
13573
  }
13568
13574
  function readState(directory, customPath) {
13569
13575
  const filePath = getStateFilePath(directory, customPath);
13570
- if (!existsSync69(filePath)) {
13576
+ if (!existsSync74(filePath)) {
13571
13577
  return null;
13572
13578
  }
13573
13579
  try {
13574
- const content = readFileSync45(filePath, "utf-8");
13580
+ const content = readFileSync48(filePath, "utf-8");
13575
13581
  const { data, body } = parseFrontmatter(content);
13576
13582
  const active = data.active;
13577
13583
  const iteration = data.iteration;
@@ -13617,9 +13623,9 @@ function readState(directory, customPath) {
13617
13623
  function writeState(directory, state3, customPath) {
13618
13624
  const filePath = getStateFilePath(directory, customPath);
13619
13625
  try {
13620
- const dir = dirname24(filePath);
13621
- if (!existsSync69(dir)) {
13622
- mkdirSync17(dir, { recursive: true });
13626
+ const dir = dirname25(filePath);
13627
+ if (!existsSync74(dir)) {
13628
+ mkdirSync18(dir, { recursive: true });
13623
13629
  }
13624
13630
  const sessionIdLine = state3.session_id ? `session_id: "${state3.session_id}"
13625
13631
  ` : "";
@@ -13651,7 +13657,7 @@ ${initialCompletionPromiseLine}${verificationAttemptLine}${verificationAttemptSt
13651
13657
  ${sessionIdLine}${ultraworkLine}${verificationPendingLine}${strategyLine}${messageCountAtStartLine}---
13652
13658
  ${state3.prompt}
13653
13659
  `;
13654
- writeFileSync17(filePath, content, "utf-8");
13660
+ writeFileSync18(filePath, content, "utf-8");
13655
13661
  return true;
13656
13662
  } catch (error) {
13657
13663
  if (!(error instanceof Error)) {
@@ -13663,8 +13669,8 @@ ${state3.prompt}
13663
13669
  function clearState(directory, customPath) {
13664
13670
  const filePath = getStateFilePath(directory, customPath);
13665
13671
  try {
13666
- if (existsSync69(filePath)) {
13667
- unlinkSync12(filePath);
13672
+ if (existsSync74(filePath)) {
13673
+ unlinkSync13(filePath);
13668
13674
  }
13669
13675
  return true;
13670
13676
  } catch (error) {
@@ -81981,6 +81987,7 @@ import { existsSync as existsSync40 } from "fs";
81981
81987
 
81982
81988
  // packages/omo-opencode/src/hooks/comment-checker/cli.ts
81983
81989
  init_bun_spawn_shim();
81990
+ init_bun_which_shim();
81984
81991
  import { join as join46 } from "path";
81985
81992
  import { existsSync as existsSync39 } from "fs";
81986
81993
  import * as fs8 from "fs";
@@ -82331,7 +82338,7 @@ function debugLog2(...args) {
82331
82338
  function getBinaryName2() {
82332
82339
  return process.platform === "win32" ? "comment-checker.exe" : "comment-checker";
82333
82340
  }
82334
- function resolveCommentCheckerPathFromPath(binaryName, which2 = Bun.which) {
82341
+ function resolveCommentCheckerPathFromPath(binaryName, which2 = bunWhich) {
82335
82342
  try {
82336
82343
  return which2(binaryName);
82337
82344
  } catch (error) {
@@ -87871,8 +87878,779 @@ async function executePreToolUseHooks(ctx, config, extendedConfig) {
87871
87878
  ...Object.keys(accumulatedCommonFields).length > 0 ? accumulatedCommonFields : {}
87872
87879
  };
87873
87880
  }
87881
+ // packages/boulder-state/src/constants.ts
87882
+ var BOULDER_DIR = ".omo";
87883
+ var BOULDER_FILE = "boulder.json";
87884
+ var BOULDER_STATE_PATH = `${BOULDER_DIR}/${BOULDER_FILE}`;
87885
+ var NOTEPAD_DIR = "notepads";
87886
+ var NOTEPAD_BASE_PATH = `${BOULDER_DIR}/${NOTEPAD_DIR}`;
87887
+ var PROMETHEUS_PLANS_DIR = ".omo/plans";
87888
+ // packages/boulder-state/src/top-level-task.ts
87889
+ import { existsSync as existsSync56, readFileSync as readFileSync33 } from "fs";
87890
+ var TODO_HEADING_PATTERN = /^##\s+TODOs\b/i;
87891
+ var FINAL_VERIFICATION_HEADING_PATTERN = /^##\s+Final Verification Wave\b/i;
87892
+ var SECOND_LEVEL_HEADING_PATTERN = /^##\s+/;
87893
+ var UNCHECKED_CHECKBOX_PATTERN = /^(\s*)[-*]\s*\[\s*\]\s*(.+)$/;
87894
+ var TODO_TASK_PATTERN = /^(\d+)\.\s+(.+)$/;
87895
+ var FINAL_WAVE_TASK_PATTERN = /^(F\d+)\.\s+(.+)$/i;
87896
+ function buildTaskRef(section, taskLabel) {
87897
+ const pattern = section === "todo" ? TODO_TASK_PATTERN : FINAL_WAVE_TASK_PATTERN;
87898
+ const match = taskLabel.match(pattern);
87899
+ if (!match) {
87900
+ return null;
87901
+ }
87902
+ const rawLabel = match[1];
87903
+ const title = match[2].trim();
87904
+ return {
87905
+ key: `${section}:${rawLabel.toLowerCase()}`,
87906
+ section,
87907
+ label: rawLabel,
87908
+ title
87909
+ };
87910
+ }
87911
+ function readCurrentTopLevelTask(planPath) {
87912
+ if (!existsSync56(planPath)) {
87913
+ return null;
87914
+ }
87915
+ try {
87916
+ const content = readFileSync33(planPath, "utf-8");
87917
+ const lines = content.split(/\r?\n/);
87918
+ let section = "other";
87919
+ for (const line of lines) {
87920
+ if (SECOND_LEVEL_HEADING_PATTERN.test(line)) {
87921
+ section = TODO_HEADING_PATTERN.test(line) ? "todo" : FINAL_VERIFICATION_HEADING_PATTERN.test(line) ? "final-wave" : "other";
87922
+ }
87923
+ const uncheckedTaskMatch = line.match(UNCHECKED_CHECKBOX_PATTERN);
87924
+ if (!uncheckedTaskMatch || uncheckedTaskMatch[1].length > 0) {
87925
+ continue;
87926
+ }
87927
+ if (section !== "todo" && section !== "final-wave") {
87928
+ continue;
87929
+ }
87930
+ const taskRef = buildTaskRef(section, uncheckedTaskMatch[2].trim());
87931
+ if (taskRef) {
87932
+ return taskRef;
87933
+ }
87934
+ }
87935
+ return null;
87936
+ } catch {
87937
+ return null;
87938
+ }
87939
+ }
87940
+ // packages/boulder-state/src/storage/path.ts
87941
+ import { existsSync as existsSync57 } from "fs";
87942
+ import { isAbsolute as isAbsolute12, join as join67, relative as relative11, resolve as resolve17 } from "path";
87943
+ function getBoulderFilePath(directory) {
87944
+ return join67(directory, BOULDER_DIR, BOULDER_FILE);
87945
+ }
87946
+ function resolveTrackedPath(baseDirectory, trackedPath) {
87947
+ return isAbsolute12(trackedPath) ? resolve17(trackedPath) : resolve17(baseDirectory, trackedPath);
87948
+ }
87949
+ function resolveBoulderPlanPath(directory, state3) {
87950
+ const absolutePlanPath = resolveTrackedPath(directory, state3.active_plan);
87951
+ const worktreePath = state3.worktree_path?.trim();
87952
+ if (!worktreePath) {
87953
+ return absolutePlanPath;
87954
+ }
87955
+ const absoluteDirectory = resolve17(directory);
87956
+ const relativePlanPath = relative11(absoluteDirectory, absolutePlanPath);
87957
+ if (relativePlanPath.length === 0 || relativePlanPath.startsWith("..") || isAbsolute12(relativePlanPath)) {
87958
+ return absolutePlanPath;
87959
+ }
87960
+ const absoluteWorktreePath = resolveTrackedPath(directory, worktreePath);
87961
+ const worktreePlanPath = resolve17(absoluteWorktreePath, relativePlanPath);
87962
+ return existsSync57(worktreePlanPath) ? worktreePlanPath : absolutePlanPath;
87963
+ }
87964
+ function resolveBoulderPlanPathForWork(directory, work) {
87965
+ return resolveBoulderPlanPath(directory, work);
87966
+ }
87967
+ // packages/boulder-state/src/storage/plan-progress.ts
87968
+ import { existsSync as existsSync58, readFileSync as readFileSync34, readdirSync as readdirSync18, statSync as statSync8 } from "fs";
87969
+ import { basename as basename12, join as join68 } from "path";
87970
+ var TODO_HEADING_PATTERN2 = /^##\s+TODOs\b/i;
87971
+ var FINAL_VERIFICATION_HEADING_PATTERN2 = /^##\s+Final Verification Wave\b/i;
87972
+ var SECOND_LEVEL_HEADING_PATTERN2 = /^##\s+/;
87973
+ var UNCHECKED_CHECKBOX_PATTERN2 = /^(\s*)[-*]\s*\[\s*\]\s*(.+)$/;
87974
+ var CHECKED_CHECKBOX_PATTERN = /^(\s*)[-*]\s*\[[xX]\]\s*(.+)$/;
87975
+ var TODO_TASK_PATTERN2 = /^\d+\.\s+/;
87976
+ var FINAL_WAVE_TASK_PATTERN2 = /^F\d+\.\s+/i;
87977
+ var LEGACY_PROMETHEUS_PLANS_DIR = ".sisyphus/plans";
87978
+ var PROMETHEUS_PLAN_DIRS = [PROMETHEUS_PLANS_DIR, LEGACY_PROMETHEUS_PLANS_DIR];
87979
+ function findPrometheusPlans(directory) {
87980
+ try {
87981
+ return PROMETHEUS_PLAN_DIRS.flatMap((planDir) => {
87982
+ const plansDir = join68(directory, planDir);
87983
+ if (!existsSync58(plansDir)) {
87984
+ return [];
87985
+ }
87986
+ return readdirSync18(plansDir).filter((file2) => file2.endsWith(".md")).map((file2) => join68(plansDir, file2));
87987
+ }).sort((left, right) => statSync8(right).mtimeMs - statSync8(left).mtimeMs);
87988
+ } catch {
87989
+ return [];
87990
+ }
87991
+ }
87992
+ function getPlanName(planPath) {
87993
+ return basename12(planPath, ".md");
87994
+ }
87995
+ function getPlanProgress(planPath) {
87996
+ if (!existsSync58(planPath)) {
87997
+ return { total: 0, completed: 0, isComplete: false };
87998
+ }
87999
+ try {
88000
+ const content = readFileSync34(planPath, "utf-8");
88001
+ const lines = content.split(/\r?\n/);
88002
+ const hasStructuredSections = lines.some((line) => TODO_HEADING_PATTERN2.test(line) || FINAL_VERIFICATION_HEADING_PATTERN2.test(line));
88003
+ if (hasStructuredSections) {
88004
+ return getStructuredPlanProgress(lines);
88005
+ }
88006
+ return getSimplePlanProgress(content);
88007
+ } catch {
88008
+ return { total: 0, completed: 0, isComplete: false };
88009
+ }
88010
+ }
88011
+ function getStructuredPlanProgress(lines) {
88012
+ let section = "other";
88013
+ let total = 0;
88014
+ let completed = 0;
88015
+ for (const line of lines) {
88016
+ if (SECOND_LEVEL_HEADING_PATTERN2.test(line)) {
88017
+ section = TODO_HEADING_PATTERN2.test(line) ? "todo" : FINAL_VERIFICATION_HEADING_PATTERN2.test(line) ? "final-wave" : "other";
88018
+ continue;
88019
+ }
88020
+ if (section !== "todo" && section !== "final-wave") {
88021
+ continue;
88022
+ }
88023
+ const checkedMatch = line.match(CHECKED_CHECKBOX_PATTERN);
88024
+ const uncheckedMatch = checkedMatch ? null : line.match(UNCHECKED_CHECKBOX_PATTERN2);
88025
+ const match = checkedMatch ?? uncheckedMatch;
88026
+ if (!match || match[1].length > 0) {
88027
+ continue;
88028
+ }
88029
+ const taskBody = match[2].trim();
88030
+ const labelPattern = section === "todo" ? TODO_TASK_PATTERN2 : FINAL_WAVE_TASK_PATTERN2;
88031
+ if (!labelPattern.test(taskBody)) {
88032
+ continue;
88033
+ }
88034
+ total += 1;
88035
+ if (checkedMatch) {
88036
+ completed += 1;
88037
+ }
88038
+ }
88039
+ return { total, completed, isComplete: total > 0 && completed === total };
88040
+ }
88041
+ function getSimplePlanProgress(content) {
88042
+ const uncheckedMatches = content.match(/^[-*]\s*\[\s*\]/gm) ?? [];
88043
+ const checkedMatches = content.match(/^[-*]\s*\[[xX]\]/gm) ?? [];
88044
+ const total = uncheckedMatches.length + checkedMatches.length;
88045
+ const completed = checkedMatches.length;
88046
+ return { total, completed, isComplete: total > 0 && completed === total };
88047
+ }
88048
+ // packages/boulder-state/src/storage/shared.ts
88049
+ var RESERVED_KEYS = new Set(["__proto__", "prototype", "constructor"]);
88050
+ var SESSION_ID_PREFIX_PATTERN = /^(codex|opencode):/;
88051
+ function normalizeSessionId(sessionId, platform2 = "opencode") {
88052
+ if (SESSION_ID_PREFIX_PATTERN.test(sessionId)) {
88053
+ return sessionId;
88054
+ }
88055
+ return `${platform2}:${sessionId}`;
88056
+ }
88057
+ function nowIsoString() {
88058
+ return new Date().toISOString();
88059
+ }
88060
+ function parseIsoToMs(value) {
88061
+ if (!value) {
88062
+ return null;
88063
+ }
88064
+ const parsed = Date.parse(value);
88065
+ return Number.isNaN(parsed) ? null : parsed;
88066
+ }
88067
+ function getElapsedMs(startedAt, endedAt) {
88068
+ const startedMs = parseIsoToMs(startedAt);
88069
+ const endedMs = parseIsoToMs(endedAt);
88070
+ if (startedMs === null || endedMs === null) {
88071
+ return;
88072
+ }
88073
+ return endedMs - startedMs;
88074
+ }
88075
+ function isValidWorkStatus(status) {
88076
+ return status === "active" || status === "completed" || status === "paused" || status === "abandoned";
88077
+ }
88078
+ function buildWorkFromMirror(state3) {
88079
+ const planName = state3.plan_name ?? state3.active_plan;
88080
+ const workId = `${planName}-legacy`;
88081
+ return {
88082
+ work_id: workId,
88083
+ active_plan: state3.active_plan,
88084
+ plan_name: planName,
88085
+ status: state3.status,
88086
+ started_at: state3.started_at,
88087
+ ended_at: state3.ended_at,
88088
+ elapsed_ms: state3.elapsed_ms,
88089
+ updated_at: state3.updated_at,
88090
+ session_ids: Array.isArray(state3.session_ids) ? [...state3.session_ids] : [],
88091
+ session_origins: state3.session_origins,
88092
+ agent: state3.agent,
88093
+ worktree_path: state3.worktree_path,
88094
+ task_sessions: state3.task_sessions
88095
+ };
88096
+ }
88097
+ function projectWorkToMirror(state3, work) {
88098
+ state3.active_plan = work.active_plan;
88099
+ state3.plan_name = work.plan_name;
88100
+ state3.status = work.status;
88101
+ state3.started_at = work.started_at;
88102
+ state3.ended_at = work.ended_at;
88103
+ state3.elapsed_ms = work.elapsed_ms;
88104
+ state3.updated_at = work.updated_at;
88105
+ state3.session_ids = [...work.session_ids];
88106
+ state3.session_origins = work.session_origins ? { ...work.session_origins } : {};
88107
+ state3.agent = work.agent;
88108
+ state3.worktree_path = work.worktree_path;
88109
+ state3.task_sessions = work.task_sessions ? { ...work.task_sessions } : {};
88110
+ }
88111
+ function selectMirrorWork(state3) {
88112
+ const works = state3.works ? Object.values(state3.works) : [];
88113
+ if (works.length === 0) {
88114
+ return null;
88115
+ }
88116
+ if (state3.active_work_id) {
88117
+ const matched = works.find((work) => work.work_id === state3.active_work_id);
88118
+ if (matched) {
88119
+ return matched;
88120
+ }
88121
+ }
88122
+ const sorted = [...works].sort((left, right) => {
88123
+ const leftMs = parseIsoToMs(left.updated_at ?? left.started_at) ?? 0;
88124
+ const rightMs = parseIsoToMs(right.updated_at ?? right.started_at) ?? 0;
88125
+ return rightMs - leftMs;
88126
+ });
88127
+ return sorted[0] ?? null;
88128
+ }
88129
+ // packages/boulder-state/src/storage/read-state.ts
88130
+ import { existsSync as existsSync59, readFileSync as readFileSync35 } from "fs";
88131
+ function readBoulderState(directory) {
88132
+ const filePath = getBoulderFilePath(directory);
88133
+ if (!existsSync59(filePath)) {
88134
+ return null;
88135
+ }
88136
+ try {
88137
+ const content = readFileSync35(filePath, "utf-8");
88138
+ const parsed = JSON.parse(content);
88139
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
88140
+ return null;
88141
+ }
88142
+ normalizeState(parsed);
88143
+ const state3 = parsed;
88144
+ const mirrorWork = selectMirrorWork(state3);
88145
+ if (mirrorWork) {
88146
+ state3.active_work_id = mirrorWork.work_id;
88147
+ projectWorkToMirror(state3, mirrorWork);
88148
+ }
88149
+ return state3;
88150
+ } catch {
88151
+ return null;
88152
+ }
88153
+ }
88154
+ function normalizeState(state3) {
88155
+ normalizeSessionFields(state3);
88156
+ const sessionIds = Array.isArray(state3.session_ids) ? state3.session_ids : [];
88157
+ const sessionOrigins = state3.session_origins && typeof state3.session_origins === "object" && !Array.isArray(state3.session_origins) ? state3.session_origins : {};
88158
+ state3.session_origins = sessionOrigins;
88159
+ if (sessionIds.length === 1) {
88160
+ const soleSessionId = sessionIds[0];
88161
+ if (typeof soleSessionId === "string" && sessionOrigins[soleSessionId] !== "appended" && sessionOrigins[soleSessionId] !== "direct") {
88162
+ sessionOrigins[soleSessionId] = "direct";
88163
+ }
88164
+ }
88165
+ if (!state3.task_sessions || typeof state3.task_sessions !== "object" || Array.isArray(state3.task_sessions)) {
88166
+ state3.task_sessions = {};
88167
+ }
88168
+ normalizeWorkSessionFields(state3.works);
88169
+ }
88170
+ function normalizeSessionFields(target) {
88171
+ const sessionIds = Array.isArray(target.session_ids) ? target.session_ids.filter((sessionId) => typeof sessionId === "string").map((sessionId) => normalizeSessionId(sessionId)) : [];
88172
+ target.session_ids = sessionIds;
88173
+ const sessionOrigins = target.session_origins && typeof target.session_origins === "object" && !Array.isArray(target.session_origins) ? normalizeSessionOrigins(target.session_origins) : {};
88174
+ target.session_origins = sessionOrigins;
88175
+ }
88176
+ function normalizeSessionOrigins(sessionOrigins) {
88177
+ return Object.fromEntries(Object.entries(sessionOrigins).map(([sessionId, origin]) => [normalizeSessionId(sessionId), origin]));
88178
+ }
88179
+ function normalizeWorkSessionFields(works) {
88180
+ if (!works || typeof works !== "object" || Array.isArray(works)) {
88181
+ return;
88182
+ }
88183
+ for (const work of Object.values(works)) {
88184
+ if (work && typeof work === "object" && !Array.isArray(work)) {
88185
+ normalizeSessionFields(work);
88186
+ }
88187
+ }
88188
+ }
88189
+ function getBoulderWorks(state3) {
88190
+ if (state3.works && typeof state3.works === "object") {
88191
+ return Object.values(state3.works).filter((work) => work != null);
88192
+ }
88193
+ if (!state3.active_plan || !state3.plan_name || !state3.started_at) {
88194
+ return [];
88195
+ }
88196
+ return [buildWorkFromMirror(state3)];
88197
+ }
88198
+ function getActiveWorks(directory) {
88199
+ const state3 = readBoulderState(directory);
88200
+ if (!state3) {
88201
+ return [];
88202
+ }
88203
+ return getBoulderWorks(state3).filter((work) => work.status !== "completed" && work.status !== "abandoned");
88204
+ }
88205
+ function getWorkById(directory, workId) {
88206
+ const state3 = readBoulderState(directory);
88207
+ if (!state3) {
88208
+ return null;
88209
+ }
88210
+ return getBoulderWorks(state3).find((work) => work.work_id === workId) ?? null;
88211
+ }
88212
+ function getWorkByPlanName(directory, planName, options) {
88213
+ const state3 = readBoulderState(directory);
88214
+ if (!state3) {
88215
+ return null;
88216
+ }
88217
+ const worktreePath = options?.worktreePath;
88218
+ return getBoulderWorks(state3).find((work) => {
88219
+ if (work.plan_name !== planName) {
88220
+ return false;
88221
+ }
88222
+ return worktreePath ? work.worktree_path === worktreePath : true;
88223
+ }) ?? null;
88224
+ }
88225
+ function getWorkForSession(directory, sessionId) {
88226
+ const state3 = readBoulderState(directory);
88227
+ if (!state3) {
88228
+ return null;
88229
+ }
88230
+ const normalizedSessionId = normalizeSessionId(sessionId);
88231
+ let newestWork = null;
88232
+ let newestWorkMs = 0;
88233
+ for (const work of getBoulderWorks(state3)) {
88234
+ if (!work.session_ids.includes(normalizedSessionId)) {
88235
+ continue;
88236
+ }
88237
+ const workMs = parseIsoToMs(work.updated_at ?? work.started_at) ?? 0;
88238
+ if (!newestWork || workMs > newestWorkMs) {
88239
+ newestWork = work;
88240
+ newestWorkMs = workMs;
88241
+ }
88242
+ }
88243
+ if (newestWork) {
88244
+ return newestWork;
88245
+ }
88246
+ return state3.session_ids.includes(normalizedSessionId) ? buildWorkFromMirror(state3) : null;
88247
+ }
88248
+ function getWorkResumeOptions(directory) {
88249
+ const state3 = readBoulderState(directory);
88250
+ if (!state3) {
88251
+ return [];
88252
+ }
88253
+ return getBoulderWorks(state3).filter((work) => work.status !== "completed" && work.status !== "abandoned").map((work) => {
88254
+ const progress = getPlanProgress(resolveBoulderPlanPathForWork(directory, work));
88255
+ return {
88256
+ work_id: work.work_id,
88257
+ plan_name: work.plan_name,
88258
+ active_plan: work.active_plan,
88259
+ worktree_path: work.worktree_path,
88260
+ status: work.status && isValidWorkStatus(work.status) ? work.status : "active",
88261
+ started_at: work.started_at,
88262
+ updated_at: work.updated_at ?? work.started_at,
88263
+ ended_at: work.ended_at,
88264
+ elapsed_ms: work.elapsed_ms,
88265
+ session_count: work.session_ids.length,
88266
+ progress,
88267
+ is_current_mirror: state3.active_work_id === work.work_id
88268
+ };
88269
+ });
88270
+ }
88271
+ function getTaskSessionState(directory, taskKey) {
88272
+ const state3 = readBoulderState(directory);
88273
+ if (state3?.active_work_id) {
88274
+ const work = state3.works?.[state3.active_work_id];
88275
+ const taskSession = work?.task_sessions?.[taskKey];
88276
+ if (taskSession) {
88277
+ return taskSession;
88278
+ }
88279
+ }
88280
+ if (!state3?.task_sessions) {
88281
+ return null;
88282
+ }
88283
+ return state3.task_sessions[taskKey] ?? null;
88284
+ }
88285
+ // packages/boulder-state/src/storage/write-state.ts
88286
+ import { existsSync as existsSync60, mkdirSync as mkdirSync13, unlinkSync as unlinkSync7, writeFileSync as writeFileSync12 } from "fs";
88287
+ import { dirname as dirname18, join as join69 } from "path";
88288
+ function writeBoulderState(directory, state3) {
88289
+ const filePath = getBoulderFilePath(directory);
88290
+ try {
88291
+ const dir = dirname18(filePath);
88292
+ if (!existsSync60(dir)) {
88293
+ mkdirSync13(dir, { recursive: true });
88294
+ writeFileSync12(join69(dir, ".gitignore"), ["*", "!/rules/", "!/rules/**", ""].join(`
88295
+ `), "utf-8");
88296
+ }
88297
+ const stateToWrite = { ...state3 };
88298
+ if (stateToWrite.works && stateToWrite.active_work_id) {
88299
+ const activeWork = stateToWrite.works[stateToWrite.active_work_id];
88300
+ if (activeWork) {
88301
+ stateToWrite.works = {
88302
+ ...stateToWrite.works,
88303
+ [stateToWrite.active_work_id]: {
88304
+ ...activeWork,
88305
+ active_plan: stateToWrite.active_plan,
88306
+ plan_name: stateToWrite.plan_name,
88307
+ status: stateToWrite.status,
88308
+ started_at: stateToWrite.started_at,
88309
+ ended_at: stateToWrite.ended_at,
88310
+ elapsed_ms: stateToWrite.elapsed_ms,
88311
+ updated_at: stateToWrite.updated_at,
88312
+ session_ids: [...stateToWrite.session_ids],
88313
+ session_origins: stateToWrite.session_origins ? { ...stateToWrite.session_origins } : {},
88314
+ agent: stateToWrite.agent,
88315
+ worktree_path: stateToWrite.worktree_path,
88316
+ task_sessions: stateToWrite.task_sessions ? { ...stateToWrite.task_sessions } : {}
88317
+ }
88318
+ };
88319
+ }
88320
+ }
88321
+ writeFileSync12(filePath, JSON.stringify(stateToWrite, null, 2), "utf-8");
88322
+ return true;
88323
+ } catch {
88324
+ return false;
88325
+ }
88326
+ }
88327
+ function clearBoulderState(directory) {
88328
+ const filePath = getBoulderFilePath(directory);
88329
+ try {
88330
+ if (existsSync60(filePath)) {
88331
+ unlinkSync7(filePath);
88332
+ }
88333
+ return true;
88334
+ } catch {
88335
+ return false;
88336
+ }
88337
+ }
88338
+ function generateWorkId(planName) {
88339
+ const slug = planName.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
88340
+ const randomHex = Math.floor(Math.random() * 4294967295).toString(16).padStart(8, "0");
88341
+ return `${slug.length > 0 ? slug : "work"}-${randomHex}`;
88342
+ }
88343
+ function createBoulderState(planPath, sessionId, agent, worktreePath) {
88344
+ const startedAt = nowIsoString();
88345
+ const normalizedSessionId = normalizeSessionId(sessionId);
88346
+ const workId = generateWorkId(getPlanName(planPath));
88347
+ const work = {
88348
+ work_id: workId,
88349
+ active_plan: planPath,
88350
+ plan_name: getPlanName(planPath),
88351
+ status: "active",
88352
+ started_at: startedAt,
88353
+ updated_at: startedAt,
88354
+ session_ids: [normalizedSessionId],
88355
+ session_origins: { [normalizedSessionId]: "direct" },
88356
+ ...agent !== undefined ? { agent } : {},
88357
+ ...worktreePath !== undefined ? { worktree_path: worktreePath } : {},
88358
+ task_sessions: {}
88359
+ };
88360
+ return {
88361
+ schema_version: 2,
88362
+ active_work_id: workId,
88363
+ works: { [workId]: work },
88364
+ active_plan: planPath,
88365
+ started_at: startedAt,
88366
+ status: "active",
88367
+ updated_at: startedAt,
88368
+ session_ids: [normalizedSessionId],
88369
+ session_origins: { [normalizedSessionId]: "direct" },
88370
+ plan_name: getPlanName(planPath),
88371
+ task_sessions: {},
88372
+ ...agent !== undefined ? { agent } : {},
88373
+ ...worktreePath !== undefined ? { worktree_path: worktreePath } : {}
88374
+ };
88375
+ }
88376
+ function selectActiveWork(directory, workId) {
88377
+ const state3 = readBoulderState(directory);
88378
+ if (!state3) {
88379
+ return null;
88380
+ }
88381
+ const works = getBoulderWorks(state3);
88382
+ const nextWork = works.find((work) => work.work_id === workId);
88383
+ if (!nextWork) {
88384
+ return null;
88385
+ }
88386
+ const nextState = {
88387
+ ...state3,
88388
+ schema_version: 2,
88389
+ active_work_id: workId,
88390
+ works: state3.works ?? Object.fromEntries(works.map((work) => [work.work_id, work]))
88391
+ };
88392
+ projectWorkToMirror(nextState, nextWork);
88393
+ return writeBoulderState(directory, nextState) ? nextState : null;
88394
+ }
88395
+ function addBoulderWork(directory, input) {
88396
+ const state3 = readBoulderState(directory);
88397
+ if (!state3) {
88398
+ return null;
88399
+ }
88400
+ const workId = generateWorkId(getPlanName(input.planPath));
88401
+ const startedAt = input.startedAt ?? nowIsoString();
88402
+ const normalizedSessionId = normalizeSessionId(input.sessionId);
88403
+ const nextWork = {
88404
+ work_id: workId,
88405
+ active_plan: input.planPath,
88406
+ plan_name: getPlanName(input.planPath),
88407
+ status: "active",
88408
+ started_at: startedAt,
88409
+ updated_at: startedAt,
88410
+ session_ids: [normalizedSessionId],
88411
+ session_origins: { [normalizedSessionId]: "direct" },
88412
+ ...input.agent !== undefined ? { agent: input.agent } : {},
88413
+ ...input.worktreePath !== undefined ? { worktree_path: input.worktreePath } : {},
88414
+ task_sessions: {}
88415
+ };
88416
+ const nextState = {
88417
+ ...state3,
88418
+ schema_version: 2,
88419
+ works: { ...Object.fromEntries(getBoulderWorks(state3).map((work) => [work.work_id, work])), [workId]: nextWork },
88420
+ active_work_id: workId
88421
+ };
88422
+ projectWorkToMirror(nextState, nextWork);
88423
+ return writeBoulderState(directory, nextState) ? nextState : null;
88424
+ }
88425
+ function completeBoulder(directory, workId, endedAt) {
88426
+ const state3 = readBoulderState(directory);
88427
+ if (!state3) {
88428
+ return null;
88429
+ }
88430
+ const targetWorkId = workId ?? state3.active_work_id;
88431
+ if (!targetWorkId) {
88432
+ return null;
88433
+ }
88434
+ const work = state3.works?.[targetWorkId] ?? getBoulderWorks(state3).find((candidate) => candidate.work_id === targetWorkId);
88435
+ if (!work) {
88436
+ return null;
88437
+ }
88438
+ if (work.status === "completed" && work.ended_at !== undefined && work.elapsed_ms !== undefined) {
88439
+ return state3;
88440
+ }
88441
+ const endAt = endedAt ?? nowIsoString();
88442
+ work.ended_at = endAt;
88443
+ work.elapsed_ms = getElapsedMs(work.started_at, endAt);
88444
+ work.status = "completed";
88445
+ work.updated_at = nowIsoString();
88446
+ if (state3.active_work_id === targetWorkId) {
88447
+ projectWorkToMirror(state3, work);
88448
+ }
88449
+ return writeBoulderState(directory, state3) ? state3 : null;
88450
+ }
87874
88451
 
88452
+ // packages/boulder-state/src/storage/session.ts
88453
+ function appendSessionId(directory, sessionId, origin = "direct") {
88454
+ const normalizedSessionId = normalizeSessionId(sessionId);
88455
+ const activeWorkId = readBoulderState(directory)?.active_work_id;
88456
+ if (activeWorkId) {
88457
+ return appendSessionIdForWork(directory, activeWorkId, normalizedSessionId, origin);
88458
+ }
88459
+ const state3 = readBoulderState(directory);
88460
+ if (!state3) {
88461
+ return null;
88462
+ }
88463
+ if (!state3.session_origins || typeof state3.session_origins !== "object" || Array.isArray(state3.session_origins)) {
88464
+ state3.session_origins = {};
88465
+ }
88466
+ if (!state3.session_ids?.includes(normalizedSessionId)) {
88467
+ if (!Array.isArray(state3.session_ids)) {
88468
+ state3.session_ids = [];
88469
+ }
88470
+ const originalSessionIds = [...state3.session_ids];
88471
+ const originalSessionOrigins = { ...state3.session_origins };
88472
+ state3.session_ids.push(normalizedSessionId);
88473
+ state3.session_origins[normalizedSessionId] = origin;
88474
+ if (writeBoulderState(directory, state3)) {
88475
+ return state3;
88476
+ }
88477
+ state3.session_ids = originalSessionIds;
88478
+ state3.session_origins = originalSessionOrigins;
88479
+ return null;
88480
+ }
88481
+ if (!state3.session_origins[normalizedSessionId]) {
88482
+ state3.session_origins[normalizedSessionId] = origin;
88483
+ if (!writeBoulderState(directory, state3)) {
88484
+ return null;
88485
+ }
88486
+ }
88487
+ return state3;
88488
+ }
88489
+ function appendSessionIdForWork(directory, workId, sessionId, origin = "direct") {
88490
+ const normalizedSessionId = normalizeSessionId(sessionId);
88491
+ const state3 = readBoulderState(directory);
88492
+ if (!state3) {
88493
+ return null;
88494
+ }
88495
+ const works = getBoulderWorks(state3);
88496
+ const targetWork = works.find((work) => work.work_id === workId);
88497
+ if (!targetWork) {
88498
+ return null;
88499
+ }
88500
+ const updatedWork = {
88501
+ ...targetWork,
88502
+ session_ids: targetWork.session_ids.includes(normalizedSessionId) ? [...targetWork.session_ids] : [...targetWork.session_ids, normalizedSessionId],
88503
+ session_origins: { ...targetWork.session_origins ?? {}, [normalizedSessionId]: origin },
88504
+ updated_at: nowIsoString()
88505
+ };
88506
+ const nextState = {
88507
+ ...state3,
88508
+ schema_version: 2,
88509
+ works: {
88510
+ ...Object.fromEntries(works.map((work) => [work.work_id, work])),
88511
+ [workId]: updatedWork
88512
+ }
88513
+ };
88514
+ if (state3.active_work_id === workId) {
88515
+ projectWorkToMirror(nextState, updatedWork);
88516
+ }
88517
+ return writeBoulderState(directory, nextState) ? nextState : null;
88518
+ }
88519
+ // packages/boulder-state/src/storage/task.ts
88520
+ function upsertTaskSessionState(directory, input) {
88521
+ const stateForWork = readBoulderState(directory);
88522
+ if (stateForWork?.active_work_id) {
88523
+ return upsertTaskSessionStateForWork(directory, stateForWork.active_work_id, input);
88524
+ }
88525
+ const state3 = readBoulderState(directory);
88526
+ if (!state3 || RESERVED_KEYS.has(input.taskKey)) {
88527
+ return null;
88528
+ }
88529
+ const normalizedSessionId = normalizeSessionId(input.sessionId);
88530
+ const taskSessions = state3.task_sessions ?? {};
88531
+ taskSessions[input.taskKey] = {
88532
+ task_key: input.taskKey,
88533
+ task_label: input.taskLabel,
88534
+ task_title: input.taskTitle,
88535
+ session_id: normalizedSessionId,
88536
+ ...input.agent !== undefined ? { agent: input.agent } : {},
88537
+ ...input.category !== undefined ? { category: input.category } : {},
88538
+ updated_at: nowIsoString()
88539
+ };
88540
+ state3.task_sessions = taskSessions;
88541
+ return writeBoulderState(directory, state3) ? state3 : null;
88542
+ }
88543
+ function upsertTaskSessionStateForWork(directory, workId, input) {
88544
+ if (RESERVED_KEYS.has(input.taskKey)) {
88545
+ return null;
88546
+ }
88547
+ const state3 = readBoulderState(directory);
88548
+ if (!state3) {
88549
+ return null;
88550
+ }
88551
+ const works = getBoulderWorks(state3);
88552
+ const targetWork = works.find((work) => work.work_id === workId);
88553
+ if (!targetWork) {
88554
+ return null;
88555
+ }
88556
+ const normalizedSessionId = normalizeSessionId(input.sessionId);
88557
+ const previousTaskSession = targetWork.task_sessions?.[input.taskKey];
88558
+ const nextTaskSession = {
88559
+ task_key: input.taskKey,
88560
+ task_label: input.taskLabel,
88561
+ task_title: input.taskTitle,
88562
+ session_id: normalizedSessionId,
88563
+ ...input.agent !== undefined ? { agent: input.agent } : {},
88564
+ ...input.category !== undefined ? { category: input.category } : {},
88565
+ ...previousTaskSession?.started_at !== undefined ? { started_at: previousTaskSession.started_at } : {},
88566
+ ...previousTaskSession?.ended_at !== undefined ? { ended_at: previousTaskSession.ended_at } : {},
88567
+ ...previousTaskSession?.elapsed_ms !== undefined ? { elapsed_ms: previousTaskSession.elapsed_ms } : {},
88568
+ ...previousTaskSession?.status !== undefined ? { status: previousTaskSession.status } : {},
88569
+ updated_at: nowIsoString()
88570
+ };
88571
+ const nextWork = {
88572
+ ...targetWork,
88573
+ task_sessions: { ...targetWork.task_sessions ?? {}, [input.taskKey]: nextTaskSession },
88574
+ updated_at: nowIsoString()
88575
+ };
88576
+ const nextState = {
88577
+ ...state3,
88578
+ schema_version: 2,
88579
+ works: {
88580
+ ...Object.fromEntries(works.map((work) => [work.work_id, work])),
88581
+ [workId]: nextWork
88582
+ }
88583
+ };
88584
+ if (state3.active_work_id === workId) {
88585
+ projectWorkToMirror(nextState, nextWork);
88586
+ }
88587
+ return writeBoulderState(directory, nextState) ? nextState : null;
88588
+ }
88589
+ function startTaskTimer(directory, workId, input) {
88590
+ const nextState = upsertTaskSessionStateForWork(directory, workId, {
88591
+ ...input,
88592
+ sessionId: normalizeSessionId(input.sessionId)
88593
+ });
88594
+ if (!nextState) {
88595
+ return null;
88596
+ }
88597
+ const work = nextState.works?.[workId];
88598
+ const taskSession = work?.task_sessions?.[input.taskKey];
88599
+ if (!work || !taskSession) {
88600
+ return null;
88601
+ }
88602
+ const startedAt = taskSession.started_at ?? input.startedAt ?? nowIsoString();
88603
+ taskSession.started_at = startedAt;
88604
+ taskSession.status = "running";
88605
+ taskSession.updated_at = nowIsoString();
88606
+ work.updated_at = nowIsoString();
88607
+ return writeBoulderState(directory, nextState) ? nextState : null;
88608
+ }
88609
+ function endTaskTimer(directory, workId, taskKey, endedAt) {
88610
+ const state3 = readBoulderState(directory);
88611
+ if (!state3) {
88612
+ return null;
88613
+ }
88614
+ const work = state3.works?.[workId] ?? getBoulderWorks(state3).find((candidate) => candidate.work_id === workId);
88615
+ if (!work?.task_sessions?.[taskKey]) {
88616
+ return null;
88617
+ }
88618
+ const taskSession = work.task_sessions[taskKey];
88619
+ const endAt = endedAt ?? nowIsoString();
88620
+ taskSession.ended_at = endAt;
88621
+ taskSession.elapsed_ms = getElapsedMs(taskSession.started_at, endAt);
88622
+ taskSession.status = "completed";
88623
+ taskSession.updated_at = nowIsoString();
88624
+ work.updated_at = nowIsoString();
88625
+ if (state3.active_work_id === workId) {
88626
+ projectWorkToMirror(state3, work);
88627
+ }
88628
+ return writeBoulderState(directory, state3) ? state3 : null;
88629
+ }
88630
+ // packages/omo-opencode/src/features/boulder-state/format-duration.ts
88631
+ init_src();
87875
88632
  // packages/omo-opencode/src/hooks/claude-code-hooks/handlers/tool-execute-before-handler.ts
88633
+ function nonBlankString(value) {
88634
+ if (typeof value !== "string") {
88635
+ return null;
88636
+ }
88637
+ const trimmed = value.trim();
88638
+ return trimmed.length > 0 ? value : null;
88639
+ }
88640
+ function resolvePreToolUseCwd(input, toolInput, ctx) {
88641
+ if (input.tool.trim().toLowerCase() !== "bash") {
88642
+ return ctx.directory;
88643
+ }
88644
+ const explicitCwd = nonBlankString(toolInput.cwd);
88645
+ if (explicitCwd) {
88646
+ return explicitCwd;
88647
+ }
88648
+ const trackedWorktree = nonBlankString(getWorkForSession(ctx.directory, input.sessionID)?.worktree_path);
88649
+ if (trackedWorktree) {
88650
+ return trackedWorktree;
88651
+ }
88652
+ return nonBlankString(ctx.worktree) ?? ctx.directory;
88653
+ }
87876
88654
  function createToolExecuteBeforeHandler(ctx, config) {
87877
88655
  return async (input, output) => {
87878
88656
  if (input.tool.trim() === "todowrite" && typeof output.args.todos === "string") {
@@ -87909,7 +88687,7 @@ function createToolExecuteBeforeHandler(ctx, config) {
87909
88687
  sessionId: input.sessionID,
87910
88688
  toolName: input.tool,
87911
88689
  toolInput: output.args,
87912
- cwd: ctx.directory,
88690
+ cwd: resolvePreToolUseCwd(input, output.args, ctx),
87913
88691
  toolUseId: input.callID
87914
88692
  };
87915
88693
  const result = await executePreToolUseHooks(preCtx, claudeConfig, extendedConfig);
@@ -87952,28 +88730,28 @@ function createClaudeCodeHooksHook(ctx, config = {}, contextCollector) {
87952
88730
  }
87953
88731
  // packages/omo-opencode/src/hooks/rules-injector/storage.ts
87954
88732
  import {
87955
- existsSync as existsSync56,
87956
- mkdirSync as mkdirSync13,
87957
- readFileSync as readFileSync33,
87958
- writeFileSync as writeFileSync12,
87959
- unlinkSync as unlinkSync7
88733
+ existsSync as existsSync61,
88734
+ mkdirSync as mkdirSync14,
88735
+ readFileSync as readFileSync36,
88736
+ writeFileSync as writeFileSync13,
88737
+ unlinkSync as unlinkSync8
87960
88738
  } from "fs";
87961
- import { join as join68 } from "path";
88739
+ import { join as join71 } from "path";
87962
88740
 
87963
88741
  // packages/omo-opencode/src/hooks/rules-injector/constants.ts
87964
- import { join as join67 } from "path";
87965
- var RULES_INJECTOR_STORAGE = join67(OPENCODE_STORAGE, "rules-injector");
88742
+ import { join as join70 } from "path";
88743
+ var RULES_INJECTOR_STORAGE = join70(OPENCODE_STORAGE, "rules-injector");
87966
88744
 
87967
88745
  // packages/omo-opencode/src/hooks/rules-injector/storage.ts
87968
88746
  function getStoragePath(sessionID) {
87969
- return join68(RULES_INJECTOR_STORAGE, `${sessionID}.json`);
88747
+ return join71(RULES_INJECTOR_STORAGE, `${sessionID}.json`);
87970
88748
  }
87971
88749
  function loadInjectedRules(sessionID) {
87972
88750
  const filePath = getStoragePath(sessionID);
87973
- if (!existsSync56(filePath))
88751
+ if (!existsSync61(filePath))
87974
88752
  return { contentHashes: new Set, realPaths: new Set };
87975
88753
  try {
87976
- const content = readFileSync33(filePath, "utf-8");
88754
+ const content = readFileSync36(filePath, "utf-8");
87977
88755
  const data = JSON.parse(content);
87978
88756
  return {
87979
88757
  contentHashes: new Set(data.injectedHashes),
@@ -87993,21 +88771,21 @@ function saveInjectedRules(sessionID, data) {
87993
88771
  injectedRealPaths: [...data.realPaths],
87994
88772
  updatedAt: Date.now()
87995
88773
  };
87996
- mkdirSync13(RULES_INJECTOR_STORAGE, { recursive: true });
88774
+ mkdirSync14(RULES_INJECTOR_STORAGE, { recursive: true });
87997
88775
  try {
87998
- writeFileSync12(getStoragePath(sessionID), JSON.stringify(storageData, null, 2));
88776
+ writeFileSync13(getStoragePath(sessionID), JSON.stringify(storageData, null, 2));
87999
88777
  } catch (error) {
88000
88778
  if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
88001
88779
  throw error;
88002
88780
  }
88003
- mkdirSync13(RULES_INJECTOR_STORAGE, { recursive: true });
88004
- writeFileSync12(getStoragePath(sessionID), JSON.stringify(storageData, null, 2));
88781
+ mkdirSync14(RULES_INJECTOR_STORAGE, { recursive: true });
88782
+ writeFileSync13(getStoragePath(sessionID), JSON.stringify(storageData, null, 2));
88005
88783
  }
88006
88784
  }
88007
88785
  function clearInjectedRules(sessionID) {
88008
88786
  const filePath = getStoragePath(sessionID);
88009
- if (existsSync56(filePath)) {
88010
- unlinkSync7(filePath);
88787
+ if (existsSync61(filePath)) {
88788
+ unlinkSync8(filePath);
88011
88789
  }
88012
88790
  }
88013
88791
  // packages/omo-opencode/src/hooks/rules-injector/cache.ts
@@ -88100,7 +88878,7 @@ function matchDecisionCacheKey(projectRoot, resolvedFilePath, realPath, statFing
88100
88878
  }
88101
88879
 
88102
88880
  // packages/omo-opencode/src/hooks/rules-injector/parsed-rule-cache.ts
88103
- import { readFileSync as readFileSync34, statSync as statSync8 } from "fs";
88881
+ import { readFileSync as readFileSync37, statSync as statSync9 } from "fs";
88104
88882
  var MAX_PARSED_RULE_CACHE_ENTRIES = 256;
88105
88883
  var MAX_PARSED_RULE_CACHE_BODY_BYTES = 64 * 1024;
88106
88884
  var parsedRuleCache = new Map;
@@ -88108,8 +88886,8 @@ function clearParsedRuleCache() {
88108
88886
  parsedRuleCache.clear();
88109
88887
  }
88110
88888
  function createParsedRuleReader(options) {
88111
- const readRuleFileSync = options?.readFileSync ?? readFileSync34;
88112
- const statRuleSync = options?.statSync ?? statSync8;
88889
+ const readRuleFileSync = options?.readFileSync ?? readFileSync37;
88890
+ const statRuleSync = options?.statSync ?? statSync9;
88113
88891
  return (filePath, realPath) => {
88114
88892
  try {
88115
88893
  const stat2 = statRuleSync(filePath);
@@ -88154,13 +88932,13 @@ function setParsedRuleCacheEntry(realPath, entry) {
88154
88932
  }
88155
88933
 
88156
88934
  // packages/omo-opencode/src/hooks/rules-injector/path-resolution.ts
88157
- import { resolve as resolve17 } from "path";
88935
+ import { resolve as resolve18 } from "path";
88158
88936
  function resolveFilePath4(workspaceDirectory, path8) {
88159
88937
  if (!path8)
88160
88938
  return null;
88161
88939
  if (path8.startsWith("/"))
88162
88940
  return path8;
88163
- return resolve17(workspaceDirectory, path8);
88941
+ return resolve18(workspaceDirectory, path8);
88164
88942
  }
88165
88943
 
88166
88944
  // packages/omo-opencode/src/hooks/rules-injector/rule-match-reason.ts
@@ -88720,7 +89498,7 @@ function getCachedVersion(options = {}) {
88720
89498
  // package.json
88721
89499
  var package_default = {
88722
89500
  name: "oh-my-opencode",
88723
- version: "4.16.0",
89501
+ version: "4.16.2",
88724
89502
  description: "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
88725
89503
  main: "./dist/index.js",
88726
89504
  types: "dist/index.d.ts",
@@ -88766,10 +89544,11 @@ var package_default = {
88766
89544
  "dist",
88767
89545
  "bin",
88768
89546
  "postinstall.mjs",
89547
+ "script/qa/strip-ansi.mjs",
88769
89548
  "script/qa/web-terminal-redaction.d.mts",
88770
89549
  "script/qa/web-terminal-redaction.mjs",
88771
- "script/qa/web-terminal-renderer.mjs",
88772
89550
  "script/qa/web-terminal-visual-qa.mjs",
89551
+ "script/qa/xterm-live-terminal.mjs",
88773
89552
  "docs/reference/github-attachment-upload.md",
88774
89553
  "docs/reference/web-terminal-visual-qa.md",
88775
89554
  ".opencode/command",
@@ -88835,6 +89614,7 @@ var package_default = {
88835
89614
  "build:all": "bun run build && bun run build:binaries",
88836
89615
  "build:binaries": "bun run script/build-binaries.ts",
88837
89616
  "build:schema": "bun run script/build-schema.ts",
89617
+ "build:omo-schema": "bun run script/build-omo-schema.ts",
88838
89618
  "build:model-capabilities": "bun run script/build-model-capabilities.ts",
88839
89619
  clean: "rm -rf dist",
88840
89620
  prepack: "bun run build:materialize-frontend",
@@ -88891,51 +89671,55 @@ var package_default = {
88891
89671
  zod: "^4.4.3"
88892
89672
  },
88893
89673
  devDependencies: {
88894
- "@oh-my-opencode/git-bash-mcp": "workspace:*",
88895
- "@oh-my-opencode/lsp-core": "workspace:*",
88896
- "@oh-my-opencode/mcp-stdio-core": "workspace:*",
88897
- "@oh-my-opencode/mcp-client-core": "workspace:*",
88898
89674
  "@oh-my-opencode/agents-md-core": "workspace:*",
88899
89675
  "@oh-my-opencode/boulder-state": "workspace:*",
88900
89676
  "@oh-my-opencode/claude-code-compat-core": "workspace:*",
88901
- "@oh-my-opencode/skills-loader-core": "workspace:*",
88902
89677
  "@oh-my-opencode/comment-checker-core": "workspace:*",
88903
89678
  "@oh-my-opencode/delegate-core": "workspace:*",
89679
+ "@oh-my-opencode/git-bash-mcp": "workspace:*",
88904
89680
  "@oh-my-opencode/hashline-core": "workspace:*",
88905
- "@oh-my-opencode/tmux-core": "workspace:*",
88906
- "@oh-my-opencode/team-core": "workspace:*",
88907
- "@oh-my-opencode/openclaw-core": "workspace:*",
89681
+ "@oh-my-opencode/lsp-core": "workspace:*",
89682
+ "@oh-my-opencode/mcp-client-core": "workspace:*",
89683
+ "@oh-my-opencode/mcp-stdio-core": "workspace:*",
88908
89684
  "@oh-my-opencode/model-core": "workspace:*",
88909
- "@oh-my-opencode/omo-config-core": "workspace:*",
88910
89685
  "@oh-my-opencode/omo-codex": "workspace:*",
89686
+ "@oh-my-opencode/omo-config-core": "workspace:*",
88911
89687
  "@oh-my-opencode/omo-senpi": "workspace:*",
88912
- "@oh-my-opencode/senpi-task": "workspace:*",
89688
+ "@oh-my-opencode/openclaw-core": "workspace:*",
88913
89689
  "@oh-my-opencode/pi-goal": "workspace:*",
88914
89690
  "@oh-my-opencode/pi-webfetch": "workspace:*",
88915
89691
  "@oh-my-opencode/prompts-core": "workspace:*",
88916
89692
  "@oh-my-opencode/rules-engine": "workspace:*",
89693
+ "@oh-my-opencode/senpi-task": "workspace:*",
88917
89694
  "@oh-my-opencode/shared-skills": "workspace:*",
89695
+ "@oh-my-opencode/skills-loader-core": "workspace:*",
89696
+ "@oh-my-opencode/team-core": "workspace:*",
88918
89697
  "@oh-my-opencode/telemetry-core": "workspace:*",
89698
+ "@oh-my-opencode/tmux-core": "workspace:*",
88919
89699
  "@oh-my-opencode/utils": "workspace:*",
88920
- "@typescript/native-preview": "7.0.0-dev.20260518.1",
88921
89700
  "@types/js-yaml": "^4.0.9",
88922
89701
  "@types/picomatch": "^4.0.3",
89702
+ "@typescript/native-preview": "7.0.0-dev.20260518.1",
89703
+ "@xterm/addon-unicode11": "^0.9.0",
89704
+ "@xterm/xterm": "^6.0.0",
88923
89705
  "bun-types": "1.3.14",
89706
+ "node-pty": "^1.1.0",
89707
+ "puppeteer-core": "^25.3.0",
88924
89708
  typescript: "^6.0.3"
88925
89709
  },
88926
89710
  optionalDependencies: {
88927
- "oh-my-opencode-darwin-arm64": "4.16.0",
88928
- "oh-my-opencode-darwin-x64": "4.16.0",
88929
- "oh-my-opencode-darwin-x64-baseline": "4.16.0",
88930
- "oh-my-opencode-linux-arm64": "4.16.0",
88931
- "oh-my-opencode-linux-arm64-musl": "4.16.0",
88932
- "oh-my-opencode-linux-x64": "4.16.0",
88933
- "oh-my-opencode-linux-x64-baseline": "4.16.0",
88934
- "oh-my-opencode-linux-x64-musl": "4.16.0",
88935
- "oh-my-opencode-linux-x64-musl-baseline": "4.16.0",
88936
- "oh-my-opencode-windows-arm64": "4.16.0",
88937
- "oh-my-opencode-windows-x64": "4.16.0",
88938
- "oh-my-opencode-windows-x64-baseline": "4.16.0"
89711
+ "oh-my-opencode-darwin-arm64": "4.16.2",
89712
+ "oh-my-opencode-darwin-x64": "4.16.2",
89713
+ "oh-my-opencode-darwin-x64-baseline": "4.16.2",
89714
+ "oh-my-opencode-linux-arm64": "4.16.2",
89715
+ "oh-my-opencode-linux-arm64-musl": "4.16.2",
89716
+ "oh-my-opencode-linux-x64": "4.16.2",
89717
+ "oh-my-opencode-linux-x64-baseline": "4.16.2",
89718
+ "oh-my-opencode-linux-x64-musl": "4.16.2",
89719
+ "oh-my-opencode-linux-x64-musl-baseline": "4.16.2",
89720
+ "oh-my-opencode-windows-arm64": "4.16.2",
89721
+ "oh-my-opencode-windows-x64": "4.16.2",
89722
+ "oh-my-opencode-windows-x64-baseline": "4.16.2"
88939
89723
  },
88940
89724
  overrides: {
88941
89725
  "@earendil-works/pi-agent-core": "0.80.3",
@@ -89116,13 +89900,13 @@ function syncCachePackageJsonToIntent(pluginInfo) {
89116
89900
  };
89117
89901
  }
89118
89902
  // packages/omo-opencode/src/hooks/auto-update-checker/hook/background-update-check.ts
89119
- import { existsSync as existsSync65 } from "fs";
89120
- import { dirname as dirname22, join as join76 } from "path";
89903
+ import { existsSync as existsSync70 } from "fs";
89904
+ import { dirname as dirname23, join as join79 } from "path";
89121
89905
  import { fileURLToPath as fileURLToPath4 } from "url";
89122
89906
  // packages/omo-opencode/src/shared/migrate-legacy-config-file.ts
89123
89907
  init_logger2();
89124
- import { existsSync as existsSync62, readFileSync as readFileSync41, renameSync as renameSync6, rmSync as rmSync3 } from "fs";
89125
- import { join as join73, dirname as dirname21, basename as basename12 } from "path";
89908
+ import { existsSync as existsSync67, readFileSync as readFileSync44, renameSync as renameSync6, rmSync as rmSync3 } from "fs";
89909
+ import { join as join76, dirname as dirname22, basename as basename13 } from "path";
89126
89910
 
89127
89911
  // packages/omo-opencode/src/shared/migration/migrations-sidecar.ts
89128
89912
  init_migrations_sidecar();
@@ -89130,9 +89914,9 @@ init_migrations_sidecar();
89130
89914
  // packages/omo-opencode/src/shared/migrate-legacy-config-file.ts
89131
89915
  init_plugin_identity();
89132
89916
  function buildCanonicalPath(legacyPath) {
89133
- const dir = dirname21(legacyPath);
89134
- const ext = basename12(legacyPath).includes(".jsonc") ? ".jsonc" : ".json";
89135
- return join73(dir, `${CONFIG_BASENAME}${ext}`);
89917
+ const dir = dirname22(legacyPath);
89918
+ const ext = basename13(legacyPath).includes(".jsonc") ? ".jsonc" : ".json";
89919
+ return join76(dir, `${CONFIG_BASENAME}${ext}`);
89136
89920
  }
89137
89921
  function archiveLegacyConfigFile(legacyPath) {
89138
89922
  const backupPath = `${legacyPath}.bak`;
@@ -89165,13 +89949,13 @@ function archiveLegacyConfigFile(legacyPath) {
89165
89949
  }
89166
89950
  function migrateLegacySidecarFile(legacyPath, canonicalPath) {
89167
89951
  const legacySidecarPath = getSidecarPath(legacyPath);
89168
- if (!existsSync62(legacySidecarPath))
89952
+ if (!existsSync67(legacySidecarPath))
89169
89953
  return true;
89170
89954
  const canonicalSidecarPath = getSidecarPath(canonicalPath);
89171
- if (existsSync62(canonicalSidecarPath))
89955
+ if (existsSync67(canonicalSidecarPath))
89172
89956
  return true;
89173
89957
  try {
89174
- const content = readFileSync41(legacySidecarPath, "utf-8");
89958
+ const content = readFileSync44(legacySidecarPath, "utf-8");
89175
89959
  writeFileAtomically2(canonicalSidecarPath, content);
89176
89960
  log2("[migrateLegacyConfigFile] Migrated legacy migration sidecar to canonical path", {
89177
89961
  from: legacySidecarPath,
@@ -89188,15 +89972,15 @@ function migrateLegacySidecarFile(legacyPath, canonicalPath) {
89188
89972
  }
89189
89973
  }
89190
89974
  function migrateLegacyConfigFile(legacyPath) {
89191
- if (!existsSync62(legacyPath))
89975
+ if (!existsSync67(legacyPath))
89192
89976
  return false;
89193
- if (!basename12(legacyPath).startsWith(LEGACY_CONFIG_BASENAME))
89977
+ if (!basename13(legacyPath).startsWith(LEGACY_CONFIG_BASENAME))
89194
89978
  return false;
89195
89979
  const canonicalPath = buildCanonicalPath(legacyPath);
89196
- if (existsSync62(canonicalPath))
89980
+ if (existsSync67(canonicalPath))
89197
89981
  return false;
89198
89982
  try {
89199
- const content = readFileSync41(legacyPath, "utf-8");
89983
+ const content = readFileSync44(legacyPath, "utf-8");
89200
89984
  writeFileAtomically2(canonicalPath, content);
89201
89985
  const migratedSidecar = migrateLegacySidecarFile(legacyPath, canonicalPath);
89202
89986
  const archivedLegacyConfig = archiveLegacyConfigFile(legacyPath);
@@ -89232,8 +90016,8 @@ function toReadableStream2(stream) {
89232
90016
  function wrapNodeProcess2(proc) {
89233
90017
  let resolveExited;
89234
90018
  let exitCode = null;
89235
- const exited = new Promise((resolve18, reject) => {
89236
- resolveExited = resolve18;
90019
+ const exited = new Promise((resolve19, reject) => {
90020
+ resolveExited = resolve19;
89237
90021
  proc.on("error", (error) => {
89238
90022
  if (exitCode === null) {
89239
90023
  exitCode = 1;
@@ -89285,13 +90069,13 @@ function spawnWithWindowsHide(command, options) {
89285
90069
  return wrapNodeProcess2(proc);
89286
90070
  }
89287
90071
  // packages/omo-opencode/src/cli/config-manager/bun-install.ts
89288
- import { existsSync as existsSync63 } from "fs";
89289
- import { join as join74 } from "path";
90072
+ import { existsSync as existsSync68 } from "fs";
90073
+ import { join as join77 } from "path";
89290
90074
  init_logger2();
89291
90075
  var BUN_INSTALL_TIMEOUT_SECONDS = 60;
89292
90076
  var BUN_INSTALL_TIMEOUT_MS = BUN_INSTALL_TIMEOUT_SECONDS * 1000;
89293
90077
  function getDefaultWorkspaceDir() {
89294
- return join74(getOpenCodeCacheDir(), "packages");
90078
+ return join77(getOpenCodeCacheDir(), "packages");
89295
90079
  }
89296
90080
  function readProcessOutput(stream) {
89297
90081
  if (!stream) {
@@ -89317,7 +90101,7 @@ async function runBunInstallWithDetails(options) {
89317
90101
  const outputMode = options?.outputMode ?? "pipe";
89318
90102
  const cacheDir = options?.workspaceDir ?? getDefaultWorkspaceDir();
89319
90103
  const packageJsonPath = `${cacheDir}/package.json`;
89320
- if (!existsSync63(packageJsonPath)) {
90104
+ if (!existsSync68(packageJsonPath)) {
89321
90105
  return {
89322
90106
  success: false,
89323
90107
  error: `Workspace not initialized: ${packageJsonPath} not found. OpenCode should create this on first run.`
@@ -89332,8 +90116,8 @@ async function runBunInstallWithDetails(options) {
89332
90116
  });
89333
90117
  const outputPromise = Promise.all([readProcessOutput(proc.stdout), readProcessOutput(proc.stderr)]).then(([stdout, stderr]) => ({ stdout, stderr }));
89334
90118
  let timeoutId;
89335
- const timeoutPromise = new Promise((resolve18) => {
89336
- timeoutId = setTimeout(() => resolve18("timeout"), BUN_INSTALL_TIMEOUT_MS);
90119
+ const timeoutPromise = new Promise((resolve19) => {
90120
+ timeoutId = setTimeout(() => resolve19("timeout"), BUN_INSTALL_TIMEOUT_MS);
89337
90121
  });
89338
90122
  const exitPromise = proc.exited.then(() => "completed");
89339
90123
  const result = await Promise.race([exitPromise, timeoutPromise]);
@@ -89534,15 +90318,15 @@ Restart OpenCode to apply.`,
89534
90318
  // packages/omo-opencode/src/hooks/auto-update-checker/hook/background-update-check.ts
89535
90319
  function defaultGetModuleHostingWorkspace() {
89536
90320
  try {
89537
- const currentDir = dirname22(fileURLToPath4(import.meta.url));
90321
+ const currentDir = dirname23(fileURLToPath4(import.meta.url));
89538
90322
  const pkgJsonPath = findPackageJsonUp(currentDir);
89539
90323
  if (!pkgJsonPath)
89540
90324
  return null;
89541
- const pkgDir = dirname22(pkgJsonPath);
89542
- const nodeModulesDir = dirname22(pkgDir);
90325
+ const pkgDir = dirname23(pkgJsonPath);
90326
+ const nodeModulesDir = dirname23(pkgDir);
89543
90327
  if (nodeModulesDir.split(/[\\/]/).pop() !== "node_modules")
89544
90328
  return null;
89545
- return dirname22(nodeModulesDir);
90329
+ return dirname23(nodeModulesDir);
89546
90330
  } catch (error) {
89547
90331
  if (error instanceof Error) {
89548
90332
  return null;
@@ -89554,8 +90338,8 @@ function getCacheWorkspaceDir(deps) {
89554
90338
  return deps.join(deps.getOpenCodeCacheDir(), "packages");
89555
90339
  }
89556
90340
  var defaultDeps2 = {
89557
- existsSync: existsSync65,
89558
- join: join76,
90341
+ existsSync: existsSync70,
90342
+ join: join79,
89559
90343
  runBunInstallWithDetails,
89560
90344
  log: log2,
89561
90345
  getOpenCodeCacheDir,
@@ -89827,7 +90611,7 @@ async function showSpinnerToast(ctx, version, message) {
89827
90611
  duration: frameInterval + 50
89828
90612
  }
89829
90613
  }).catch(ignoreToastError);
89830
- await new Promise((resolve18) => setTimeout(resolve18, frameInterval));
90614
+ await new Promise((resolve19) => setTimeout(resolve19, frameInterval));
89831
90615
  }
89832
90616
  }
89833
90617
 
@@ -89926,9 +90710,9 @@ v${latestVersion} available. Restart OpenCode to apply.` : "OpenCode is now on S
89926
90710
  }
89927
90711
  // packages/omo-opencode/src/hooks/codegraph-bootstrap/hook.ts
89928
90712
  init_src();
89929
- import { existsSync as existsSync66 } from "fs";
90713
+ import { existsSync as existsSync71 } from "fs";
89930
90714
  import { homedir as homedir21 } from "os";
89931
- import { join as join77 } from "path";
90715
+ import { join as join80 } from "path";
89932
90716
 
89933
90717
  // packages/omo-opencode/src/hooks/codegraph-bootstrap/command-runner.ts
89934
90718
  init_src();
@@ -89950,7 +90734,7 @@ function resolveExitCode(error) {
89950
90734
  async function runCodegraphCommand(projectRoot, command, args, options) {
89951
90735
  const { execFile: execFile3 } = await import("child_process");
89952
90736
  const invocation = resolveCodegraphCommandInvocation(command, args);
89953
- return new Promise((resolve18) => {
90737
+ return new Promise((resolve19) => {
89954
90738
  execFile3(invocation.command, [...invocation.args], {
89955
90739
  cwd: projectRoot,
89956
90740
  encoding: "utf8",
@@ -89960,10 +90744,10 @@ async function runCodegraphCommand(projectRoot, command, args, options) {
89960
90744
  windowsHide: true
89961
90745
  }, (error, stdout, stderr) => {
89962
90746
  if (error === null) {
89963
- resolve18({ exitCode: 0, stderr: toOutputText(stderr), stdout: toOutputText(stdout), timedOut: false });
90747
+ resolve19({ exitCode: 0, stderr: toOutputText(stderr), stdout: toOutputText(stdout), timedOut: false });
89964
90748
  return;
89965
90749
  }
89966
- resolve18({
90750
+ resolve19({
89967
90751
  exitCode: resolveExitCode(error),
89968
90752
  stderr: toOutputText(stderr),
89969
90753
  stdout: toOutputText(stdout),
@@ -89985,7 +90769,7 @@ function resolveCodegraphCommandInvocation(command, args, platform2 = process.pl
89985
90769
 
89986
90770
  // packages/omo-opencode/src/hooks/codegraph-bootstrap/project-root.ts
89987
90771
  init_src();
89988
- import { resolve as resolve18 } from "path";
90772
+ import { resolve as resolve19 } from "path";
89989
90773
  var PROJECT_ROOT_KEYS = ["directory", "worktree", "cwd", "projectRoot", "projectPath"];
89990
90774
  function readStringField(record, key) {
89991
90775
  const value = record[key];
@@ -90018,8 +90802,8 @@ function readRecordRoot(record) {
90018
90802
  }
90019
90803
  function resolveCodegraphProjectRoot(properties, fallbackDirectory) {
90020
90804
  if (!isRecord(properties))
90021
- return resolve18(fallbackDirectory);
90022
- return resolve18(readRecordRoot(properties) ?? fallbackDirectory);
90805
+ return resolve19(fallbackDirectory);
90806
+ return resolve19(readRecordRoot(properties) ?? fallbackDirectory);
90023
90807
  }
90024
90808
 
90025
90809
  // packages/omo-opencode/src/hooks/codegraph-bootstrap/status.ts
@@ -90081,14 +90865,14 @@ function defaultSchedule(task) {
90081
90865
  timer.unref?.();
90082
90866
  }
90083
90867
  function defaultInstallDir2() {
90084
- return join77(homedir21(), ".omo", "codegraph");
90868
+ return join80(homedir21(), ".omo", "codegraph");
90085
90869
  }
90086
90870
  function provisionedBinFromInstallDir(installDir) {
90087
90871
  if (installDir === undefined)
90088
90872
  return null;
90089
90873
  const binaryName = process.platform === "win32" ? "codegraph.cmd" : "codegraph";
90090
- const candidate = join77(installDir, "bin", binaryName);
90091
- return existsSync66(candidate) ? candidate : null;
90874
+ const candidate = join80(installDir, "bin", binaryName);
90875
+ return existsSync71(candidate) ? candidate : null;
90092
90876
  }
90093
90877
  function codegraphEnv(deps, config) {
90094
90878
  const env2 = deps.buildEnv();
@@ -90117,7 +90901,7 @@ async function resolveOrProvisionCommand(deps, config) {
90117
90901
  const installDir = config.install_dir ?? defaultInstallDir2();
90118
90902
  const provisioned = await deps.ensureProvisioned({
90119
90903
  installDir,
90120
- lockDir: join77(installDir, "locks"),
90904
+ lockDir: join80(installDir, "locks"),
90121
90905
  version: CODEGRAPH_VERSION
90122
90906
  });
90123
90907
  if (!provisioned.provisioned || provisioned.binPath === undefined) {
@@ -90132,8 +90916,8 @@ async function resolveOrProvisionCommand(deps, config) {
90132
90916
  async function runBootstrap(projectRoot, config, deps) {
90133
90917
  try {
90134
90918
  const autoInit = config.auto_init !== false;
90135
- const codegraphPath = join77(projectRoot, ".codegraph");
90136
- if (!autoInit && !existsSync66(codegraphPath)) {
90919
+ const codegraphPath = join80(projectRoot, ".codegraph");
90920
+ if (!autoInit && !existsSync71(codegraphPath)) {
90137
90921
  deps.log("[codegraph-bootstrap] CodeGraph auto_init disabled and .codegraph not present; skipping bootstrap", {
90138
90922
  projectRoot
90139
90923
  });
@@ -90221,7 +91005,7 @@ function createCodegraphBootstrapHook(ctx, config, depsOverride = {}) {
90221
91005
  // packages/omo-opencode/src/hooks/ast-grep-sg-provision/hook.ts
90222
91006
  init_src();
90223
91007
  import { homedir as homedir22 } from "os";
90224
- import { join as join78 } from "path";
91008
+ import { join as join81 } from "path";
90225
91009
  var provisionedTargets = new Set;
90226
91010
  function defaultSchedule2(task) {
90227
91011
  const timer = setTimeout(() => {
@@ -90248,7 +91032,7 @@ var defaultDeps5 = {
90248
91032
  function createAstGrepSgProvisionHook(depsOverride = {}) {
90249
91033
  const deps = { ...defaultDeps5, ...depsOverride };
90250
91034
  const platform2 = deps.platform ?? process.platform;
90251
- const targetDir = astGrepRuntimeDir(join78(deps.homeDir(), ".omo"), platform2, deps.arch ?? process.arch);
91035
+ const targetDir = astGrepRuntimeDir(join81(deps.homeDir(), ".omo"), platform2, deps.arch ?? process.arch);
90252
91036
  return {
90253
91037
  event(input) {
90254
91038
  try {
@@ -90269,17 +91053,17 @@ function createAstGrepSgProvisionHook(depsOverride = {}) {
90269
91053
  }
90270
91054
  // packages/omo-opencode/src/hooks/agent-usage-reminder/storage.ts
90271
91055
  import {
90272
- existsSync as existsSync67,
90273
- mkdirSync as mkdirSync15,
90274
- readFileSync as readFileSync43,
90275
- writeFileSync as writeFileSync15,
90276
- unlinkSync as unlinkSync10
91056
+ existsSync as existsSync72,
91057
+ mkdirSync as mkdirSync16,
91058
+ readFileSync as readFileSync46,
91059
+ writeFileSync as writeFileSync16,
91060
+ unlinkSync as unlinkSync11
90277
91061
  } from "fs";
90278
- import { join as join80 } from "path";
91062
+ import { join as join83 } from "path";
90279
91063
 
90280
91064
  // packages/omo-opencode/src/hooks/agent-usage-reminder/constants.ts
90281
- import { join as join79 } from "path";
90282
- var AGENT_USAGE_REMINDER_STORAGE = join79(OPENCODE_STORAGE, "agent-usage-reminder");
91065
+ import { join as join82 } from "path";
91066
+ var AGENT_USAGE_REMINDER_STORAGE = join82(OPENCODE_STORAGE, "agent-usage-reminder");
90283
91067
  var TARGET_TOOLS = new Set([
90284
91068
  "grep",
90285
91069
  "safe_grep",
@@ -90325,14 +91109,14 @@ ALWAYS prefer: Multiple parallel task calls > Direct tool calls
90325
91109
 
90326
91110
  // packages/omo-opencode/src/hooks/agent-usage-reminder/storage.ts
90327
91111
  function getStoragePath2(sessionID) {
90328
- return join80(AGENT_USAGE_REMINDER_STORAGE, `${sessionID}.json`);
91112
+ return join83(AGENT_USAGE_REMINDER_STORAGE, `${sessionID}.json`);
90329
91113
  }
90330
91114
  function loadAgentUsageState(sessionID) {
90331
91115
  const filePath = getStoragePath2(sessionID);
90332
- if (!existsSync67(filePath))
91116
+ if (!existsSync72(filePath))
90333
91117
  return null;
90334
91118
  try {
90335
- const content = readFileSync43(filePath, "utf-8");
91119
+ const content = readFileSync46(filePath, "utf-8");
90336
91120
  return JSON.parse(content);
90337
91121
  } catch (error) {
90338
91122
  error instanceof Error;
@@ -90340,16 +91124,16 @@ function loadAgentUsageState(sessionID) {
90340
91124
  }
90341
91125
  }
90342
91126
  function saveAgentUsageState(state3) {
90343
- if (!existsSync67(AGENT_USAGE_REMINDER_STORAGE)) {
90344
- mkdirSync15(AGENT_USAGE_REMINDER_STORAGE, { recursive: true });
91127
+ if (!existsSync72(AGENT_USAGE_REMINDER_STORAGE)) {
91128
+ mkdirSync16(AGENT_USAGE_REMINDER_STORAGE, { recursive: true });
90345
91129
  }
90346
91130
  const filePath = getStoragePath2(state3.sessionID);
90347
- writeFileSync15(filePath, JSON.stringify(state3, null, 2));
91131
+ writeFileSync16(filePath, JSON.stringify(state3, null, 2));
90348
91132
  }
90349
91133
  function clearAgentUsageState(sessionID) {
90350
91134
  const filePath = getStoragePath2(sessionID);
90351
- if (existsSync67(filePath)) {
90352
- unlinkSync10(filePath);
91135
+ if (existsSync72(filePath)) {
91136
+ unlinkSync11(filePath);
90353
91137
  }
90354
91138
  }
90355
91139
 
@@ -93963,7 +94747,7 @@ Tests are the FLOOR (always required). Surface artifact is the CEILING (also req
93963
94747
  | Changes build output | Run the build. Verify the output files exist and are correct. |
93964
94748
  | Modifies API behavior | Call the endpoint. Show the response. |
93965
94749
  | Changes UI rendering | Use Chrome to drive the REAL page; if Chrome is not available, download and use agent-browser (https://github.com/vercel-labs/agent-browser). Capture screenshot + action log. |
93966
- | Changes UI rendering or a TUI/terminal layout (incl. CJK/Korean/Japanese/Chinese text) | Load the visual-qa skill: capture reference + actual screenshots (web) or \`tmux capture-pane\` (TUI), run its bundled pixel-diff / column-width script, and get the dual read-only verdict (design-system + functional integrity, and visual fidelity + CJK precision). Record the diff/score artifact. |
94750
+ | Changes UI rendering or a TUI/terminal layout (incl. CJK/Korean/Japanese/Chinese text) | Load the visual-qa skill: capture reference + actual screenshots (web) or the xterm.js web terminal render (TUI; NEVER \`tmux capture-pane\` - it degrades color and CJK width), run its bundled pixel-diff / column-width script, and get the dual read-only verdict (design-system + functional integrity, and visual fidelity + CJK precision). Record the diff/score artifact. |
93967
94751
  | Changes a desktop/GUI (non-page) surface | Computer use: OS-level GUI automation against the running app. Capture action log + screenshot. |
93968
94752
  | Adds a new tool/hook/feature | Test it end-to-end in a real scenario. |
93969
94753
  | Modifies config handling | Load the config. Verify it parses correctly. |
@@ -94325,7 +95109,7 @@ Trigger if user said "\uC5C4\uBC00"/"strictly"/"rigorously"/"properly review", o
94325
95109
  | Changes build output | Run the build. Verify output files exist and are correct. |
94326
95110
  | Modifies API behavior | Call the endpoint. Show the response. |
94327
95111
  | Renders/changes a page | Use Chrome to drive the REAL page; if Chrome is not available, download and use agent-browser (https://github.com/vercel-labs/agent-browser). Capture screenshot + action log. |
94328
- | Changes UI rendering or a TUI/terminal layout (incl. CJK/Korean/Japanese/Chinese text) | Load the visual-qa skill: capture reference + actual screenshots (web) or \`tmux capture-pane\` (TUI), run its bundled pixel-diff / column-width script, and get the dual read-only verdict (design-system + functional integrity, and visual fidelity + CJK precision). Record the diff/score artifact. |
95112
+ | Changes UI rendering or a TUI/terminal layout (incl. CJK/Korean/Japanese/Chinese text) | Load the visual-qa skill: capture reference + actual screenshots (web) or the xterm.js web terminal render (TUI; NEVER \`tmux capture-pane\` - it degrades color and CJK width), run its bundled pixel-diff / column-width script, and get the dual read-only verdict (design-system + functional integrity, and visual fidelity + CJK precision). Record the diff/score artifact. |
94329
95113
  | Drives a desktop/GUI (non-page) surface | Computer use: OS-level GUI automation against the running app. Capture action log + screenshot. |
94330
95114
  | Adds a new tool/hook/feature | Test it end-to-end in a real scenario. |
94331
95115
  | Modifies config handling | Load the config. Verify it parses correctly. |
@@ -94545,7 +95329,7 @@ Tests are necessary and insufficient. Exercise the real surface.
94545
95329
  | CLI | Run the command and show stdout/stderr. |
94546
95330
  | API | Call the endpoint and show status/body. |
94547
95331
  | UI | Drive the page in a browser and capture a screenshot or trace. |
94548
- | TUI | Capture the terminal pane and verify layout. |
95332
+ | TUI | Render through the xterm.js web terminal and screenshot it (NEVER \`tmux capture-pane\`); verify color, layout, and CJK width. |
94549
95333
  | Config | Load the config and verify the parsed shape. |
94550
95334
  | Prompt or mode | Verify the prompt loads or the registry resolves it. |
94551
95335
  | Build output | Run build and verify exit code 0. |
@@ -94740,7 +95524,7 @@ lsp_diagnostics catches type errors only. Logic bugs, missing behavior, broken f
94740
95524
  | Changes build output | Run build. Verify output files. |
94741
95525
  | Modifies API behavior | Call the endpoint. Show response. |
94742
95526
  | Renders/changes a page | Use Chrome to drive the page; if Chrome is not available, download and use agent-browser (https://github.com/vercel-labs/agent-browser). Screenshot + action log. |
94743
- | Changes UI rendering or a TUI/terminal layout (incl. CJK/Korean/Japanese/Chinese text) | Load the visual-qa skill: capture reference + actual screenshots (web) or \`tmux capture-pane\` (TUI), run its bundled pixel-diff / column-width script, and get the dual read-only verdict (design-system + functional integrity, and visual fidelity + CJK precision). Record the diff/score artifact. |
95527
+ | Changes UI rendering or a TUI/terminal layout (incl. CJK/Korean/Japanese/Chinese text) | Load the visual-qa skill: capture reference + actual screenshots (web) or the xterm.js web terminal render (TUI; NEVER \`tmux capture-pane\` - it degrades color and CJK width), run its bundled pixel-diff / column-width script, and get the dual read-only verdict (design-system + functional integrity, and visual fidelity + CJK precision). Record the diff/score artifact. |
94744
95528
  | Drives a desktop GUI | Computer use: OS-level GUI automation against the running app. Action log + screenshot. |
94745
95529
  | Adds tool/hook/feature | Test end-to-end in a real scenario. |
94746
95530
  | Modifies config handling | Load config. Verify parsed shape. |
@@ -95299,17 +96083,17 @@ function createNonInteractiveEnvHook(_ctx) {
95299
96083
  }
95300
96084
  // packages/omo-opencode/src/hooks/interactive-bash-session/storage.ts
95301
96085
  import {
95302
- existsSync as existsSync68,
95303
- mkdirSync as mkdirSync16,
95304
- readFileSync as readFileSync44,
95305
- writeFileSync as writeFileSync16,
95306
- unlinkSync as unlinkSync11
96086
+ existsSync as existsSync73,
96087
+ mkdirSync as mkdirSync17,
96088
+ readFileSync as readFileSync47,
96089
+ writeFileSync as writeFileSync17,
96090
+ unlinkSync as unlinkSync12
95307
96091
  } from "fs";
95308
- import { join as join82 } from "path";
96092
+ import { join as join85 } from "path";
95309
96093
 
95310
96094
  // packages/omo-opencode/src/hooks/interactive-bash-session/constants.ts
95311
- import { join as join81 } from "path";
95312
- var INTERACTIVE_BASH_SESSION_STORAGE = join81(OPENCODE_STORAGE, "interactive-bash-session");
96095
+ import { join as join84 } from "path";
96096
+ var INTERACTIVE_BASH_SESSION_STORAGE = join84(OPENCODE_STORAGE, "interactive-bash-session");
95313
96097
  var OMO_SESSION_PREFIX = "omo-";
95314
96098
  function buildSessionReminderMessage(sessions) {
95315
96099
  if (sessions.length === 0)
@@ -95321,14 +96105,14 @@ function buildSessionReminderMessage(sessions) {
95321
96105
 
95322
96106
  // packages/omo-opencode/src/hooks/interactive-bash-session/storage.ts
95323
96107
  function getStoragePath3(sessionID) {
95324
- return join82(INTERACTIVE_BASH_SESSION_STORAGE, `${sessionID}.json`);
96108
+ return join85(INTERACTIVE_BASH_SESSION_STORAGE, `${sessionID}.json`);
95325
96109
  }
95326
96110
  function loadInteractiveBashSessionState(sessionID) {
95327
96111
  const filePath = getStoragePath3(sessionID);
95328
- if (!existsSync68(filePath))
96112
+ if (!existsSync73(filePath))
95329
96113
  return null;
95330
96114
  try {
95331
- const content = readFileSync44(filePath, "utf-8");
96115
+ const content = readFileSync47(filePath, "utf-8");
95332
96116
  const serialized = JSON.parse(content);
95333
96117
  return {
95334
96118
  sessionID: serialized.sessionID,
@@ -95343,8 +96127,8 @@ function loadInteractiveBashSessionState(sessionID) {
95343
96127
  }
95344
96128
  }
95345
96129
  function saveInteractiveBashSessionState(state3) {
95346
- if (!existsSync68(INTERACTIVE_BASH_SESSION_STORAGE)) {
95347
- mkdirSync16(INTERACTIVE_BASH_SESSION_STORAGE, { recursive: true });
96130
+ if (!existsSync73(INTERACTIVE_BASH_SESSION_STORAGE)) {
96131
+ mkdirSync17(INTERACTIVE_BASH_SESSION_STORAGE, { recursive: true });
95348
96132
  }
95349
96133
  const filePath = getStoragePath3(state3.sessionID);
95350
96134
  const serialized = {
@@ -95352,12 +96136,12 @@ function saveInteractiveBashSessionState(state3) {
95352
96136
  tmuxSessions: Array.from(state3.tmuxSessions),
95353
96137
  updatedAt: state3.updatedAt
95354
96138
  };
95355
- writeFileSync16(filePath, JSON.stringify(serialized, null, 2));
96139
+ writeFileSync17(filePath, JSON.stringify(serialized, null, 2));
95356
96140
  }
95357
96141
  function clearInteractiveBashSessionState(sessionID) {
95358
96142
  const filePath = getStoragePath3(sessionID);
95359
- if (existsSync68(filePath)) {
95360
- unlinkSync11(filePath);
96143
+ if (existsSync73(filePath)) {
96144
+ unlinkSync12(filePath);
95361
96145
  }
95362
96146
  }
95363
96147
 
@@ -96415,7 +97199,7 @@ async function withTimeout(promise, timeoutMs) {
96415
97199
  var USER_MESSAGE_IN_PROGRESS_WINDOW_MS = 2000;
96416
97200
  var RAPID_IDLE_DEDUP_MS = 500;
96417
97201
  function sleep2(ms) {
96418
- return ms > 0 ? new Promise((resolve19) => setTimeout(resolve19, ms)) : Promise.resolve();
97202
+ return ms > 0 ? new Promise((resolve20) => setTimeout(resolve20, ms)) : Promise.resolve();
96419
97203
  }
96420
97204
  function hasActiveBackgroundTasks(backgroundManager, sessionID) {
96421
97205
  return backgroundManager ? backgroundManager.getTasksByParentSession(sessionID).some((task) => task.status === "pending" || task.status === "running") : false;
@@ -97299,7 +98083,7 @@ async function handleDetectedCompletion(ctx, input) {
97299
98083
  init_logger2();
97300
98084
  init_constants3();
97301
98085
  init_constants3();
97302
- import { existsSync as existsSync70, readFileSync as readFileSync46 } from "fs";
98086
+ import { existsSync as existsSync75, readFileSync as readFileSync49 } from "fs";
97303
98087
  function extractTranscriptEntryText(entry) {
97304
98088
  if (typeof entry.content === "string")
97305
98089
  return entry.content;
@@ -97337,9 +98121,9 @@ function detectCompletionInTranscript(transcriptPath, promise, startedAt) {
97337
98121
  if (!transcriptPath)
97338
98122
  return false;
97339
98123
  try {
97340
- if (!existsSync70(transcriptPath))
98124
+ if (!existsSync75(transcriptPath))
97341
98125
  return false;
97342
- const content = readFileSync46(transcriptPath, "utf-8");
98126
+ const content = readFileSync49(transcriptPath, "utf-8");
97343
98127
  const pattern = buildPromisePattern(promise);
97344
98128
  const lines = content.split(`
97345
98129
  `).filter((line) => line.trim());
@@ -98066,7 +98850,9 @@ function createRalphLoopEventHandlerImpl(ctx, options) {
98066
98850
  const runtimeRetryActivitySessionID = getRuntimeRetryActivitySessionID(event.type, props);
98067
98851
  if (runtimeRetryActivitySessionID) {
98068
98852
  runtime5.runtimeErrorRetriedSessions.delete(runtimeRetryActivitySessionID);
98069
- releasePromptAsyncReservation(runtimeRetryActivitySessionID, "ralph-loop");
98853
+ releasePromptAsyncReservation(runtimeRetryActivitySessionID, "ralph-loop", {
98854
+ supersedeTransientRetryOwners: true
98855
+ });
98070
98856
  runtime5.recentHandledSyntheticIdleAt.delete(runtimeRetryActivitySessionID);
98071
98857
  }
98072
98858
  if (event.type === "session.idle") {
@@ -98436,13 +99222,13 @@ function findSlashCommandPartIndex(parts) {
98436
99222
  return -1;
98437
99223
  }
98438
99224
  // packages/omo-opencode/src/hooks/auto-slash-command/executor.ts
98439
- import { dirname as dirname30 } from "path";
99225
+ import { dirname as dirname31 } from "path";
98440
99226
 
98441
99227
  // packages/omo-opencode/src/shared/command-executor/resolve-commands-in-text.ts
98442
99228
  init_resolve_commands_in_text();
98443
99229
  // packages/skills-loader-core/src/features/opencode-skill-loader/loader.ts
98444
99230
  init_src();
98445
- import { join as join90 } from "path";
99231
+ import { join as join93 } from "path";
98446
99232
 
98447
99233
  // packages/shared-skills/index.mjs
98448
99234
  import { fileURLToPath as fileURLToPath5 } from "url";
@@ -98452,21 +99238,21 @@ function sharedSkillsRootPath() {
98452
99238
 
98453
99239
  // packages/skills-loader-core/src/shared/claude-config-dir.ts
98454
99240
  init_src();
98455
- import { join as join84 } from "path";
99241
+ import { join as join87 } from "path";
98456
99242
  function getClaudeConfigDir2() {
98457
99243
  const envConfigDir = process.env.CLAUDE_CONFIG_DIR;
98458
99244
  if (envConfigDir) {
98459
99245
  return envConfigDir;
98460
99246
  }
98461
- return join84(getHomeDirectory(), ".claude");
99247
+ return join87(getHomeDirectory(), ".claude");
98462
99248
  }
98463
99249
  // packages/skills-loader-core/src/shared/opencode-command-dirs.ts
98464
- import { basename as basename13, dirname as dirname25, join as join86 } from "path";
99250
+ import { basename as basename14, dirname as dirname26, join as join89 } from "path";
98465
99251
 
98466
99252
  // packages/skills-loader-core/src/shared/opencode-config-dir.ts
98467
- import { existsSync as existsSync71, realpathSync as realpathSync12 } from "fs";
99253
+ import { existsSync as existsSync76, realpathSync as realpathSync12 } from "fs";
98468
99254
  import { homedir as homedir24 } from "os";
98469
- import { join as join85, posix as posix4, resolve as resolve19, win32 as win325 } from "path";
99255
+ import { join as join88, posix as posix4, resolve as resolve20, win32 as win325 } from "path";
98470
99256
 
98471
99257
  // packages/skills-loader-core/src/shared/plugin-identity.ts
98472
99258
  init_src();
@@ -98499,15 +99285,15 @@ function getTauriConfigDir2(identifier) {
98499
99285
  const platform2 = process.platform;
98500
99286
  switch (platform2) {
98501
99287
  case "darwin":
98502
- return join85(homedir24(), "Library", "Application Support", identifier);
99288
+ return join88(homedir24(), "Library", "Application Support", identifier);
98503
99289
  case "win32": {
98504
- const appData = process.env.APPDATA || join85(homedir24(), "AppData", "Roaming");
99290
+ const appData = process.env.APPDATA || join88(homedir24(), "AppData", "Roaming");
98505
99291
  return win325.join(appData, identifier);
98506
99292
  }
98507
99293
  case "linux":
98508
99294
  default: {
98509
- const xdgConfig = process.env.XDG_CONFIG_HOME || join85(homedir24(), ".config");
98510
- return join85(xdgConfig, identifier);
99295
+ const xdgConfig = process.env.XDG_CONFIG_HOME || join88(homedir24(), ".config");
99296
+ return join88(xdgConfig, identifier);
98511
99297
  }
98512
99298
  }
98513
99299
  }
@@ -98515,8 +99301,8 @@ function resolveConfigPath2(pathValue) {
98515
99301
  if (isWslEnvironment2() && pathValue.startsWith("/")) {
98516
99302
  return posix4.normalize(pathValue);
98517
99303
  }
98518
- const resolvedPath = resolve19(pathValue);
98519
- if (!existsSync71(resolvedPath))
99304
+ const resolvedPath = resolve20(pathValue);
99305
+ if (!existsSync76(resolvedPath))
98520
99306
  return resolvedPath;
98521
99307
  try {
98522
99308
  return realpathSync12(resolvedPath);
@@ -98550,8 +99336,8 @@ function getWslLinuxHomeDir2(windowsConfigRoot) {
98550
99336
  function getCliDefaultConfigDir2() {
98551
99337
  const envXdgConfig = process.env.XDG_CONFIG_HOME?.trim();
98552
99338
  const shouldIgnoreWindowsXdg = envXdgConfig !== undefined && envXdgConfig.length > 0 && isWslEnvironment2() && isWindowsUserConfigRoot2(envXdgConfig);
98553
- const xdgConfig = shouldIgnoreWindowsXdg ? posix4.join(getWslLinuxHomeDir2(envXdgConfig) ?? "/home", ".config") : envXdgConfig || join85(homedir24(), ".config");
98554
- const configDir = isWslEnvironment2() ? posix4.join(xdgConfig, "opencode") : join85(xdgConfig, "opencode");
99339
+ const xdgConfig = shouldIgnoreWindowsXdg ? posix4.join(getWslLinuxHomeDir2(envXdgConfig) ?? "/home", ".config") : envXdgConfig || join88(homedir24(), ".config");
99340
+ const configDir = isWslEnvironment2() ? posix4.join(xdgConfig, "opencode") : join88(xdgConfig, "opencode");
98555
99341
  return resolveConfigPath2(configDir);
98556
99342
  }
98557
99343
  function getCliCustomConfigDir2() {
@@ -98584,9 +99370,9 @@ function getOpenCodeConfigDir2(options) {
98584
99370
  const tauriDir = process.platform === "win32" ? win325.isAbsolute(tauriDirBase) ? win325.normalize(tauriDirBase) : win325.resolve(tauriDirBase) : resolveConfigPath2(tauriDirBase);
98585
99371
  if (checkExisting) {
98586
99372
  const legacyDir = getCliConfigDir2();
98587
- const legacyConfig = join85(legacyDir, "opencode.json");
98588
- const legacyConfigC = join85(legacyDir, "opencode.jsonc");
98589
- if (existsSync71(legacyConfig) || existsSync71(legacyConfigC)) {
99373
+ const legacyConfig = join88(legacyDir, "opencode.json");
99374
+ const legacyConfigC = join88(legacyDir, "opencode.jsonc");
99375
+ if (existsSync76(legacyConfig) || existsSync76(legacyConfigC)) {
98590
99376
  return legacyDir;
98591
99377
  }
98592
99378
  }
@@ -98595,11 +99381,11 @@ function getOpenCodeConfigDir2(options) {
98595
99381
 
98596
99382
  // packages/skills-loader-core/src/shared/opencode-command-dirs.ts
98597
99383
  function getParentOpencodeConfigDir2(configDir) {
98598
- const parentDir = dirname25(configDir);
98599
- if (basename13(parentDir) !== "profiles") {
99384
+ const parentDir = dirname26(configDir);
99385
+ if (basename14(parentDir) !== "profiles") {
98600
99386
  return null;
98601
99387
  }
98602
- return dirname25(parentDir);
99388
+ return dirname26(parentDir);
98603
99389
  }
98604
99390
  function getOpenCodeSkillDirs(options) {
98605
99391
  const configDirs = getOpenCodeConfigDirs2(options);
@@ -98607,21 +99393,21 @@ function getOpenCodeSkillDirs(options) {
98607
99393
  ...configDirs.flatMap((configDir) => {
98608
99394
  const parentConfigDir = getParentOpencodeConfigDir2(configDir);
98609
99395
  return [
98610
- join86(configDir, "skills"),
98611
- join86(configDir, "skill"),
98612
- ...parentConfigDir ? [join86(parentConfigDir, "skills"), join86(parentConfigDir, "skill")] : []
99396
+ join89(configDir, "skills"),
99397
+ join89(configDir, "skill"),
99398
+ ...parentConfigDir ? [join89(parentConfigDir, "skills"), join89(parentConfigDir, "skill")] : []
98613
99399
  ];
98614
99400
  })
98615
99401
  ]));
98616
99402
  }
98617
99403
  // packages/skills-loader-core/src/shared/project-discovery-dirs.ts
98618
99404
  import { execFileSync as execFileSync4 } from "child_process";
98619
- import { existsSync as existsSync72, realpathSync as realpathSync13 } from "fs";
98620
- import { dirname as dirname26, join as join87, resolve as resolve20, win32 as win326 } from "path";
99405
+ import { existsSync as existsSync77, realpathSync as realpathSync13 } from "fs";
99406
+ import { dirname as dirname27, join as join90, resolve as resolve21, win32 as win326 } from "path";
98621
99407
  var worktreePathCache2 = new Map;
98622
99408
  function normalizePath4(path17) {
98623
- const resolvedPath = process.platform !== "win32" && win326.isAbsolute(path17) ? path17 : resolve20(path17);
98624
- if (!existsSync72(resolvedPath)) {
99409
+ const resolvedPath = process.platform !== "win32" && win326.isAbsolute(path17) ? path17 : resolve21(path17);
99410
+ if (!existsSync77(resolvedPath)) {
98625
99411
  return resolvedPath;
98626
99412
  }
98627
99413
  try {
@@ -98652,8 +99438,8 @@ function findAncestorDirectories2(startDirectory, targetPaths, stopDirectory) {
98652
99438
  const stopDirectoryKey = resolvedStopDirectory ? pathKey2(resolvedStopDirectory) : undefined;
98653
99439
  while (true) {
98654
99440
  for (const targetPath of targetPaths) {
98655
- const candidateDirectory = join87(currentDirectory, ...targetPath);
98656
- if (!existsSync72(candidateDirectory)) {
99441
+ const candidateDirectory = join90(currentDirectory, ...targetPath);
99442
+ if (!existsSync77(candidateDirectory)) {
98657
99443
  continue;
98658
99444
  }
98659
99445
  const normalizedCandidateDirectory = normalizePath4(candidateDirectory);
@@ -98667,7 +99453,7 @@ function findAncestorDirectories2(startDirectory, targetPaths, stopDirectory) {
98667
99453
  if (stopDirectoryKey === pathKey2(currentDirectory)) {
98668
99454
  return directories;
98669
99455
  }
98670
- const parentDirectory = dirname26(currentDirectory);
99456
+ const parentDirectory = dirname27(currentDirectory);
98671
99457
  if (parentDirectory === currentDirectory) {
98672
99458
  return directories;
98673
99459
  }
@@ -98675,7 +99461,7 @@ function findAncestorDirectories2(startDirectory, targetPaths, stopDirectory) {
98675
99461
  }
98676
99462
  }
98677
99463
  function detectWorktreePath2(directory) {
98678
- const resolvedDirectory = resolve20(directory);
99464
+ const resolvedDirectory = resolve21(directory);
98679
99465
  const cacheKey = pathKey2(normalizePath4(resolvedDirectory));
98680
99466
  if (worktreePathCache2.has(cacheKey)) {
98681
99467
  return worktreePathCache2.get(cacheKey);
@@ -98711,7 +99497,7 @@ function findProjectOpencodeSkillDirs(startDirectory, stopDirectory) {
98711
99497
  ], stopDirectory ?? detectWorktreePath2(startDirectory));
98712
99498
  }
98713
99499
  // packages/skills-loader-core/src/shared/skill-path-resolver.ts
98714
- import { isAbsolute as isAbsolute12, posix as posix5, relative as relative11, resolve as resolve21, win32 as win327 } from "path";
99500
+ import { isAbsolute as isAbsolute13, posix as posix5, relative as relative12, resolve as resolve22, win32 as win327 } from "path";
98715
99501
  function toDisplayPath2(path17) {
98716
99502
  return path17.replaceAll("\\", "/");
98717
99503
  }
@@ -98750,9 +99536,9 @@ function resolveSkillPathReferences2(content, basePath) {
98750
99536
  }
98751
99537
  return relativePath.endsWith("/") && !resolvedPath2.endsWith("/") ? `${resolvedPath2}/` : resolvedPath2;
98752
99538
  }
98753
- const resolvedPath = resolve21(normalizedBase, relativePath);
98754
- const relativePathFromBase = relative11(normalizedBase, resolvedPath);
98755
- if (relativePathFromBase.startsWith("..") || isAbsolute12(relativePathFromBase)) {
99539
+ const resolvedPath = resolve22(normalizedBase, relativePath);
99540
+ const relativePathFromBase = relative12(normalizedBase, resolvedPath);
99541
+ if (relativePathFromBase.startsWith("..") || isAbsolute13(relativePathFromBase)) {
98756
99542
  return match;
98757
99543
  }
98758
99544
  const displayPath = toDisplayPath2(resolvedPath);
@@ -98875,12 +99661,12 @@ function deduplicateSkillsByName(skills) {
98875
99661
  // packages/skills-loader-core/src/features/opencode-skill-loader/skill-directory-loader.ts
98876
99662
  init_src();
98877
99663
  import * as fs19 from "fs/promises";
98878
- import { join as join89 } from "path";
99664
+ import { join as join92 } from "path";
98879
99665
 
98880
99666
  // packages/skills-loader-core/src/features/opencode-skill-loader/loaded-skill-from-path.ts
98881
99667
  init_src();
98882
99668
  import * as fs18 from "fs/promises";
98883
- import { basename as basename14 } from "path";
99669
+ import { basename as basename15 } from "path";
98884
99670
 
98885
99671
  // packages/skills-loader-core/src/features/opencode-skill-loader/allowed-tools-parser.ts
98886
99672
  function parseAllowedTools(allowedTools) {
@@ -98895,7 +99681,7 @@ function parseAllowedTools(allowedTools) {
98895
99681
  // packages/skills-loader-core/src/features/opencode-skill-loader/skill-mcp-config.ts
98896
99682
  init_js_yaml();
98897
99683
  import * as fs17 from "fs/promises";
98898
- import { join as join88 } from "path";
99684
+ import { join as join91 } from "path";
98899
99685
  function parseSkillMcpConfigFromFrontmatter(content) {
98900
99686
  const frontmatterMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
98901
99687
  if (!frontmatterMatch)
@@ -98914,7 +99700,7 @@ function parseSkillMcpConfigFromFrontmatter(content) {
98914
99700
  return;
98915
99701
  }
98916
99702
  async function loadMcpJsonFromDir(skillDir) {
98917
- const mcpJsonPath = join88(skillDir, "mcp.json");
99703
+ const mcpJsonPath = join91(skillDir, "mcp.json");
98918
99704
  try {
98919
99705
  const content = await fs17.readFile(mcpJsonPath, "utf-8");
98920
99706
  const parsed = JSON.parse(content);
@@ -98996,7 +99782,7 @@ $ARGUMENTS
98996
99782
  }
98997
99783
  }
98998
99784
  function inferSkillNameFromFileName(filePath) {
98999
- return basename14(filePath, ".md");
99785
+ return basename15(filePath, ".md");
99000
99786
  }
99001
99787
 
99002
99788
  // packages/skills-loader-core/src/features/opencode-skill-loader/skill-directory-loader.ts
@@ -99030,10 +99816,10 @@ async function loadSkillsFromDir(options) {
99030
99816
  const directories = entries.filter((entry) => !entry.name.startsWith(".") && (entry.isDirectory() || entry.isSymbolicLink()));
99031
99817
  const files = entries.filter((entry) => !entry.name.startsWith(".") && !entry.isDirectory() && !entry.isSymbolicLink() && isMarkdownFile(entry));
99032
99818
  for (const entry of directories) {
99033
- const entryPath = join89(options.skillsDir, entry.name);
99819
+ const entryPath = join92(options.skillsDir, entry.name);
99034
99820
  const resolvedPath = await resolveSymlinkAsync(entryPath);
99035
99821
  const dirName = entry.name;
99036
- const skillMdPath = join89(resolvedPath, "SKILL.md");
99822
+ const skillMdPath = join92(resolvedPath, "SKILL.md");
99037
99823
  if (await canAccessFile(skillMdPath)) {
99038
99824
  const skill = await loadSkillFromPath({
99039
99825
  skillPath: skillMdPath,
@@ -99047,7 +99833,7 @@ async function loadSkillsFromDir(options) {
99047
99833
  }
99048
99834
  continue;
99049
99835
  }
99050
- const namedSkillMdPath = join89(resolvedPath, `${dirName}.md`);
99836
+ const namedSkillMdPath = join92(resolvedPath, `${dirName}.md`);
99051
99837
  if (await canAccessFile(namedSkillMdPath)) {
99052
99838
  const skill = await loadSkillFromPath({
99053
99839
  skillPath: namedSkillMdPath,
@@ -99078,7 +99864,7 @@ async function loadSkillsFromDir(options) {
99078
99864
  }
99079
99865
  }
99080
99866
  for (const entry of files) {
99081
- const entryPath = join89(options.skillsDir, entry.name);
99867
+ const entryPath = join92(options.skillsDir, entry.name);
99082
99868
  const baseName = inferSkillNameFromFileName(entryPath);
99083
99869
  const skill = await loadSkillFromPath({
99084
99870
  skillPath: entryPath,
@@ -99096,7 +99882,7 @@ async function loadSkillsFromDir(options) {
99096
99882
 
99097
99883
  // packages/skills-loader-core/src/features/opencode-skill-loader/loader.ts
99098
99884
  async function loadUserSkills() {
99099
- const userSkillsDir = join90(getClaudeConfigDir2(), "skills");
99885
+ const userSkillsDir = join93(getClaudeConfigDir2(), "skills");
99100
99886
  const skills = await loadSkillsFromDir({ skillsDir: userSkillsDir, scope: "user" });
99101
99887
  return skillsToCommandDefinitionRecord(skills);
99102
99888
  }
@@ -99121,7 +99907,7 @@ async function loadProjectAgentsSkills(directory) {
99121
99907
  return skillsToCommandDefinitionRecord(deduplicateSkillsByName(allSkills.flat()));
99122
99908
  }
99123
99909
  async function loadGlobalAgentsSkills(homeDirectory = getHomeDirectory()) {
99124
- const agentsGlobalDir = join90(homeDirectory, ".agents", "skills");
99910
+ const agentsGlobalDir = join93(homeDirectory, ".agents", "skills");
99125
99911
  const skills = await loadSkillsFromDir({ skillsDir: agentsGlobalDir, scope: "user" });
99126
99912
  return skillsToCommandDefinitionRecord(skills);
99127
99913
  }
@@ -99192,7 +99978,7 @@ async function discoverSkills(options = {}) {
99192
99978
  ]);
99193
99979
  }
99194
99980
  async function discoverUserClaudeSkills() {
99195
- const userSkillsDir = join90(getClaudeConfigDir2(), "skills");
99981
+ const userSkillsDir = join93(getClaudeConfigDir2(), "skills");
99196
99982
  return loadSkillsFromDir({ skillsDir: userSkillsDir, scope: "user" });
99197
99983
  }
99198
99984
  async function discoverProjectClaudeSkills(directory) {
@@ -99219,17 +100005,17 @@ async function discoverProjectAgentsSkills(directory) {
99219
100005
  return deduplicateSkillsByName(allSkills.flat());
99220
100006
  }
99221
100007
  async function discoverGlobalAgentsSkills(homeDirectory = getHomeDirectory()) {
99222
- const agentsGlobalDir = join90(homeDirectory, ".agents", "skills");
100008
+ const agentsGlobalDir = join93(homeDirectory, ".agents", "skills");
99223
100009
  return loadSkillsFromDir({ skillsDir: agentsGlobalDir, scope: "user" });
99224
100010
  }
99225
100011
  // packages/skills-loader-core/src/features/opencode-skill-loader/merger/builtin-skill-converter.ts
99226
- import { existsSync as existsSync73 } from "fs";
99227
- import { join as join91 } from "path";
100012
+ import { existsSync as existsSync78 } from "fs";
100013
+ import { join as join94 } from "path";
99228
100014
  function resolveBuiltinSkillPath(builtin) {
99229
100015
  if (builtin.resolvedPath !== undefined)
99230
100016
  return builtin.resolvedPath;
99231
- const sharedSkillPath = join91(sharedSkillsRootPath(), builtin.name);
99232
- return existsSync73(sharedSkillPath) ? sharedSkillPath : undefined;
100017
+ const sharedSkillPath = join94(sharedSkillsRootPath(), builtin.name);
100018
+ return existsSync78(sharedSkillPath) ? sharedSkillPath : undefined;
99233
100019
  }
99234
100020
  function builtinToLoadedSkill(builtin) {
99235
100021
  const definition = {
@@ -99256,8 +100042,8 @@ function builtinToLoadedSkill(builtin) {
99256
100042
 
99257
100043
  // packages/skills-loader-core/src/features/opencode-skill-loader/merger/config-skill-entry-loader.ts
99258
100044
  init_src();
99259
- import { existsSync as existsSync74, readFileSync as readFileSync47 } from "fs";
99260
- import { dirname as dirname27, isAbsolute as isAbsolute13, resolve as resolve22 } from "path";
100045
+ import { existsSync as existsSync79, readFileSync as readFileSync50 } from "fs";
100046
+ import { dirname as dirname28, isAbsolute as isAbsolute14, resolve as resolve23 } from "path";
99261
100047
  import { homedir as homedir25 } from "os";
99262
100048
  function resolveFilePath5(from, configDir) {
99263
100049
  let filePath = from;
@@ -99265,19 +100051,19 @@ function resolveFilePath5(from, configDir) {
99265
100051
  filePath = filePath.slice(6, -1);
99266
100052
  }
99267
100053
  if (filePath.startsWith("~/")) {
99268
- return resolve22(homedir25(), filePath.slice(2));
100054
+ return resolve23(homedir25(), filePath.slice(2));
99269
100055
  }
99270
- if (isAbsolute13(filePath)) {
100056
+ if (isAbsolute14(filePath)) {
99271
100057
  return filePath;
99272
100058
  }
99273
100059
  const baseDir = configDir || process.cwd();
99274
- return resolve22(baseDir, filePath);
100060
+ return resolve23(baseDir, filePath);
99275
100061
  }
99276
100062
  function loadSkillFromFile(filePath) {
99277
100063
  try {
99278
- if (!existsSync74(filePath))
100064
+ if (!existsSync79(filePath))
99279
100065
  return null;
99280
- const content = readFileSync47(filePath, "utf-8");
100066
+ const content = readFileSync50(filePath, "utf-8");
99281
100067
  const { data, body } = parseFrontmatter(content);
99282
100068
  return { template: body, metadata: data };
99283
100069
  } catch (error) {
@@ -99313,7 +100099,7 @@ function configEntryToLoadedSkill(name, entry, configDir) {
99313
100099
  return null;
99314
100100
  }
99315
100101
  const description = entry.description || fileMetadata.description || "";
99316
- const resolvedPath = sourcePath ? dirname27(sourcePath) : configDir || process.cwd();
100102
+ const resolvedPath = sourcePath ? dirname28(sourcePath) : configDir || process.cwd();
99317
100103
  const resolvedTemplate = resolveSkillPathReferences2(template.trim(), resolvedPath);
99318
100104
  const wrappedTemplate = `<skill-instruction>
99319
100105
  Base directory for this skill: ${resolvedPath}/
@@ -100143,17 +100929,17 @@ playwright-cli close
100143
100929
  allowedTools: ["Bash(playwright-cli:*)"]
100144
100930
  };
100145
100931
  // packages/skills-loader-core/src/features/builtin-skills/skill-file-loader.ts
100146
- import { readFileSync as readFileSync48 } from "fs";
100147
- import { join as join92 } from "path";
100932
+ import { readFileSync as readFileSync51 } from "fs";
100933
+ import { join as join95 } from "path";
100148
100934
  init_src();
100149
- function createSharedSkillTemplateLoader(readFile9 = readFileSync48, skillsRootPath = sharedSkillsRootPath()) {
100935
+ function createSharedSkillTemplateLoader(readFile9 = readFileSync51, skillsRootPath = sharedSkillsRootPath()) {
100150
100936
  const cache2 = new Map;
100151
100937
  return (skillName) => {
100152
100938
  const cached = cache2.get(skillName);
100153
100939
  if (cached !== undefined)
100154
100940
  return cached;
100155
100941
  try {
100156
- const { body } = parseFrontmatter(readFile9(join92(skillsRootPath, skillName, "SKILL.md"), "utf8"));
100942
+ const { body } = parseFrontmatter(readFile9(join95(skillsRootPath, skillName, "SKILL.md"), "utf8"));
100157
100943
  cache2.set(skillName, body);
100158
100944
  return body;
100159
100945
  } catch (error) {
@@ -101290,9 +102076,9 @@ var gitMasterSkill = {
101290
102076
  template: GIT_MASTER_TEMPLATE
101291
102077
  };
101292
102078
  // packages/skills-loader-core/src/features/builtin-skills/skills/dev-browser.ts
101293
- import { dirname as dirname28, join as join93 } from "path";
102079
+ import { dirname as dirname29, join as join96 } from "path";
101294
102080
  import { fileURLToPath as fileURLToPath6 } from "url";
101295
- var CURRENT_DIR = dirname28(fileURLToPath6(import.meta.url));
102081
+ var CURRENT_DIR = dirname29(fileURLToPath6(import.meta.url));
101296
102082
  var devBrowserSkill = {
101297
102083
  name: "dev-browser",
101298
102084
  description: "Browser automation with persistent page state. Use when users ask to navigate websites, fill forms, take screenshots, extract web data, test web apps, or automate browser workflows. Trigger phrases include 'go to [url]', 'click on', 'fill out the form', 'take a screenshot', 'scrape', 'automate', 'test the website', 'log into', or any browser interaction request.",
@@ -101510,7 +102296,7 @@ console.log({
101510
102296
  await client.disconnect();
101511
102297
  EOF
101512
102298
  \`\`\``,
101513
- resolvedPath: join93(CURRENT_DIR, "..", "dev-browser")
102299
+ resolvedPath: join96(CURRENT_DIR, "..", "dev-browser")
101514
102300
  };
101515
102301
  // packages/skills-loader-core/src/features/builtin-skills/skills/review-work.ts
101516
102302
  var reviewWorkSkill = {
@@ -102187,13 +102973,13 @@ function mergeSkills(builtinSkills, config, configSourceSkills, userClaudeSkills
102187
102973
  }
102188
102974
  // packages/skills-loader-core/src/features/opencode-skill-loader/loaded-skill-template-extractor.ts
102189
102975
  init_src();
102190
- import { readFileSync as readFileSync49 } from "fs";
102976
+ import { readFileSync as readFileSync52 } from "fs";
102191
102977
  function extractSkillTemplate(skill) {
102192
102978
  if (skill.scope === "config" && skill.definition.template) {
102193
102979
  return skill.definition.template;
102194
102980
  }
102195
102981
  if (skill.path) {
102196
- const content = readFileSync49(skill.path, "utf-8");
102982
+ const content = readFileSync52(skill.path, "utf-8");
102197
102983
  const { body } = parseFrontmatter(content);
102198
102984
  return body.trim();
102199
102985
  }
@@ -102510,7 +103296,7 @@ init_src();
102510
103296
  var import_picomatch2 = __toESM(require_picomatch2(), 1);
102511
103297
  import * as fs20 from "fs/promises";
102512
103298
  import { homedir as homedir26 } from "os";
102513
- import { dirname as dirname29, extname as extname2, isAbsolute as isAbsolute14, join as join94, relative as relative12 } from "path";
103299
+ import { dirname as dirname30, extname as extname2, isAbsolute as isAbsolute15, join as join97, relative as relative13 } from "path";
102514
103300
  var MAX_RECURSIVE_DEPTH = 10;
102515
103301
  function isHttpUrl(path17) {
102516
103302
  return path17.startsWith("http://") || path17.startsWith("https://");
@@ -102520,12 +103306,12 @@ function toAbsolutePath(path17, configDir) {
102520
103306
  return homedir26();
102521
103307
  }
102522
103308
  if (path17.startsWith("~/")) {
102523
- return join94(homedir26(), path17.slice(2));
103309
+ return join97(homedir26(), path17.slice(2));
102524
103310
  }
102525
- if (isAbsolute14(path17)) {
103311
+ if (isAbsolute15(path17)) {
102526
103312
  return path17;
102527
103313
  }
102528
- return join94(configDir, path17);
103314
+ return join97(configDir, path17);
102529
103315
  }
102530
103316
  function isMarkdownPath(path17) {
102531
103317
  return extname2(path17).toLowerCase() === ".md";
@@ -102539,7 +103325,7 @@ function filterByGlob(skills, sourceBaseDir, globPattern) {
102539
103325
  return skills.filter((skill) => {
102540
103326
  if (!skill.path)
102541
103327
  return false;
102542
- const rel = normalizePathForGlob(relative12(sourceBaseDir, skill.path));
103328
+ const rel = normalizePathForGlob(relative13(sourceBaseDir, skill.path));
102543
103329
  return import_picomatch2.default.isMatch(rel, globPattern, { dot: true, bash: true });
102544
103330
  });
102545
103331
  }
@@ -102565,13 +103351,13 @@ async function loadSourcePath(options) {
102565
103351
  return [];
102566
103352
  const loaded = await loadSkillFromPath({
102567
103353
  skillPath: realSourcePath,
102568
- resolvedPath: dirname29(realSourcePath),
103354
+ resolvedPath: dirname30(realSourcePath),
102569
103355
  defaultName: inferSkillNameFromFileName(realSourcePath),
102570
103356
  scope: "config"
102571
103357
  });
102572
103358
  if (!loaded)
102573
103359
  return [];
102574
- return filterByGlob([loaded], dirname29(realSourcePath), options.globPattern);
103360
+ return filterByGlob([loaded], dirname30(realSourcePath), options.globPattern);
102575
103361
  }
102576
103362
  if (!stat5.isDirectory())
102577
103363
  return [];
@@ -102652,8 +103438,8 @@ function readOpencodeConfigSkills(directory) {
102652
103438
  return { paths, urls };
102653
103439
  }
102654
103440
  // packages/omo-opencode/src/tools/slashcommand/command-discovery.ts
102655
- import { existsSync as existsSync76, readdirSync as readdirSync19, readFileSync as readFileSync51, statSync as statSync10 } from "fs";
102656
- import { basename as basename15, join as join96 } from "path";
103441
+ import { existsSync as existsSync81, readdirSync as readdirSync20, readFileSync as readFileSync54, statSync as statSync11 } from "fs";
103442
+ import { basename as basename16, join as join99 } from "path";
102657
103443
 
102658
103444
  // packages/omo-opencode/src/tools/slashcommand/command-discovery-deps.ts
102659
103445
  init_frontmatter2();
@@ -102893,13 +103679,15 @@ This command includes examples for the OpenCode harness. In Codex, do not call O
102893
103679
 
102894
103680
  | OpenCode example | Codex tool to use |
102895
103681
  | --- | --- |
102896
- | \`call_omo_agent(subagent_type="explore", ...)\` | \`spawn_agent(agent_type="explorer", task_name="...", message="...")\` |
102897
- | \`call_omo_agent(subagent_type="librarian", ...)\` | \`spawn_agent(agent_type="librarian", task_name="...", message="...")\` |
102898
- | \`task(subagent_type="plan", ...)\` | \`spawn_agent(agent_type="plan", task_name="...", message="...")\` |
103682
+ | \`call_omo_agent(subagent_type="explore", ...)\` | \`multi_agent_v1.spawn_agent({"message":"TASK: act as an explorer. ...","agent_type":"explorer","fork_context":false})\` |
103683
+ | \`call_omo_agent(subagent_type="librarian", ...)\` | \`multi_agent_v1.spawn_agent({"message":"TASK: act as a librarian. ...","agent_type":"librarian","fork_context":false})\` |
103684
+ | \`task(subagent_type="plan", ...)\` | \`multi_agent_v1.spawn_agent({"message":"TASK: act as a planning agent. ...","agent_type":"plan","fork_context":false})\` |
102899
103685
  | \`task(subagent_type="oracle", ...)\` | \`multi_agent_v1.spawn_agent({"message":"TASK: act as a rigorous reviewer. ...","agent_type":"lazycodex-gate-reviewer","fork_context":false})\` |
102900
- | \`task(category="...", ...)\` | \`spawn_agent(agent_type="worker", task_name="...", message="...")\` |
102901
- | \`background_output(task_id="...")\` | \`wait_agent(...)\` to wait for subagent completion and mailbox updates |
102902
- | \`team_*(...)\` | Use Codex native subagents plus \`send_message\`, \`followup_task\`, \`wait_agent\`, and \`close_agent\` |
103686
+ | \`task(category="...", ...)\` | \`multi_agent_v1.spawn_agent({"message":"TASK: act as an implementation or QA worker. ...","fork_context":false})\` |
103687
+ | \`background_output(task_id="...")\` | \`multi_agent_v1.wait_agent(...)\` for mailbox signals |
103688
+ | \`team_*(...)\` | Use Codex native subagents via \`multi_agent_v1.spawn_agent\`, \`multi_agent_v1.send_input\`, \`multi_agent_v1.wait_agent\`, and \`multi_agent_v1.close_agent\` |
103689
+
103690
+ Codex exposes ONE of two subagent tool surfaces per session; check your own tool list and route accordingly. If \`multi_agent_v1.*\` tools exist, use the table above as written. If instead a flat \`spawn_agent\` with a required \`task_name\` exists (\`multi_agent_v2\`), rewrite every \`multi_agent_v1.*\` example: \`multi_agent_v1.spawn_agent({...,"fork_context":false})\` becomes \`spawn_agent({"task_name":"<lowercase_digits_underscores>","message":...,"agent_type":...,"fork_turns":"none"})\` (\`"all"\` only when full parent history is truly required); \`send_input\` becomes \`send_message\`; do not call \`close_agent\`/\`resume_agent\` (finished agents end on their own; \`followup_task\` re-tasks one, \`interrupt_agent\` stops one); \`wait_agent\` takes only \`timeout_ms\` and returns on any child mailbox activity. \`agent_type\` works the same on both surfaces. If a code block below conflicts with this section, this section wins.
102903
103691
 
102904
103692
  When translating \`load_skills=[...]\`, include the requested skill names in the spawned agent's \`message\`. If a code block below conflicts with this section, this section wins.
102905
103693
 
@@ -103523,7 +104311,7 @@ Append to the 6.6 summary a "Dispatch path" line and, when team path was used, t
103523
104311
  var REFACTOR_TEMPLATE = REFACTOR_INTRO_AND_ANALYSIS + REFACTOR_CODEMAP_AND_TESTS + REFACTOR_PLAN_AND_EXECUTION + REFACTOR_VERIFICATION_AND_TOOLING;
103524
104312
 
103525
104313
  // packages/omo-opencode/src/features/builtin-commands/templates/start-work.ts
103526
- var START_WORK_TEMPLATE = `You are starting a Sisyphus work session.
104314
+ var START_WORK_TEMPLATE = `You are starting an Atlas work session.
103527
104315
 
103528
104316
  ## ARGUMENTS
103529
104317
 
@@ -103863,13 +104651,15 @@ This command includes examples for the OpenCode harness. In Codex, do not call O
103863
104651
 
103864
104652
  | OpenCode example | Codex tool to use |
103865
104653
  | --- | --- |
103866
- | \`call_omo_agent(subagent_type="explore", ...)\` | \`spawn_agent(agent_type="explorer", task_name="...", message="...")\` |
103867
- | \`call_omo_agent(subagent_type="librarian", ...)\` | \`spawn_agent(agent_type="librarian", task_name="...", message="...")\` |
103868
- | \`task(subagent_type="plan", ...)\` | \`spawn_agent(agent_type="plan", task_name="...", message="...")\` |
104654
+ | \`call_omo_agent(subagent_type="explore", ...)\` | \`multi_agent_v1.spawn_agent({"message":"TASK: act as an explorer. ...","agent_type":"explorer","fork_context":false})\` |
104655
+ | \`call_omo_agent(subagent_type="librarian", ...)\` | \`multi_agent_v1.spawn_agent({"message":"TASK: act as a librarian. ...","agent_type":"librarian","fork_context":false})\` |
104656
+ | \`task(subagent_type="plan", ...)\` | \`multi_agent_v1.spawn_agent({"message":"TASK: act as a planning agent. ...","agent_type":"plan","fork_context":false})\` |
103869
104657
  | \`task(subagent_type="oracle", ...)\` | \`multi_agent_v1.spawn_agent({"message":"TASK: act as a rigorous reviewer. ...","agent_type":"lazycodex-gate-reviewer","fork_context":false})\` |
103870
- | \`task(category="...", ...)\` | \`spawn_agent(agent_type="worker", task_name="...", message="...")\` |
103871
- | \`background_output(task_id="...")\` | \`wait_agent(...)\` to wait for subagent completion and mailbox updates |
103872
- | \`team_*(...)\` | Use Codex native subagents plus \`send_message\`, \`followup_task\`, \`wait_agent\`, and \`close_agent\` |
104658
+ | \`task(category="...", ...)\` | \`multi_agent_v1.spawn_agent({"message":"TASK: act as an implementation or QA worker. ...","fork_context":false})\` |
104659
+ | \`background_output(task_id="...")\` | \`multi_agent_v1.wait_agent(...)\` for mailbox signals |
104660
+ | \`team_*(...)\` | Use Codex native subagents via \`multi_agent_v1.spawn_agent\`, \`multi_agent_v1.send_input\`, \`multi_agent_v1.wait_agent\`, and \`multi_agent_v1.close_agent\` |
104661
+
104662
+ Codex exposes ONE of two subagent tool surfaces per session; check your own tool list and route accordingly. If \`multi_agent_v1.*\` tools exist, use the table above as written. If instead a flat \`spawn_agent\` with a required \`task_name\` exists (\`multi_agent_v2\`), rewrite every \`multi_agent_v1.*\` example: \`multi_agent_v1.spawn_agent({...,"fork_context":false})\` becomes \`spawn_agent({"task_name":"<lowercase_digits_underscores>","message":...,"agent_type":...,"fork_turns":"none"})\` (\`"all"\` only when full parent history is truly required); \`send_input\` becomes \`send_message\`; do not call \`close_agent\`/\`resume_agent\` (finished agents end on their own; \`followup_task\` re-tasks one, \`interrupt_agent\` stops one); \`wait_agent\` takes only \`timeout_ms\` and returns on any child mailbox activity. \`agent_type\` works the same on both surfaces. If a code block below conflicts with this section, this section wins.
103873
104663
 
103874
104664
  When translating \`load_skills=[...]\`, include the requested skill names in the spawned agent's \`message\`. If a code block below conflicts with this section, this section wins.
103875
104665
 
@@ -104140,7 +104930,7 @@ ${refactorContent}
104140
104930
  argumentHint: "<refactoring-target> [--scope=<file|module|project>] [--strategy=<safe|aggressive>]"
104141
104931
  },
104142
104932
  "start-work": {
104143
- description: "(builtin) Start Sisyphus work session from Prometheus plan",
104933
+ description: "(builtin) Start Atlas work session from Prometheus plan",
104144
104934
  agent: resolveStartWorkAgent(options),
104145
104935
  template: `<command-instruction>
104146
104936
  ${START_WORK_TEMPLATE}
@@ -104213,13 +105003,13 @@ function loadBuiltinCommands(disabledCommands, options) {
104213
105003
  // packages/omo-opencode/src/tools/slashcommand/command-discovery.ts
104214
105004
  var NESTED_COMMAND_SEPARATOR = "/";
104215
105005
  function discoverCommandsFromDir(commandsDir, scope, prefix = "") {
104216
- if (!existsSync76(commandsDir))
105006
+ if (!existsSync81(commandsDir))
104217
105007
  return [];
104218
- if (!statSync10(commandsDir).isDirectory()) {
105008
+ if (!statSync11(commandsDir).isDirectory()) {
104219
105009
  log2(`[command-discovery] Skipping non-directory path: ${commandsDir}`);
104220
105010
  return [];
104221
105011
  }
104222
- const entries = readdirSync19(commandsDir, { withFileTypes: true });
105012
+ const entries = readdirSync20(commandsDir, { withFileTypes: true });
104223
105013
  const commands = [];
104224
105014
  for (const entry of entries) {
104225
105015
  if (entry.isDirectory()) {
@@ -104228,16 +105018,16 @@ function discoverCommandsFromDir(commandsDir, scope, prefix = "") {
104228
105018
  if (entry.name.startsWith("."))
104229
105019
  continue;
104230
105020
  const nestedPrefix = prefix ? `${prefix}${NESTED_COMMAND_SEPARATOR}${entry.name}` : entry.name;
104231
- commands.push(...discoverCommandsFromDir(join96(commandsDir, entry.name), scope, nestedPrefix));
105021
+ commands.push(...discoverCommandsFromDir(join99(commandsDir, entry.name), scope, nestedPrefix));
104232
105022
  continue;
104233
105023
  }
104234
105024
  if (!isMarkdownFile(entry))
104235
105025
  continue;
104236
- const commandPath = join96(commandsDir, entry.name);
104237
- const baseCommandName = basename15(entry.name, ".md");
105026
+ const commandPath = join99(commandsDir, entry.name);
105027
+ const baseCommandName = basename16(entry.name, ".md");
104238
105028
  const commandName = prefix ? `${prefix}${NESTED_COMMAND_SEPARATOR}${baseCommandName}` : baseCommandName;
104239
105029
  try {
104240
- const content = readFileSync51(commandPath, "utf-8");
105030
+ const content = readFileSync54(commandPath, "utf-8");
104241
105031
  const { data, body } = parseFrontmatter(content);
104242
105032
  const isOpencodeSource = scope === "opencode" || scope === "opencode-project";
104243
105033
  const metadata = {
@@ -104292,8 +105082,8 @@ function deduplicateCommandInfosByName(commands) {
104292
105082
  return deduplicatedCommands;
104293
105083
  }
104294
105084
  function discoverCommandsSync(directory, options) {
104295
- const userCommandsDir = join96(getClaudeConfigDir(), "commands");
104296
- const projectCommandsDir = join96(directory ?? process.cwd(), ".claude", "commands");
105085
+ const userCommandsDir = join99(getClaudeConfigDir(), "commands");
105086
+ const projectCommandsDir = join99(directory ?? process.cwd(), ".claude", "commands");
104297
105087
  const opencodeGlobalDirs = getOpenCodeCommandDirs({ binary: "opencode" });
104298
105088
  const opencodeProjectDirs = findProjectOpencodeCommandDirs(directory ?? process.cwd());
104299
105089
  const userCommands = discoverCommandsFromDir(userCommandsDir, "user");
@@ -104397,7 +105187,7 @@ async function formatCommandTemplate(cmd, args) {
104397
105187
  if (!content && cmd.lazyContentLoader) {
104398
105188
  content = await cmd.lazyContentLoader.load();
104399
105189
  }
104400
- const commandDir = cmd.path ? dirname30(cmd.path) : process.cwd();
105190
+ const commandDir = cmd.path ? dirname31(cmd.path) : process.cwd();
104401
105191
  const withFileRefs = await resolveFileReferencesInText(content, commandDir);
104402
105192
  const resolvedContent = await resolveCommandsInText(withFileRefs);
104403
105193
  const resolvedArguments = args;
@@ -104773,757 +105563,7 @@ If you skipped steps, STOP NOW. Go back and complete them.
104773
105563
  `;
104774
105564
  // packages/omo-opencode/src/hooks/prometheus-md-only/hook.ts
104775
105565
  init_logger2();
104776
- // packages/boulder-state/src/constants.ts
104777
- var BOULDER_DIR = ".omo";
104778
- var BOULDER_FILE = "boulder.json";
104779
- var BOULDER_STATE_PATH = `${BOULDER_DIR}/${BOULDER_FILE}`;
104780
- var NOTEPAD_DIR = "notepads";
104781
- var NOTEPAD_BASE_PATH = `${BOULDER_DIR}/${NOTEPAD_DIR}`;
104782
- var PROMETHEUS_PLANS_DIR = ".omo/plans";
104783
- // packages/boulder-state/src/top-level-task.ts
104784
- import { existsSync as existsSync77, readFileSync as readFileSync52 } from "fs";
104785
- var TODO_HEADING_PATTERN = /^##\s+TODOs\b/i;
104786
- var FINAL_VERIFICATION_HEADING_PATTERN = /^##\s+Final Verification Wave\b/i;
104787
- var SECOND_LEVEL_HEADING_PATTERN = /^##\s+/;
104788
- var UNCHECKED_CHECKBOX_PATTERN = /^(\s*)[-*]\s*\[\s*\]\s*(.+)$/;
104789
- var TODO_TASK_PATTERN = /^(\d+)\.\s+(.+)$/;
104790
- var FINAL_WAVE_TASK_PATTERN = /^(F\d+)\.\s+(.+)$/i;
104791
- function buildTaskRef(section, taskLabel) {
104792
- const pattern = section === "todo" ? TODO_TASK_PATTERN : FINAL_WAVE_TASK_PATTERN;
104793
- const match = taskLabel.match(pattern);
104794
- if (!match) {
104795
- return null;
104796
- }
104797
- const rawLabel = match[1];
104798
- const title = match[2].trim();
104799
- return {
104800
- key: `${section}:${rawLabel.toLowerCase()}`,
104801
- section,
104802
- label: rawLabel,
104803
- title
104804
- };
104805
- }
104806
- function readCurrentTopLevelTask(planPath) {
104807
- if (!existsSync77(planPath)) {
104808
- return null;
104809
- }
104810
- try {
104811
- const content = readFileSync52(planPath, "utf-8");
104812
- const lines = content.split(/\r?\n/);
104813
- let section = "other";
104814
- for (const line of lines) {
104815
- if (SECOND_LEVEL_HEADING_PATTERN.test(line)) {
104816
- section = TODO_HEADING_PATTERN.test(line) ? "todo" : FINAL_VERIFICATION_HEADING_PATTERN.test(line) ? "final-wave" : "other";
104817
- }
104818
- const uncheckedTaskMatch = line.match(UNCHECKED_CHECKBOX_PATTERN);
104819
- if (!uncheckedTaskMatch || uncheckedTaskMatch[1].length > 0) {
104820
- continue;
104821
- }
104822
- if (section !== "todo" && section !== "final-wave") {
104823
- continue;
104824
- }
104825
- const taskRef = buildTaskRef(section, uncheckedTaskMatch[2].trim());
104826
- if (taskRef) {
104827
- return taskRef;
104828
- }
104829
- }
104830
- return null;
104831
- } catch {
104832
- return null;
104833
- }
104834
- }
104835
- // packages/boulder-state/src/storage/path.ts
104836
- import { existsSync as existsSync78 } from "fs";
104837
- import { isAbsolute as isAbsolute15, join as join97, relative as relative13, resolve as resolve23 } from "path";
104838
- function getBoulderFilePath(directory) {
104839
- return join97(directory, BOULDER_DIR, BOULDER_FILE);
104840
- }
104841
- function resolveTrackedPath(baseDirectory, trackedPath) {
104842
- return isAbsolute15(trackedPath) ? resolve23(trackedPath) : resolve23(baseDirectory, trackedPath);
104843
- }
104844
- function resolveBoulderPlanPath(directory, state3) {
104845
- const absolutePlanPath = resolveTrackedPath(directory, state3.active_plan);
104846
- const worktreePath = state3.worktree_path?.trim();
104847
- if (!worktreePath) {
104848
- return absolutePlanPath;
104849
- }
104850
- const absoluteDirectory = resolve23(directory);
104851
- const relativePlanPath = relative13(absoluteDirectory, absolutePlanPath);
104852
- if (relativePlanPath.length === 0 || relativePlanPath.startsWith("..") || isAbsolute15(relativePlanPath)) {
104853
- return absolutePlanPath;
104854
- }
104855
- const absoluteWorktreePath = resolveTrackedPath(directory, worktreePath);
104856
- const worktreePlanPath = resolve23(absoluteWorktreePath, relativePlanPath);
104857
- return existsSync78(worktreePlanPath) ? worktreePlanPath : absolutePlanPath;
104858
- }
104859
- function resolveBoulderPlanPathForWork(directory, work) {
104860
- return resolveBoulderPlanPath(directory, work);
104861
- }
104862
- // packages/boulder-state/src/storage/plan-progress.ts
104863
- import { existsSync as existsSync79, readFileSync as readFileSync53, readdirSync as readdirSync20, statSync as statSync11 } from "fs";
104864
- import { basename as basename16, join as join98 } from "path";
104865
- var TODO_HEADING_PATTERN2 = /^##\s+TODOs\b/i;
104866
- var FINAL_VERIFICATION_HEADING_PATTERN2 = /^##\s+Final Verification Wave\b/i;
104867
- var SECOND_LEVEL_HEADING_PATTERN2 = /^##\s+/;
104868
- var UNCHECKED_CHECKBOX_PATTERN2 = /^(\s*)[-*]\s*\[\s*\]\s*(.+)$/;
104869
- var CHECKED_CHECKBOX_PATTERN = /^(\s*)[-*]\s*\[[xX]\]\s*(.+)$/;
104870
- var TODO_TASK_PATTERN2 = /^\d+\.\s+/;
104871
- var FINAL_WAVE_TASK_PATTERN2 = /^F\d+\.\s+/i;
104872
- var LEGACY_PROMETHEUS_PLANS_DIR = ".sisyphus/plans";
104873
- var PROMETHEUS_PLAN_DIRS = [PROMETHEUS_PLANS_DIR, LEGACY_PROMETHEUS_PLANS_DIR];
104874
- function findPrometheusPlans(directory) {
104875
- try {
104876
- return PROMETHEUS_PLAN_DIRS.flatMap((planDir) => {
104877
- const plansDir = join98(directory, planDir);
104878
- if (!existsSync79(plansDir)) {
104879
- return [];
104880
- }
104881
- return readdirSync20(plansDir).filter((file2) => file2.endsWith(".md")).map((file2) => join98(plansDir, file2));
104882
- }).sort((left, right) => statSync11(right).mtimeMs - statSync11(left).mtimeMs);
104883
- } catch {
104884
- return [];
104885
- }
104886
- }
104887
- function getPlanName(planPath) {
104888
- return basename16(planPath, ".md");
104889
- }
104890
- function getPlanProgress(planPath) {
104891
- if (!existsSync79(planPath)) {
104892
- return { total: 0, completed: 0, isComplete: false };
104893
- }
104894
- try {
104895
- const content = readFileSync53(planPath, "utf-8");
104896
- const lines = content.split(/\r?\n/);
104897
- const hasStructuredSections = lines.some((line) => TODO_HEADING_PATTERN2.test(line) || FINAL_VERIFICATION_HEADING_PATTERN2.test(line));
104898
- if (hasStructuredSections) {
104899
- return getStructuredPlanProgress(lines);
104900
- }
104901
- return getSimplePlanProgress(content);
104902
- } catch {
104903
- return { total: 0, completed: 0, isComplete: false };
104904
- }
104905
- }
104906
- function getStructuredPlanProgress(lines) {
104907
- let section = "other";
104908
- let total = 0;
104909
- let completed = 0;
104910
- for (const line of lines) {
104911
- if (SECOND_LEVEL_HEADING_PATTERN2.test(line)) {
104912
- section = TODO_HEADING_PATTERN2.test(line) ? "todo" : FINAL_VERIFICATION_HEADING_PATTERN2.test(line) ? "final-wave" : "other";
104913
- continue;
104914
- }
104915
- if (section !== "todo" && section !== "final-wave") {
104916
- continue;
104917
- }
104918
- const checkedMatch = line.match(CHECKED_CHECKBOX_PATTERN);
104919
- const uncheckedMatch = checkedMatch ? null : line.match(UNCHECKED_CHECKBOX_PATTERN2);
104920
- const match = checkedMatch ?? uncheckedMatch;
104921
- if (!match || match[1].length > 0) {
104922
- continue;
104923
- }
104924
- const taskBody = match[2].trim();
104925
- const labelPattern = section === "todo" ? TODO_TASK_PATTERN2 : FINAL_WAVE_TASK_PATTERN2;
104926
- if (!labelPattern.test(taskBody)) {
104927
- continue;
104928
- }
104929
- total += 1;
104930
- if (checkedMatch) {
104931
- completed += 1;
104932
- }
104933
- }
104934
- return { total, completed, isComplete: total > 0 && completed === total };
104935
- }
104936
- function getSimplePlanProgress(content) {
104937
- const uncheckedMatches = content.match(/^[-*]\s*\[\s*\]/gm) ?? [];
104938
- const checkedMatches = content.match(/^[-*]\s*\[[xX]\]/gm) ?? [];
104939
- const total = uncheckedMatches.length + checkedMatches.length;
104940
- const completed = checkedMatches.length;
104941
- return { total, completed, isComplete: total > 0 && completed === total };
104942
- }
104943
- // packages/boulder-state/src/storage/shared.ts
104944
- var RESERVED_KEYS = new Set(["__proto__", "prototype", "constructor"]);
104945
- var SESSION_ID_PREFIX_PATTERN = /^(codex|opencode):/;
104946
- function normalizeSessionId(sessionId, platform2 = "opencode") {
104947
- if (SESSION_ID_PREFIX_PATTERN.test(sessionId)) {
104948
- return sessionId;
104949
- }
104950
- return `${platform2}:${sessionId}`;
104951
- }
104952
- function nowIsoString() {
104953
- return new Date().toISOString();
104954
- }
104955
- function parseIsoToMs(value) {
104956
- if (!value) {
104957
- return null;
104958
- }
104959
- const parsed = Date.parse(value);
104960
- return Number.isNaN(parsed) ? null : parsed;
104961
- }
104962
- function getElapsedMs(startedAt, endedAt) {
104963
- const startedMs = parseIsoToMs(startedAt);
104964
- const endedMs = parseIsoToMs(endedAt);
104965
- if (startedMs === null || endedMs === null) {
104966
- return;
104967
- }
104968
- return endedMs - startedMs;
104969
- }
104970
- function isValidWorkStatus(status) {
104971
- return status === "active" || status === "completed" || status === "paused" || status === "abandoned";
104972
- }
104973
- function buildWorkFromMirror(state3) {
104974
- const planName = state3.plan_name ?? state3.active_plan;
104975
- const workId = `${planName}-legacy`;
104976
- return {
104977
- work_id: workId,
104978
- active_plan: state3.active_plan,
104979
- plan_name: planName,
104980
- status: state3.status,
104981
- started_at: state3.started_at,
104982
- ended_at: state3.ended_at,
104983
- elapsed_ms: state3.elapsed_ms,
104984
- updated_at: state3.updated_at,
104985
- session_ids: Array.isArray(state3.session_ids) ? [...state3.session_ids] : [],
104986
- session_origins: state3.session_origins,
104987
- agent: state3.agent,
104988
- worktree_path: state3.worktree_path,
104989
- task_sessions: state3.task_sessions
104990
- };
104991
- }
104992
- function projectWorkToMirror(state3, work) {
104993
- state3.active_plan = work.active_plan;
104994
- state3.plan_name = work.plan_name;
104995
- state3.status = work.status;
104996
- state3.started_at = work.started_at;
104997
- state3.ended_at = work.ended_at;
104998
- state3.elapsed_ms = work.elapsed_ms;
104999
- state3.updated_at = work.updated_at;
105000
- state3.session_ids = [...work.session_ids];
105001
- state3.session_origins = work.session_origins ? { ...work.session_origins } : {};
105002
- state3.agent = work.agent;
105003
- state3.worktree_path = work.worktree_path;
105004
- state3.task_sessions = work.task_sessions ? { ...work.task_sessions } : {};
105005
- }
105006
- function selectMirrorWork(state3) {
105007
- const works = state3.works ? Object.values(state3.works) : [];
105008
- if (works.length === 0) {
105009
- return null;
105010
- }
105011
- if (state3.active_work_id) {
105012
- const matched = works.find((work) => work.work_id === state3.active_work_id);
105013
- if (matched) {
105014
- return matched;
105015
- }
105016
- }
105017
- const sorted = [...works].sort((left, right) => {
105018
- const leftMs = parseIsoToMs(left.updated_at ?? left.started_at) ?? 0;
105019
- const rightMs = parseIsoToMs(right.updated_at ?? right.started_at) ?? 0;
105020
- return rightMs - leftMs;
105021
- });
105022
- return sorted[0] ?? null;
105023
- }
105024
- // packages/boulder-state/src/storage/read-state.ts
105025
- import { existsSync as existsSync80, readFileSync as readFileSync54 } from "fs";
105026
- function readBoulderState(directory) {
105027
- const filePath = getBoulderFilePath(directory);
105028
- if (!existsSync80(filePath)) {
105029
- return null;
105030
- }
105031
- try {
105032
- const content = readFileSync54(filePath, "utf-8");
105033
- const parsed = JSON.parse(content);
105034
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
105035
- return null;
105036
- }
105037
- normalizeState(parsed);
105038
- const state3 = parsed;
105039
- const mirrorWork = selectMirrorWork(state3);
105040
- if (mirrorWork) {
105041
- state3.active_work_id = mirrorWork.work_id;
105042
- projectWorkToMirror(state3, mirrorWork);
105043
- }
105044
- return state3;
105045
- } catch {
105046
- return null;
105047
- }
105048
- }
105049
- function normalizeState(state3) {
105050
- normalizeSessionFields(state3);
105051
- const sessionIds = Array.isArray(state3.session_ids) ? state3.session_ids : [];
105052
- const sessionOrigins = state3.session_origins && typeof state3.session_origins === "object" && !Array.isArray(state3.session_origins) ? state3.session_origins : {};
105053
- state3.session_origins = sessionOrigins;
105054
- if (sessionIds.length === 1) {
105055
- const soleSessionId = sessionIds[0];
105056
- if (typeof soleSessionId === "string" && sessionOrigins[soleSessionId] !== "appended" && sessionOrigins[soleSessionId] !== "direct") {
105057
- sessionOrigins[soleSessionId] = "direct";
105058
- }
105059
- }
105060
- if (!state3.task_sessions || typeof state3.task_sessions !== "object" || Array.isArray(state3.task_sessions)) {
105061
- state3.task_sessions = {};
105062
- }
105063
- normalizeWorkSessionFields(state3.works);
105064
- }
105065
- function normalizeSessionFields(target) {
105066
- const sessionIds = Array.isArray(target.session_ids) ? target.session_ids.filter((sessionId) => typeof sessionId === "string").map((sessionId) => normalizeSessionId(sessionId)) : [];
105067
- target.session_ids = sessionIds;
105068
- const sessionOrigins = target.session_origins && typeof target.session_origins === "object" && !Array.isArray(target.session_origins) ? normalizeSessionOrigins(target.session_origins) : {};
105069
- target.session_origins = sessionOrigins;
105070
- }
105071
- function normalizeSessionOrigins(sessionOrigins) {
105072
- return Object.fromEntries(Object.entries(sessionOrigins).map(([sessionId, origin]) => [normalizeSessionId(sessionId), origin]));
105073
- }
105074
- function normalizeWorkSessionFields(works) {
105075
- if (!works || typeof works !== "object" || Array.isArray(works)) {
105076
- return;
105077
- }
105078
- for (const work of Object.values(works)) {
105079
- if (work && typeof work === "object" && !Array.isArray(work)) {
105080
- normalizeSessionFields(work);
105081
- }
105082
- }
105083
- }
105084
- function getBoulderWorks(state3) {
105085
- if (state3.works && typeof state3.works === "object") {
105086
- return Object.values(state3.works).filter((work) => work != null);
105087
- }
105088
- if (!state3.active_plan || !state3.plan_name || !state3.started_at) {
105089
- return [];
105090
- }
105091
- return [buildWorkFromMirror(state3)];
105092
- }
105093
- function getActiveWorks(directory) {
105094
- const state3 = readBoulderState(directory);
105095
- if (!state3) {
105096
- return [];
105097
- }
105098
- return getBoulderWorks(state3).filter((work) => work.status !== "completed" && work.status !== "abandoned");
105099
- }
105100
- function getWorkById(directory, workId) {
105101
- const state3 = readBoulderState(directory);
105102
- if (!state3) {
105103
- return null;
105104
- }
105105
- return getBoulderWorks(state3).find((work) => work.work_id === workId) ?? null;
105106
- }
105107
- function getWorkByPlanName(directory, planName, options) {
105108
- const state3 = readBoulderState(directory);
105109
- if (!state3) {
105110
- return null;
105111
- }
105112
- const worktreePath = options?.worktreePath;
105113
- return getBoulderWorks(state3).find((work) => {
105114
- if (work.plan_name !== planName) {
105115
- return false;
105116
- }
105117
- return worktreePath ? work.worktree_path === worktreePath : true;
105118
- }) ?? null;
105119
- }
105120
- function getWorkForSession(directory, sessionId) {
105121
- const state3 = readBoulderState(directory);
105122
- if (!state3) {
105123
- return null;
105124
- }
105125
- const normalizedSessionId = normalizeSessionId(sessionId);
105126
- let newestWork = null;
105127
- let newestWorkMs = 0;
105128
- for (const work of getBoulderWorks(state3)) {
105129
- if (!work.session_ids.includes(normalizedSessionId)) {
105130
- continue;
105131
- }
105132
- const workMs = parseIsoToMs(work.updated_at ?? work.started_at) ?? 0;
105133
- if (!newestWork || workMs > newestWorkMs) {
105134
- newestWork = work;
105135
- newestWorkMs = workMs;
105136
- }
105137
- }
105138
- if (newestWork) {
105139
- return newestWork;
105140
- }
105141
- return state3.session_ids.includes(normalizedSessionId) ? buildWorkFromMirror(state3) : null;
105142
- }
105143
- function getWorkResumeOptions(directory) {
105144
- const state3 = readBoulderState(directory);
105145
- if (!state3) {
105146
- return [];
105147
- }
105148
- return getBoulderWorks(state3).filter((work) => work.status !== "completed" && work.status !== "abandoned").map((work) => {
105149
- const progress = getPlanProgress(resolveBoulderPlanPathForWork(directory, work));
105150
- return {
105151
- work_id: work.work_id,
105152
- plan_name: work.plan_name,
105153
- active_plan: work.active_plan,
105154
- worktree_path: work.worktree_path,
105155
- status: work.status && isValidWorkStatus(work.status) ? work.status : "active",
105156
- started_at: work.started_at,
105157
- updated_at: work.updated_at ?? work.started_at,
105158
- ended_at: work.ended_at,
105159
- elapsed_ms: work.elapsed_ms,
105160
- session_count: work.session_ids.length,
105161
- progress,
105162
- is_current_mirror: state3.active_work_id === work.work_id
105163
- };
105164
- });
105165
- }
105166
- function getTaskSessionState(directory, taskKey) {
105167
- const state3 = readBoulderState(directory);
105168
- if (state3?.active_work_id) {
105169
- const work = state3.works?.[state3.active_work_id];
105170
- const taskSession = work?.task_sessions?.[taskKey];
105171
- if (taskSession) {
105172
- return taskSession;
105173
- }
105174
- }
105175
- if (!state3?.task_sessions) {
105176
- return null;
105177
- }
105178
- return state3.task_sessions[taskKey] ?? null;
105179
- }
105180
- // packages/boulder-state/src/storage/write-state.ts
105181
- import { existsSync as existsSync81, mkdirSync as mkdirSync18, unlinkSync as unlinkSync13, writeFileSync as writeFileSync18 } from "fs";
105182
- import { dirname as dirname31, join as join99 } from "path";
105183
- function writeBoulderState(directory, state3) {
105184
- const filePath = getBoulderFilePath(directory);
105185
- try {
105186
- const dir = dirname31(filePath);
105187
- if (!existsSync81(dir)) {
105188
- mkdirSync18(dir, { recursive: true });
105189
- writeFileSync18(join99(dir, ".gitignore"), ["*", "!/rules/", "!/rules/**", ""].join(`
105190
- `), "utf-8");
105191
- }
105192
- const stateToWrite = { ...state3 };
105193
- if (stateToWrite.works && stateToWrite.active_work_id) {
105194
- const activeWork = stateToWrite.works[stateToWrite.active_work_id];
105195
- if (activeWork) {
105196
- stateToWrite.works = {
105197
- ...stateToWrite.works,
105198
- [stateToWrite.active_work_id]: {
105199
- ...activeWork,
105200
- active_plan: stateToWrite.active_plan,
105201
- plan_name: stateToWrite.plan_name,
105202
- status: stateToWrite.status,
105203
- started_at: stateToWrite.started_at,
105204
- ended_at: stateToWrite.ended_at,
105205
- elapsed_ms: stateToWrite.elapsed_ms,
105206
- updated_at: stateToWrite.updated_at,
105207
- session_ids: [...stateToWrite.session_ids],
105208
- session_origins: stateToWrite.session_origins ? { ...stateToWrite.session_origins } : {},
105209
- agent: stateToWrite.agent,
105210
- worktree_path: stateToWrite.worktree_path,
105211
- task_sessions: stateToWrite.task_sessions ? { ...stateToWrite.task_sessions } : {}
105212
- }
105213
- };
105214
- }
105215
- }
105216
- writeFileSync18(filePath, JSON.stringify(stateToWrite, null, 2), "utf-8");
105217
- return true;
105218
- } catch {
105219
- return false;
105220
- }
105221
- }
105222
- function clearBoulderState(directory) {
105223
- const filePath = getBoulderFilePath(directory);
105224
- try {
105225
- if (existsSync81(filePath)) {
105226
- unlinkSync13(filePath);
105227
- }
105228
- return true;
105229
- } catch {
105230
- return false;
105231
- }
105232
- }
105233
- function generateWorkId(planName) {
105234
- const slug = planName.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
105235
- const randomHex = Math.floor(Math.random() * 4294967295).toString(16).padStart(8, "0");
105236
- return `${slug.length > 0 ? slug : "work"}-${randomHex}`;
105237
- }
105238
- function createBoulderState(planPath, sessionId, agent, worktreePath) {
105239
- const startedAt = nowIsoString();
105240
- const normalizedSessionId = normalizeSessionId(sessionId);
105241
- const workId = generateWorkId(getPlanName(planPath));
105242
- const work = {
105243
- work_id: workId,
105244
- active_plan: planPath,
105245
- plan_name: getPlanName(planPath),
105246
- status: "active",
105247
- started_at: startedAt,
105248
- updated_at: startedAt,
105249
- session_ids: [normalizedSessionId],
105250
- session_origins: { [normalizedSessionId]: "direct" },
105251
- ...agent !== undefined ? { agent } : {},
105252
- ...worktreePath !== undefined ? { worktree_path: worktreePath } : {},
105253
- task_sessions: {}
105254
- };
105255
- return {
105256
- schema_version: 2,
105257
- active_work_id: workId,
105258
- works: { [workId]: work },
105259
- active_plan: planPath,
105260
- started_at: startedAt,
105261
- status: "active",
105262
- updated_at: startedAt,
105263
- session_ids: [normalizedSessionId],
105264
- session_origins: { [normalizedSessionId]: "direct" },
105265
- plan_name: getPlanName(planPath),
105266
- task_sessions: {},
105267
- ...agent !== undefined ? { agent } : {},
105268
- ...worktreePath !== undefined ? { worktree_path: worktreePath } : {}
105269
- };
105270
- }
105271
- function selectActiveWork(directory, workId) {
105272
- const state3 = readBoulderState(directory);
105273
- if (!state3) {
105274
- return null;
105275
- }
105276
- const works = getBoulderWorks(state3);
105277
- const nextWork = works.find((work) => work.work_id === workId);
105278
- if (!nextWork) {
105279
- return null;
105280
- }
105281
- const nextState = {
105282
- ...state3,
105283
- schema_version: 2,
105284
- active_work_id: workId,
105285
- works: state3.works ?? Object.fromEntries(works.map((work) => [work.work_id, work]))
105286
- };
105287
- projectWorkToMirror(nextState, nextWork);
105288
- return writeBoulderState(directory, nextState) ? nextState : null;
105289
- }
105290
- function addBoulderWork(directory, input) {
105291
- const state3 = readBoulderState(directory);
105292
- if (!state3) {
105293
- return null;
105294
- }
105295
- const workId = generateWorkId(getPlanName(input.planPath));
105296
- const startedAt = input.startedAt ?? nowIsoString();
105297
- const normalizedSessionId = normalizeSessionId(input.sessionId);
105298
- const nextWork = {
105299
- work_id: workId,
105300
- active_plan: input.planPath,
105301
- plan_name: getPlanName(input.planPath),
105302
- status: "active",
105303
- started_at: startedAt,
105304
- updated_at: startedAt,
105305
- session_ids: [normalizedSessionId],
105306
- session_origins: { [normalizedSessionId]: "direct" },
105307
- ...input.agent !== undefined ? { agent: input.agent } : {},
105308
- ...input.worktreePath !== undefined ? { worktree_path: input.worktreePath } : {},
105309
- task_sessions: {}
105310
- };
105311
- const nextState = {
105312
- ...state3,
105313
- schema_version: 2,
105314
- works: { ...Object.fromEntries(getBoulderWorks(state3).map((work) => [work.work_id, work])), [workId]: nextWork },
105315
- active_work_id: workId
105316
- };
105317
- projectWorkToMirror(nextState, nextWork);
105318
- return writeBoulderState(directory, nextState) ? nextState : null;
105319
- }
105320
- function completeBoulder(directory, workId, endedAt) {
105321
- const state3 = readBoulderState(directory);
105322
- if (!state3) {
105323
- return null;
105324
- }
105325
- const targetWorkId = workId ?? state3.active_work_id;
105326
- if (!targetWorkId) {
105327
- return null;
105328
- }
105329
- const work = state3.works?.[targetWorkId] ?? getBoulderWorks(state3).find((candidate) => candidate.work_id === targetWorkId);
105330
- if (!work) {
105331
- return null;
105332
- }
105333
- if (work.status === "completed" && work.ended_at !== undefined && work.elapsed_ms !== undefined) {
105334
- return state3;
105335
- }
105336
- const endAt = endedAt ?? nowIsoString();
105337
- work.ended_at = endAt;
105338
- work.elapsed_ms = getElapsedMs(work.started_at, endAt);
105339
- work.status = "completed";
105340
- work.updated_at = nowIsoString();
105341
- if (state3.active_work_id === targetWorkId) {
105342
- projectWorkToMirror(state3, work);
105343
- }
105344
- return writeBoulderState(directory, state3) ? state3 : null;
105345
- }
105346
105566
 
105347
- // packages/boulder-state/src/storage/session.ts
105348
- function appendSessionId(directory, sessionId, origin = "direct") {
105349
- const normalizedSessionId = normalizeSessionId(sessionId);
105350
- const activeWorkId = readBoulderState(directory)?.active_work_id;
105351
- if (activeWorkId) {
105352
- return appendSessionIdForWork(directory, activeWorkId, normalizedSessionId, origin);
105353
- }
105354
- const state3 = readBoulderState(directory);
105355
- if (!state3) {
105356
- return null;
105357
- }
105358
- if (!state3.session_origins || typeof state3.session_origins !== "object" || Array.isArray(state3.session_origins)) {
105359
- state3.session_origins = {};
105360
- }
105361
- if (!state3.session_ids?.includes(normalizedSessionId)) {
105362
- if (!Array.isArray(state3.session_ids)) {
105363
- state3.session_ids = [];
105364
- }
105365
- const originalSessionIds = [...state3.session_ids];
105366
- const originalSessionOrigins = { ...state3.session_origins };
105367
- state3.session_ids.push(normalizedSessionId);
105368
- state3.session_origins[normalizedSessionId] = origin;
105369
- if (writeBoulderState(directory, state3)) {
105370
- return state3;
105371
- }
105372
- state3.session_ids = originalSessionIds;
105373
- state3.session_origins = originalSessionOrigins;
105374
- return null;
105375
- }
105376
- if (!state3.session_origins[normalizedSessionId]) {
105377
- state3.session_origins[normalizedSessionId] = origin;
105378
- if (!writeBoulderState(directory, state3)) {
105379
- return null;
105380
- }
105381
- }
105382
- return state3;
105383
- }
105384
- function appendSessionIdForWork(directory, workId, sessionId, origin = "direct") {
105385
- const normalizedSessionId = normalizeSessionId(sessionId);
105386
- const state3 = readBoulderState(directory);
105387
- if (!state3) {
105388
- return null;
105389
- }
105390
- const works = getBoulderWorks(state3);
105391
- const targetWork = works.find((work) => work.work_id === workId);
105392
- if (!targetWork) {
105393
- return null;
105394
- }
105395
- const updatedWork = {
105396
- ...targetWork,
105397
- session_ids: targetWork.session_ids.includes(normalizedSessionId) ? [...targetWork.session_ids] : [...targetWork.session_ids, normalizedSessionId],
105398
- session_origins: { ...targetWork.session_origins ?? {}, [normalizedSessionId]: origin },
105399
- updated_at: nowIsoString()
105400
- };
105401
- const nextState = {
105402
- ...state3,
105403
- schema_version: 2,
105404
- works: {
105405
- ...Object.fromEntries(works.map((work) => [work.work_id, work])),
105406
- [workId]: updatedWork
105407
- }
105408
- };
105409
- if (state3.active_work_id === workId) {
105410
- projectWorkToMirror(nextState, updatedWork);
105411
- }
105412
- return writeBoulderState(directory, nextState) ? nextState : null;
105413
- }
105414
- // packages/boulder-state/src/storage/task.ts
105415
- function upsertTaskSessionState(directory, input) {
105416
- const stateForWork = readBoulderState(directory);
105417
- if (stateForWork?.active_work_id) {
105418
- return upsertTaskSessionStateForWork(directory, stateForWork.active_work_id, input);
105419
- }
105420
- const state3 = readBoulderState(directory);
105421
- if (!state3 || RESERVED_KEYS.has(input.taskKey)) {
105422
- return null;
105423
- }
105424
- const normalizedSessionId = normalizeSessionId(input.sessionId);
105425
- const taskSessions = state3.task_sessions ?? {};
105426
- taskSessions[input.taskKey] = {
105427
- task_key: input.taskKey,
105428
- task_label: input.taskLabel,
105429
- task_title: input.taskTitle,
105430
- session_id: normalizedSessionId,
105431
- ...input.agent !== undefined ? { agent: input.agent } : {},
105432
- ...input.category !== undefined ? { category: input.category } : {},
105433
- updated_at: nowIsoString()
105434
- };
105435
- state3.task_sessions = taskSessions;
105436
- return writeBoulderState(directory, state3) ? state3 : null;
105437
- }
105438
- function upsertTaskSessionStateForWork(directory, workId, input) {
105439
- if (RESERVED_KEYS.has(input.taskKey)) {
105440
- return null;
105441
- }
105442
- const state3 = readBoulderState(directory);
105443
- if (!state3) {
105444
- return null;
105445
- }
105446
- const works = getBoulderWorks(state3);
105447
- const targetWork = works.find((work) => work.work_id === workId);
105448
- if (!targetWork) {
105449
- return null;
105450
- }
105451
- const normalizedSessionId = normalizeSessionId(input.sessionId);
105452
- const previousTaskSession = targetWork.task_sessions?.[input.taskKey];
105453
- const nextTaskSession = {
105454
- task_key: input.taskKey,
105455
- task_label: input.taskLabel,
105456
- task_title: input.taskTitle,
105457
- session_id: normalizedSessionId,
105458
- ...input.agent !== undefined ? { agent: input.agent } : {},
105459
- ...input.category !== undefined ? { category: input.category } : {},
105460
- ...previousTaskSession?.started_at !== undefined ? { started_at: previousTaskSession.started_at } : {},
105461
- ...previousTaskSession?.ended_at !== undefined ? { ended_at: previousTaskSession.ended_at } : {},
105462
- ...previousTaskSession?.elapsed_ms !== undefined ? { elapsed_ms: previousTaskSession.elapsed_ms } : {},
105463
- ...previousTaskSession?.status !== undefined ? { status: previousTaskSession.status } : {},
105464
- updated_at: nowIsoString()
105465
- };
105466
- const nextWork = {
105467
- ...targetWork,
105468
- task_sessions: { ...targetWork.task_sessions ?? {}, [input.taskKey]: nextTaskSession },
105469
- updated_at: nowIsoString()
105470
- };
105471
- const nextState = {
105472
- ...state3,
105473
- schema_version: 2,
105474
- works: {
105475
- ...Object.fromEntries(works.map((work) => [work.work_id, work])),
105476
- [workId]: nextWork
105477
- }
105478
- };
105479
- if (state3.active_work_id === workId) {
105480
- projectWorkToMirror(nextState, nextWork);
105481
- }
105482
- return writeBoulderState(directory, nextState) ? nextState : null;
105483
- }
105484
- function startTaskTimer(directory, workId, input) {
105485
- const nextState = upsertTaskSessionStateForWork(directory, workId, {
105486
- ...input,
105487
- sessionId: normalizeSessionId(input.sessionId)
105488
- });
105489
- if (!nextState) {
105490
- return null;
105491
- }
105492
- const work = nextState.works?.[workId];
105493
- const taskSession = work?.task_sessions?.[input.taskKey];
105494
- if (!work || !taskSession) {
105495
- return null;
105496
- }
105497
- const startedAt = taskSession.started_at ?? input.startedAt ?? nowIsoString();
105498
- taskSession.started_at = startedAt;
105499
- taskSession.status = "running";
105500
- taskSession.updated_at = nowIsoString();
105501
- work.updated_at = nowIsoString();
105502
- return writeBoulderState(directory, nextState) ? nextState : null;
105503
- }
105504
- function endTaskTimer(directory, workId, taskKey, endedAt) {
105505
- const state3 = readBoulderState(directory);
105506
- if (!state3) {
105507
- return null;
105508
- }
105509
- const work = state3.works?.[workId] ?? getBoulderWorks(state3).find((candidate) => candidate.work_id === workId);
105510
- if (!work?.task_sessions?.[taskKey]) {
105511
- return null;
105512
- }
105513
- const taskSession = work.task_sessions[taskKey];
105514
- const endAt = endedAt ?? nowIsoString();
105515
- taskSession.ended_at = endAt;
105516
- taskSession.elapsed_ms = getElapsedMs(taskSession.started_at, endAt);
105517
- taskSession.status = "completed";
105518
- taskSession.updated_at = nowIsoString();
105519
- work.updated_at = nowIsoString();
105520
- if (state3.active_work_id === workId) {
105521
- projectWorkToMirror(state3, work);
105522
- }
105523
- return writeBoulderState(directory, state3) ? state3 : null;
105524
- }
105525
- // packages/omo-opencode/src/features/boulder-state/format-duration.ts
105526
- init_src();
105527
105567
  // packages/omo-opencode/src/hooks/prometheus-md-only/agent-resolution.ts
105528
105568
  function isCompactionAgent2(agent) {
105529
105569
  return agent.toLowerCase() === "compaction";
@@ -106350,7 +106390,7 @@ async function findRecentSessionPlanPath(input) {
106350
106390
 
106351
106391
  // packages/omo-opencode/src/hooks/start-work/start-work-hook.ts
106352
106392
  var HOOK_NAME6 = "start-work";
106353
- var START_WORK_TEMPLATE_MARKER = "You are starting a Sisyphus work session.";
106393
+ var START_WORK_TEMPLATE_MARKER = "You are starting an Atlas work session.";
106354
106394
  var CONTEXT_INFO_MARKER = "<!-- omo-start-work-context -->";
106355
106395
  function resolveWorktreeContext(explicitWorktreePath) {
106356
106396
  if (explicitWorktreePath === null) {
@@ -110651,7 +110691,8 @@ function createAbortSessionRequest(deps) {
110651
110691
  await ctx.client.session.abort({ path: { id: sessionID } });
110652
110692
  releasePromptAsyncReservation(sessionID, `runtime-fallback-abort:${source}`, {
110653
110693
  reservedBy: `runtime-fallback:${source}`,
110654
- reservedByPrefix: "runtime-fallback:"
110694
+ reservedByPrefix: "runtime-fallback:",
110695
+ supersedeTransientRetryOwners: true
110655
110696
  });
110656
110697
  log2(`[${HOOK_NAME11}] Aborted in-flight session request (${source})`, { sessionID });
110657
110698
  } catch (error) {
@@ -110897,6 +110938,7 @@ function createAutoRetryDispatcher(deps, scheduleSessionFallbackTimeout, clearSe
110897
110938
  sessionStates,
110898
110939
  sessionRetryInFlight,
110899
110940
  sessionAwaitingFallbackResult,
110941
+ internallyAbortedSessions,
110900
110942
  pluginConfig
110901
110943
  } = deps;
110902
110944
  return async (sessionID, newModel, resolvedAgent, source) => {
@@ -110966,6 +111008,7 @@ function createAutoRetryDispatcher(deps, scheduleSessionFallbackTimeout, clearSe
110966
111008
  },
110967
111009
  query: { directory: ctx.directory }
110968
111010
  };
111011
+ const wasInternallyAborted = internallyAbortedSessions.has(sessionID);
110969
111012
  const dispatchRetryPrompt = (retrySource, queueBehavior) => dispatchInternalPrompt({
110970
111013
  mode: "async",
110971
111014
  client: ctx.client,
@@ -110973,6 +111016,7 @@ function createAutoRetryDispatcher(deps, scheduleSessionFallbackTimeout, clearSe
110973
111016
  source: retrySource,
110974
111017
  settleMs: 0,
110975
111018
  ...queueBehavior ? { queueBehavior } : {},
111019
+ ...wasInternallyAborted ? { checkToolState: false } : {},
110976
111020
  input: retryPromptInput
110977
111021
  });
110978
111022
  let promptResult = await dispatchRetryPrompt(`runtime-fallback:${source}`, "defer");
@@ -111733,6 +111777,7 @@ function createEventHandler(deps, helpers) {
111733
111777
  }
111734
111778
  sessionRetryInFlight.delete(sessionID);
111735
111779
  sessionAwaitingFallbackResult.delete(sessionID);
111780
+ deps.internallyAbortedSessions.delete(sessionID);
111736
111781
  sessionStatusRetryKeys.delete(sessionID);
111737
111782
  helpers.clearSessionFallbackTimeout(sessionID);
111738
111783
  };
@@ -111766,6 +111811,7 @@ function createEventHandler(deps, helpers) {
111766
111811
  sessionLastAccess.delete(sessionID);
111767
111812
  sessionRetryInFlight.delete(sessionID);
111768
111813
  sessionAwaitingFallbackResult.delete(sessionID);
111814
+ deps.internallyAbortedSessions.delete(sessionID);
111769
111815
  helpers.clearSessionFallbackTimeout(sessionID);
111770
111816
  sessionStatusRetryKeys.delete(sessionID);
111771
111817
  SessionCategoryRegistry.remove(sessionID);
@@ -127695,6 +127741,7 @@ function cloneParentWake(wake) {
127695
127741
  promptContext,
127696
127742
  notifications: [...wake.notifications],
127697
127743
  shouldReply: wake.shouldReply,
127744
+ ...wake.queuedAt !== undefined ? { queuedAt: wake.queuedAt } : {},
127698
127745
  ...wake.dispatchedAt !== undefined ? { dispatchedAt: wake.dispatchedAt } : {},
127699
127746
  ...wake.noReplyAdmittedAt !== undefined ? { noReplyAdmittedAt: wake.noReplyAdmittedAt } : {},
127700
127747
  ...wake.toolCallDeferralStartedAt !== undefined ? { toolCallDeferralStartedAt: wake.toolCallDeferralStartedAt } : {},
@@ -128037,7 +128084,7 @@ async function sendParentWakePrompt(input) {
128037
128084
  ...input.emptyAssistantTurnRetry ? { dedupeKey: createEmptyAssistantTurnRetryDedupeKey(input.latestWake) } : {},
128038
128085
  settleMs: 0,
128039
128086
  queueBehavior: "defer",
128040
- checkStatus: input.forceNoReply !== true,
128087
+ checkStatus: input.forceNoReply !== true && input.skipPromptGateStatusCheck !== true,
128041
128088
  checkToolState: input.forceNoReply !== true && !input.toolWaitDecision.skipPromptGateToolStateCheck,
128042
128089
  input: {
128043
128090
  path: { id: input.sessionID },
@@ -128120,6 +128167,8 @@ function createTrackedDispatchedWake(wake, forceNoReply) {
128120
128167
  }
128121
128168
 
128122
128169
  // packages/omo-opencode/src/features/background-agent/parent-wake-flush-runner.ts
128170
+ var PENDING_PARENT_WAKE_MAX_ACTIVE_DEFER_MS = 60000;
128171
+
128123
128172
  class ParentWakeFlushRunner {
128124
128173
  deps;
128125
128174
  constructor(deps) {
@@ -128149,14 +128198,16 @@ class ParentWakeFlushRunner {
128149
128198
  if (await this.dropAdmittedWakeConsumedByParent(sessionID, latestWake)) {
128150
128199
  return;
128151
128200
  }
128152
- if (sessionActive) {
128201
+ const emptyAssistantTurnRetry = latestWake.allowEmptyAssistantTurnRetry === true;
128202
+ const forceDispatchAfterActiveDefer = sessionActive && this.shouldForceDispatchAfterActiveDefer(latestWake);
128203
+ if (sessionActive && !forceDispatchAfterActiveDefer) {
128153
128204
  this.schedulePendingParentWakeFlush(sessionID);
128154
128205
  log2("[background-agent] Deferred parent wake because parent session is active:", {
128155
128206
  sessionID
128156
128207
  });
128157
128208
  return;
128158
128209
  }
128159
- if (this.hasRecentParentSessionActivity(sessionID)) {
128210
+ if (!forceDispatchAfterActiveDefer && this.hasRecentParentSessionActivity(sessionID)) {
128160
128211
  if (this.deferReplyWakeWhileUnsafe(sessionID, latestWake)) {
128161
128212
  return;
128162
128213
  }
@@ -128174,7 +128225,6 @@ class ParentWakeFlushRunner {
128174
128225
  }
128175
128226
  return;
128176
128227
  }
128177
- const emptyAssistantTurnRetry = latestWake.allowEmptyAssistantTurnRetry === true;
128178
128228
  const toolWaitDecision = await this.shouldDeferParentWakeForSessionHistory(sessionID, latestWake);
128179
128229
  if (toolWaitDecision.defer) {
128180
128230
  if (this.deferReplyWakeWhileUnsafe(sessionID, latestWake)) {
@@ -128227,8 +128277,15 @@ class ParentWakeFlushRunner {
128227
128277
  }
128228
128278
  await this.sendParentWakePrompt(sessionID, latestWake, {
128229
128279
  emptyAssistantTurnRetry,
128230
- toolWaitDecision: finalToolWaitDecision
128280
+ toolWaitDecision: finalToolWaitDecision,
128281
+ ...forceDispatchAfterActiveDefer ? { skipPromptGateStatusCheck: true } : {}
128231
128282
  });
128283
+ if (forceDispatchAfterActiveDefer) {
128284
+ log2("[background-agent] Sent parent wake after active-session defer ceiling:", {
128285
+ sessionID,
128286
+ queuedAgeMs: this.getQueuedAgeMs(latestWake)
128287
+ });
128288
+ }
128232
128289
  }
128233
128290
  schedulePendingParentWakeFlush(sessionID, delayMs) {
128234
128291
  this.deps.pendingQueue.scheduleFlush(sessionID, async () => {
@@ -128280,6 +128337,7 @@ class ParentWakeFlushRunner {
128280
128337
  latestWake,
128281
128338
  ...options.forceNoReply !== undefined ? { forceNoReply: options.forceNoReply } : {},
128282
128339
  ...options.retainPendingWake !== undefined ? { retainPendingWake: options.retainPendingWake } : {},
128340
+ ...options.skipPromptGateStatusCheck !== undefined ? { skipPromptGateStatusCheck: options.skipPromptGateStatusCheck } : {},
128283
128341
  emptyAssistantTurnRetry: options.emptyAssistantTurnRetry,
128284
128342
  toolWaitDecision: options.toolWaitDecision,
128285
128343
  getDispatchedWake: () => this.deps.dispatchedTracker.getWake(sessionID),
@@ -128295,6 +128353,12 @@ class ParentWakeFlushRunner {
128295
128353
  async isSessionActive(sessionID) {
128296
128354
  return isSessionActive(this.deps.notifierDeps.client, sessionID);
128297
128355
  }
128356
+ shouldForceDispatchAfterActiveDefer(wake) {
128357
+ return wake.shouldReply && this.getQueuedAgeMs(wake) >= PENDING_PARENT_WAKE_MAX_ACTIVE_DEFER_MS;
128358
+ }
128359
+ getQueuedAgeMs(wake) {
128360
+ return Date.now() - (wake.queuedAt ?? Date.now());
128361
+ }
128298
128362
  hasRecentParentSessionActivity(sessionID) {
128299
128363
  return this.deps.sessionInspector.hasRecentActivity(sessionID);
128300
128364
  }
@@ -128339,9 +128403,11 @@ class ParentWakePendingQueue {
128339
128403
  this.pendingParentWakes.delete(sessionID);
128340
128404
  }
128341
128405
  queueWake(sessionID, notification, promptContext, shouldReply) {
128406
+ const now = Date.now();
128342
128407
  const resolvedPromptContext = resolveParentWakePromptContext(promptContext);
128343
128408
  const pendingWake = this.pendingParentWakes.get(sessionID);
128344
128409
  if (pendingWake) {
128410
+ pendingWake.queuedAt ??= now;
128345
128411
  const mergedNotifications = mergeParentWakeNotifications(pendingWake.notifications, notification);
128346
128412
  const notificationsChanged = mergedNotifications.length !== pendingWake.notifications.length || mergedNotifications.some((merged, index) => merged !== pendingWake.notifications[index]);
128347
128413
  pendingWake.notifications = mergedNotifications;
@@ -128356,12 +128422,17 @@ class ParentWakePendingQueue {
128356
128422
  this.pendingParentWakes.set(sessionID, {
128357
128423
  promptContext: resolvedPromptContext,
128358
128424
  notifications: [notification],
128359
- shouldReply
128425
+ shouldReply,
128426
+ queuedAt: now
128360
128427
  });
128361
128428
  }
128362
128429
  requeueWake(sessionID, latestWake) {
128430
+ const now = Date.now();
128363
128431
  const pendingWake = this.pendingParentWakes.get(sessionID);
128364
128432
  if (pendingWake) {
128433
+ const existingQueuedAt = pendingWake.queuedAt ?? now;
128434
+ const latestQueuedAt = latestWake.queuedAt ?? now;
128435
+ pendingWake.queuedAt = Math.min(existingQueuedAt, latestQueuedAt);
128365
128436
  pendingWake.notifications = pendingWake.notifications.reduce((notifications, notification) => mergeParentWakeNotifications(notifications, notification), [...latestWake.notifications]);
128366
128437
  pendingWake.shouldReply = pendingWake.shouldReply || latestWake.shouldReply;
128367
128438
  pendingWake.promptContext = latestWake.promptContext;
@@ -128374,7 +128445,9 @@ class ParentWakePendingQueue {
128374
128445
  }
128375
128446
  return;
128376
128447
  }
128377
- this.pendingParentWakes.set(sessionID, cloneParentWake(latestWake));
128448
+ const clonedWake = cloneParentWake(latestWake);
128449
+ clonedWake.queuedAt ??= now;
128450
+ this.pendingParentWakes.set(sessionID, clonedWake);
128378
128451
  }
128379
128452
  scheduleFlush(sessionID, operation, delayMs) {
128380
128453
  if (this.pendingParentWakeTimers.has(sessionID)) {
@@ -133073,6 +133146,7 @@ function createMonitorPipeline(components, deps) {
133073
133146
  }
133074
133147
 
133075
133148
  // packages/omo-opencode/src/features/monitor/process.ts
133149
+ init_bun_spawn_shim();
133076
133150
  var KILL_GRACE_MS = 5000;
133077
133151
  function killProcessGroup(pid, signal) {
133078
133152
  try {
@@ -133094,7 +133168,7 @@ function spawnMonitoredProcess(opts, deps) {
133094
133168
  if (argv.length === 0) {
133095
133169
  throw new Error("Cannot spawn an empty monitor command");
133096
133170
  }
133097
- const subprocess = spawnDetachedProcess(argv, opts, deps.spawn ?? Bun.spawn);
133171
+ const subprocess = spawnDetachedProcess(argv, opts, deps.spawn ?? spawn);
133098
133172
  let actualExited = false;
133099
133173
  let publicExitSettled = false;
133100
133174
  let watchdogTimer;
@@ -133124,11 +133198,15 @@ function spawnMonitoredProcess(opts, deps) {
133124
133198
  function kill(signal = "SIGTERM") {
133125
133199
  if (actualExited)
133126
133200
  return;
133127
- killProcessGroup(subprocess.pid, signal);
133201
+ if (subprocess.pid !== undefined) {
133202
+ killProcessGroup(subprocess.pid, signal);
133203
+ }
133128
133204
  if (graceTimer === undefined) {
133129
133205
  graceTimer = deps.setTimer(() => {
133130
133206
  if (!actualExited) {
133131
- killProcessGroup(subprocess.pid, "SIGKILL");
133207
+ if (subprocess.pid !== undefined) {
133208
+ killProcessGroup(subprocess.pid, "SIGKILL");
133209
+ }
133132
133210
  }
133133
133211
  }, KILL_GRACE_MS);
133134
133212
  }
@@ -133142,7 +133220,7 @@ function spawnMonitoredProcess(opts, deps) {
133142
133220
  actualExited = true;
133143
133221
  clearWatchdog();
133144
133222
  clearGraceTimer();
133145
- settlePublicExit({ code, signal: subprocess.signalCode });
133223
+ settlePublicExit({ code, signal: subprocess.signalCode ?? null });
133146
133224
  }).catch((error) => {
133147
133225
  actualExited = true;
133148
133226
  clearWatchdog();
@@ -160299,7 +160377,7 @@ function extractPromptText5(parts) {
160299
160377
  }
160300
160378
 
160301
160379
  // packages/omo-opencode/src/plugin/chat-message/start-work-message.ts
160302
- var START_WORK_TEMPLATE_MARKER2 = "You are starting a Sisyphus work session.";
160380
+ var START_WORK_TEMPLATE_MARKER2 = "You are starting an Atlas work session.";
160303
160381
  function isStartWorkHookOutput(value) {
160304
160382
  if (typeof value !== "object" || value === null)
160305
160383
  return false;