oh-my-opencode 4.5.12 → 4.7.0

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 (189) hide show
  1. package/.agents/skills/opencode-qa/SKILL.md +194 -0
  2. package/.agents/skills/opencode-qa/references/cli-commands.md +188 -0
  3. package/.agents/skills/opencode-qa/references/db-investigation.md +197 -0
  4. package/.agents/skills/opencode-qa/references/events-hooks.md +110 -0
  5. package/.agents/skills/opencode-qa/references/sdk.md +96 -0
  6. package/.agents/skills/opencode-qa/references/server-api.md +200 -0
  7. package/.agents/skills/opencode-qa/references/testing-harness.md +218 -0
  8. package/.agents/skills/opencode-qa/references/tui-tmux.md +52 -0
  9. package/.agents/skills/opencode-qa/scripts/db-session-by-id.sh +53 -0
  10. package/.agents/skills/opencode-qa/scripts/db-session-by-name.sh +57 -0
  11. package/.agents/skills/opencode-qa/scripts/db-session-by-text.sh +158 -0
  12. package/.agents/skills/opencode-qa/scripts/export-roundtrip.sh +57 -0
  13. package/.agents/skills/opencode-qa/scripts/lib/common.sh +216 -0
  14. package/.agents/skills/opencode-qa/scripts/server-smoke.sh +64 -0
  15. package/.agents/skills/opencode-qa/scripts/sse-hook-probe.sh +106 -0
  16. package/.agents/skills/opencode-qa/scripts/tui-smoke.sh +89 -0
  17. package/README.ja.md +13 -3
  18. package/README.ko.md +13 -3
  19. package/README.md +24 -14
  20. package/README.ru.md +13 -3
  21. package/README.zh-cn.md +13 -3
  22. package/bin/oh-my-opencode.js +4 -3
  23. package/bin/oh-my-opencode.test.ts +35 -7
  24. package/bin/platform.d.ts +1 -1
  25. package/bin/platform.js +4 -4
  26. package/bin/platform.test.ts +31 -9
  27. package/bin/version-mismatch.js +47 -0
  28. package/bin/version-mismatch.test.ts +120 -0
  29. package/dist/cli/cleanup-command.d.ts +4 -0
  30. package/dist/cli/cleanup.d.ts +11 -0
  31. package/dist/cli/cli-program.d.ts +2 -1
  32. package/dist/cli/codex-ulw-loop.d.ts +12 -0
  33. package/dist/cli/doctor/checks/tui-plugin-config.d.ts +2 -0
  34. package/dist/cli/index.js +2189 -529
  35. package/dist/cli/install-codex/codex-cache.d.ts +1 -0
  36. package/dist/cli/install-codex/codex-cleanup-config.d.ts +6 -0
  37. package/dist/cli/install-codex/codex-cleanup.d.ts +21 -0
  38. package/dist/cli/install-codex/codex-config-permissions.d.ts +1 -0
  39. package/dist/cli/install-codex/codex-config-reasoning.d.ts +2 -0
  40. package/dist/cli/install-codex/codex-config-toml.d.ts +2 -1
  41. package/dist/cli/install-codex/codex-installation-detection.d.ts +36 -0
  42. package/dist/cli/install-codex/codex-model-catalog.d.ts +13 -0
  43. package/dist/cli/install-codex/codex-package-layout.d.ts +1 -0
  44. package/dist/cli/install-codex/codex-project-local-cleanup-best-effort.d.ts +7 -0
  45. package/dist/cli/install-codex/codex-project-local-cleanup.d.ts +35 -0
  46. package/dist/cli/install-codex/git-bash.d.ts +35 -0
  47. package/dist/cli/install-codex/index.d.ts +4 -0
  48. package/dist/cli/install-codex/toml-section-editor.d.ts +2 -0
  49. package/dist/cli/install-codex/types.d.ts +20 -0
  50. package/dist/cli/run/event-state.d.ts +1 -0
  51. package/dist/cli/run/poll-for-completion.d.ts +1 -0
  52. package/dist/cli/run/prompt-start.d.ts +7 -0
  53. package/dist/cli/star-request.d.ts +9 -0
  54. package/dist/config/schema/hooks.d.ts +0 -1
  55. package/dist/create-hooks.d.ts +0 -1
  56. package/dist/features/background-agent/concurrency.d.ts +1 -0
  57. package/dist/features/background-agent/process-cleanup.d.ts +6 -0
  58. package/dist/features/builtin-skills/skills/debugging.d.ts +2 -0
  59. package/dist/features/builtin-skills/skills/index.d.ts +1 -0
  60. package/dist/features/claude-code-session-state/state.d.ts +1 -0
  61. package/dist/features/opencode-skill-loader/index.d.ts +1 -0
  62. package/dist/features/opencode-skill-loader/opencode-config-skills-reader.d.ts +5 -0
  63. package/dist/features/tmux-subagent/attachable-session-status.d.ts +1 -1
  64. package/dist/features/tmux-subagent/session-status-parser.d.ts +1 -0
  65. package/dist/hooks/comment-checker/cli.d.ts +1 -0
  66. package/dist/hooks/index.d.ts +0 -1
  67. package/dist/hooks/tasks-todowrite-disabler/constants.d.ts +1 -1
  68. package/dist/index.js +1077 -563
  69. package/dist/plugin/hooks/create-core-hooks.d.ts +0 -1
  70. package/dist/plugin/hooks/create-session-hooks.d.ts +1 -2
  71. package/dist/plugin/messages-transform.d.ts +8 -1
  72. package/dist/plugin/user-abort-interrupted-recovery-guard.d.ts +6 -0
  73. package/dist/shared/command-executor/execute-hook-command.d.ts +2 -0
  74. package/dist/shared/prompt-async-gate/recent-dispatches.d.ts +14 -0
  75. package/dist/shared/prompt-async-gate/semantic-dedupe.d.ts +7 -0
  76. package/dist/shared/prompt-async-gate/session-idle-dispatch.d.ts +1 -0
  77. package/dist/shared/prompt-async-gate/timing.d.ts +1 -0
  78. package/dist/shared/prompt-async-gate/types.d.ts +2 -0
  79. package/dist/shared/prompt-async-gate.d.ts +1 -1
  80. package/dist/tools/skill/description-formatter.d.ts +5 -1
  81. package/dist/tools/skill/types.d.ts +1 -0
  82. package/package.json +22 -18
  83. package/packages/ast-grep-mcp/dist/cli.js +53 -9
  84. package/packages/git-bash-mcp/dist/cli.js +367 -0
  85. package/packages/lsp-tools-mcp/dist/lsp/process.js +1 -1
  86. package/packages/omo-codex/plugin/.mcp.json +11 -0
  87. package/packages/omo-codex/plugin/components/comment-checker/README.md +1 -1
  88. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +29 -0
  89. package/packages/omo-codex/plugin/components/git-bash/package.json +23 -0
  90. package/packages/omo-codex/plugin/components/git-bash/src/cli.ts +33 -0
  91. package/packages/omo-codex/plugin/components/git-bash/src/codex-hook.ts +180 -0
  92. package/packages/omo-codex/plugin/components/git-bash/src/index.ts +10 -0
  93. package/packages/omo-codex/plugin/components/git-bash/test/codex-hook.test.ts +195 -0
  94. package/packages/omo-codex/plugin/components/git-bash/tsconfig.build.json +13 -0
  95. package/packages/omo-codex/plugin/components/git-bash/tsconfig.json +25 -0
  96. package/packages/omo-codex/plugin/components/lsp/README.md +1 -1
  97. package/packages/omo-codex/plugin/components/lsp/src/cli.ts +5 -5
  98. package/packages/omo-codex/plugin/components/lsp/src/codex-hook-cli.ts +33 -0
  99. package/packages/omo-codex/plugin/components/lsp/src/codex-hook.ts +19 -27
  100. package/packages/omo-codex/plugin/components/lsp/test/codex-hook-cli.test.ts +28 -0
  101. package/packages/omo-codex/plugin/components/lsp/test/codex-hook-errors.test.ts +55 -0
  102. package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +7 -5
  103. package/packages/omo-codex/plugin/components/rules/README.md +1 -1
  104. package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus.md +6 -4
  105. package/packages/omo-codex/plugin/components/rules/bundled-rules/windows-git-bash.md +10 -0
  106. package/packages/omo-codex/plugin/components/rules/src/post-compact-budget.ts +0 -2
  107. package/packages/omo-codex/plugin/components/rules/test/package-smoke.test.ts +3 -1
  108. package/packages/omo-codex/plugin/components/rules/test/windows-git-bash-bundled-rule.test.ts +97 -0
  109. package/packages/omo-codex/plugin/components/start-work-continuation/directive.md +6 -5
  110. package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +22 -0
  111. package/packages/omo-codex/plugin/components/ultrawork/CHANGELOG.md +1 -1
  112. package/packages/omo-codex/plugin/components/ultrawork/README.md +3 -3
  113. package/packages/omo-codex/plugin/components/ultrawork/agents/codex-ultrawork-reviewer.toml +4 -1
  114. package/packages/omo-codex/plugin/components/ultrawork/agents/librarian.toml +8 -7
  115. package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +9 -8
  116. package/packages/omo-codex/plugin/components/ultrawork/directive.md +32 -6
  117. package/packages/omo-codex/plugin/components/ultrawork/test/codex-hook.test.ts +27 -4
  118. package/packages/omo-codex/plugin/components/ultrawork/test/package-smoke.test.ts +25 -0
  119. package/packages/omo-codex/plugin/components/ulw-loop/README.md +1 -1
  120. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +28 -205
  121. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +231 -0
  122. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +12 -1
  123. package/packages/omo-codex/plugin/components/ulw-loop/test/checkpoint.test.ts +19 -1
  124. package/packages/omo-codex/plugin/components/ulw-loop/test/package-smoke.test.ts +102 -5
  125. package/packages/omo-codex/plugin/hooks/hooks.json +35 -2
  126. package/packages/omo-codex/plugin/model-catalog.json +49 -0
  127. package/packages/omo-codex/plugin/package-lock.json +19 -0
  128. package/packages/omo-codex/plugin/package.json +3 -1
  129. package/packages/omo-codex/plugin/scripts/auto-update.mjs +159 -0
  130. package/packages/omo-codex/plugin/scripts/build-bundled-mcp-runtimes.mjs +16 -1
  131. package/packages/omo-codex/plugin/scripts/build-components.mjs +2 -1
  132. package/packages/omo-codex/plugin/scripts/migrate-codex-config.mjs +269 -0
  133. package/packages/omo-codex/plugin/scripts/sync-hook-status-messages.mjs +89 -0
  134. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +6 -6
  135. package/packages/omo-codex/plugin/skills/init-deep/SKILL.md +6 -6
  136. package/packages/omo-codex/plugin/skills/lcx-report-bug/SKILL.md +127 -0
  137. package/packages/omo-codex/plugin/skills/lcx-report-bug/agents/openai.yaml +9 -0
  138. package/packages/omo-codex/plugin/skills/refactor/SKILL.md +6 -6
  139. package/packages/omo-codex/plugin/skills/remove-ai-slops/SKILL.md +6 -6
  140. package/packages/omo-codex/plugin/skills/review-work/SKILL.md +33 -8
  141. package/packages/omo-codex/plugin/skills/start-work/SKILL.md +25 -5
  142. package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +28 -205
  143. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +231 -0
  144. package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +17 -17
  145. package/packages/omo-codex/plugin/test/aggregate.test.mjs +188 -20
  146. package/packages/omo-codex/plugin/test/auto-update.test.mjs +129 -0
  147. package/packages/omo-codex/plugin/test/hook-status-message.test.mjs +58 -11
  148. package/packages/omo-codex/plugin/test/install-time-build-runtime.test.mjs +34 -0
  149. package/packages/omo-codex/plugin/test/mcp-research-servers.test.mjs +21 -0
  150. package/packages/omo-codex/plugin/test/migrate-codex-config.test.mjs +146 -0
  151. package/packages/omo-codex/plugin/test/node-install-surface.test.mjs +48 -0
  152. package/packages/omo-codex/plugin/test/subagent-guidance.test.mjs +76 -0
  153. package/packages/omo-codex/plugin/test/sync-hook-status-messages.test.mjs +67 -0
  154. package/packages/omo-codex/plugin/test/sync-skills.test.mjs +54 -2
  155. package/packages/omo-codex/scripts/install/cache.mjs +5 -3
  156. package/packages/omo-codex/scripts/install/cli-args.mjs +112 -0
  157. package/packages/omo-codex/scripts/install/config.mjs +23 -1
  158. package/packages/omo-codex/scripts/install/delegated-command.mjs +25 -0
  159. package/packages/omo-codex/scripts/install/git-bash.mjs +99 -0
  160. package/packages/omo-codex/scripts/install/git-bash.test.mjs +174 -0
  161. package/packages/omo-codex/scripts/install/legacy-bins.mjs +1 -0
  162. package/packages/omo-codex/scripts/install/mcp-runtime-cache.mjs +5 -1
  163. package/packages/omo-codex/scripts/install/model-catalog.mjs +66 -0
  164. package/packages/omo-codex/scripts/install/multi-agent-v2-config.mjs +7 -1
  165. package/packages/omo-codex/scripts/install/permissions.d.mts +1 -0
  166. package/packages/omo-codex/scripts/install/permissions.mjs +26 -0
  167. package/packages/omo-codex/scripts/install/project-local-cleanup.mjs +229 -0
  168. package/packages/omo-codex/scripts/install/reasoning-config.mjs +72 -0
  169. package/packages/omo-codex/scripts/install/source-package-build.mjs +20 -0
  170. package/packages/omo-codex/scripts/install/toml-editor.mjs +19 -2
  171. package/packages/omo-codex/scripts/install-bin-links.test.mjs +23 -0
  172. package/packages/omo-codex/scripts/install-cli-args.test.mjs +146 -0
  173. package/packages/omo-codex/scripts/install-config-autonomous.test.mjs +48 -0
  174. package/packages/omo-codex/scripts/install-config-reasoning.test.mjs +141 -0
  175. package/packages/omo-codex/scripts/install-config.test.mjs +205 -0
  176. package/packages/omo-codex/scripts/install-local-entrypoint.test.mjs +157 -0
  177. package/packages/omo-codex/scripts/install-local-git-bash-preflight.test.mjs +145 -0
  178. package/packages/omo-codex/scripts/install-local.mjs +91 -8
  179. package/packages/omo-codex/scripts/install-local.test.mjs +15 -0
  180. package/packages/omo-codex/scripts/install-mcp-runtime.test.mjs +60 -0
  181. package/packages/omo-codex/scripts/install-packaged-local.test.mjs +67 -0
  182. package/packages/omo-codex/scripts/install-project-local-cleanup.test.mjs +277 -0
  183. package/packages/shared-skills/skills/lcx-report-bug/SKILL.md +127 -0
  184. package/packages/shared-skills/skills/lcx-report-bug/agents/openai.yaml +9 -0
  185. package/packages/shared-skills/skills/review-work/SKILL.md +33 -8
  186. package/packages/shared-skills/skills/start-work/SKILL.md +25 -5
  187. package/packages/shared-skills/skills/ulw-plan/SKILL.md +11 -11
  188. package/postinstall.mjs +36 -3
  189. package/dist/hooks/context-window-monitor.d.ts +0 -19
package/dist/cli/index.js CHANGED
@@ -6181,8 +6181,8 @@ var init_model_requirements = __esm(() => {
6181
6181
  { providers: ["opencode-go"], model: "qwen3.5-plus" },
6182
6182
  { providers: ["vercel"], model: "minimax-m2.7-highspeed" },
6183
6183
  { providers: ["opencode-go", "vercel"], model: "minimax-m2.7" },
6184
- { providers: ["anthropic", "opencode", "vercel"], model: "claude-haiku-4-5" },
6185
- { providers: ["openai", "opencode", "vercel"], model: "gpt-5.4-nano" }
6184
+ { providers: ["anthropic", "vercel"], model: "claude-haiku-4-5" },
6185
+ { providers: ["openai", "vercel"], model: "gpt-5.4-nano" }
6186
6186
  ]
6187
6187
  },
6188
6188
  explore: {
@@ -6191,8 +6191,8 @@ var init_model_requirements = __esm(() => {
6191
6191
  { providers: ["opencode-go"], model: "qwen3.5-plus" },
6192
6192
  { providers: ["vercel"], model: "minimax-m2.7-highspeed" },
6193
6193
  { providers: ["opencode-go", "vercel"], model: "minimax-m2.7" },
6194
- { providers: ["anthropic", "opencode", "vercel"], model: "claude-haiku-4-5" },
6195
- { providers: ["openai", "opencode", "vercel"], model: "gpt-5.4-nano" }
6194
+ { providers: ["anthropic", "vercel"], model: "claude-haiku-4-5" },
6195
+ { providers: ["openai", "vercel"], model: "gpt-5.4-nano" }
6196
6196
  ]
6197
6197
  },
6198
6198
  "multimodal-looker": {
@@ -6371,7 +6371,7 @@ var init_model_requirements = __esm(() => {
6371
6371
  model: "gpt-5.4-mini"
6372
6372
  },
6373
6373
  {
6374
- providers: ["anthropic", "github-copilot", "opencode", "vercel"],
6374
+ providers: ["anthropic", "github-copilot", "vercel"],
6375
6375
  model: "claude-haiku-4-5"
6376
6376
  },
6377
6377
  {
@@ -6606,7 +6606,7 @@ var init_model_capability_heuristics = __esm(() => {
6606
6606
  },
6607
6607
  {
6608
6608
  family: "kimi",
6609
- includes: ["kimi", "k2"],
6609
+ pattern: /(?:kimi|k2(?![-.]?p\d))/,
6610
6610
  variants: ["low", "medium", "high"],
6611
6611
  supportsThinking: false
6612
6612
  },
@@ -6679,7 +6679,7 @@ function claudeVersionDot(model) {
6679
6679
  function applyGatewayTransforms(model) {
6680
6680
  return claudeVersionDot(model).replace(GEMINI_31_PRO_PREVIEW, "gemini-3.1-pro-preview");
6681
6681
  }
6682
- function transformModelForProviderUsingAnthropicBehavior(provider, model, directAnthropicTransform) {
6682
+ function transformModelForProviderUsingAnthropicBehavior(provider, model) {
6683
6683
  if (provider === "vercel") {
6684
6684
  const slashIndex = model.indexOf("/");
6685
6685
  if (slashIndex !== -1) {
@@ -6700,12 +6700,12 @@ function transformModelForProviderUsingAnthropicBehavior(provider, model, direct
6700
6700
  return model.replace(GEMINI_31_PRO_PREVIEW, "gemini-3.1-pro-preview").replace(GEMINI_3_FLASH_PREVIEW, "gemini-3-flash-preview");
6701
6701
  }
6702
6702
  if (provider === "anthropic") {
6703
- return directAnthropicTransform(model);
6703
+ return model;
6704
6704
  }
6705
6705
  return model;
6706
6706
  }
6707
6707
  function transformModelForProviderDisplay(provider, model) {
6708
- return transformModelForProviderUsingAnthropicBehavior(provider, model, (model2) => model2);
6708
+ return transformModelForProviderUsingAnthropicBehavior(provider, model);
6709
6709
  }
6710
6710
  var CLAUDE_VERSION_DOT, GEMINI_31_PRO_PREVIEW, GEMINI_3_FLASH_PREVIEW;
6711
6711
  var init_provider_model_id_transform = __esm(() => {
@@ -7874,8 +7874,30 @@ function resolveConfigPath(pathValue) {
7874
7874
  return resolvedPath;
7875
7875
  }
7876
7876
  }
7877
+ function isWslEnvironment() {
7878
+ return process.platform === "linux" && (Boolean(process.env.WSL_DISTRO_NAME?.trim()) || Boolean(process.env.WSL_INTEROP?.trim()));
7879
+ }
7880
+ function isWindowsUserConfigRoot(pathValue) {
7881
+ const normalizedPath = pathValue.replaceAll("\\", "/").toLowerCase();
7882
+ return /^[a-z]:\/users\//.test(normalizedPath) || /^\/mnt\/[a-z]\/users\//.test(normalizedPath);
7883
+ }
7884
+ function getWindowsUserFromConfigRoot(pathValue) {
7885
+ const normalizedPath = pathValue.replaceAll("\\", "/");
7886
+ const match = /^(?:[a-z]:|\/mnt\/[a-z])\/Users\/([^/]+)/i.exec(normalizedPath);
7887
+ return match?.[1] ?? null;
7888
+ }
7889
+ function getWslLinuxHomeDir(windowsConfigRoot) {
7890
+ const envHome = process.env.HOME?.trim();
7891
+ if (envHome && envHome.startsWith("/") && !isWindowsUserConfigRoot(envHome)) {
7892
+ return envHome;
7893
+ }
7894
+ const user = process.env.USER?.trim() || process.env.LOGNAME?.trim() || process.env.SUDO_USER?.trim() || (windowsConfigRoot ? getWindowsUserFromConfigRoot(windowsConfigRoot) : undefined);
7895
+ return user ? join5("/home", user) : null;
7896
+ }
7877
7897
  function getCliDefaultConfigDir() {
7878
- const xdgConfig = process.env.XDG_CONFIG_HOME || join5(homedir3(), ".config");
7898
+ const envXdgConfig = process.env.XDG_CONFIG_HOME?.trim();
7899
+ const shouldIgnoreWindowsXdg = envXdgConfig !== undefined && envXdgConfig.length > 0 && isWslEnvironment() && isWindowsUserConfigRoot(envXdgConfig);
7900
+ const xdgConfig = shouldIgnoreWindowsXdg ? join5(getWslLinuxHomeDir(envXdgConfig) ?? "/home", ".config") : envXdgConfig || join5(homedir3(), ".config");
7879
7901
  return resolveConfigPath(join5(xdgConfig, "opencode"));
7880
7902
  }
7881
7903
  function getCliCustomConfigDir() {
@@ -51711,7 +51733,7 @@ async function withDispatchTimeout(operation, dispatchTimeoutMs, operationName)
51711
51733
  }
51712
51734
  }
51713
51735
  }
51714
- var DEFAULT_PROMPT_ASYNC_POST_DISPATCH_HOLD_MS = 2000, DEFAULT_PROMPT_DISPATCH_TIMEOUT_MS = 30000, DEFAULT_PROMPT_GATE_MESSAGES_FETCH_TIMEOUT_MS = 5000, DEFAULT_PROMPT_QUEUE_RETRY_MS = 250, promptGateMessagesFetchTimeoutMsForTesting;
51736
+ var DEFAULT_PROMPT_ASYNC_POST_DISPATCH_HOLD_MS = 2000, DEFAULT_PROMPT_SEMANTIC_DEDUPE_HOLD_MS = 15000, DEFAULT_PROMPT_DISPATCH_TIMEOUT_MS = 30000, DEFAULT_PROMPT_GATE_MESSAGES_FETCH_TIMEOUT_MS = 5000, DEFAULT_PROMPT_QUEUE_RETRY_MS = 250, promptGateMessagesFetchTimeoutMsForTesting;
51715
51737
 
51716
51738
  // src/shared/prompt-async-gate/pending-tool-turn.ts
51717
51739
  function getPromptQuery(input) {
@@ -51808,6 +51830,42 @@ var init_pending_tool_turn = __esm(() => {
51808
51830
  init_message_inspection_error();
51809
51831
  });
51810
51832
 
51833
+ // src/shared/prompt-async-gate/recent-dispatches.ts
51834
+ function recentDispatchKey(sessionID, dedupeKey) {
51835
+ return `${sessionID}\x00${dedupeKey}`;
51836
+ }
51837
+ function pruneRecentPromptDispatches(now = Date.now()) {
51838
+ for (const [key, dispatch] of recentPromptDispatches) {
51839
+ if (dispatch.expiresAt <= now) {
51840
+ recentPromptDispatches.delete(key);
51841
+ }
51842
+ }
51843
+ }
51844
+ function getRecentPromptDispatch(sessionID, dedupeKey) {
51845
+ pruneRecentPromptDispatches();
51846
+ return recentPromptDispatches.get(recentDispatchKey(sessionID, dedupeKey));
51847
+ }
51848
+ function rememberRecentPromptDispatch(args) {
51849
+ pruneRecentPromptDispatches();
51850
+ if (args.holdMs <= 0) {
51851
+ return;
51852
+ }
51853
+ recentPromptDispatches.set(recentDispatchKey(args.sessionID, args.dedupeKey), {
51854
+ source: args.source,
51855
+ expiresAt: Date.now() + args.holdMs
51856
+ });
51857
+ log("[prompt-async-gate] remembered semantic prompt dispatch", {
51858
+ sessionID: args.sessionID,
51859
+ source: args.source,
51860
+ holdMs: args.holdMs
51861
+ });
51862
+ }
51863
+ var recentPromptDispatches;
51864
+ var init_recent_dispatches = __esm(() => {
51865
+ init_logger();
51866
+ recentPromptDispatches = new Map;
51867
+ });
51868
+
51811
51869
  // src/shared/prompt-async-gate/reservations.ts
51812
51870
  function setExpiredReservationHandler(handler) {
51813
51871
  expiredReservationHandler = handler;
@@ -51869,6 +51927,7 @@ async function dispatchAfterSessionIdle(args) {
51869
51927
  dedupeKey,
51870
51928
  settleMs,
51871
51929
  postDispatchHoldMs,
51930
+ semanticDedupeHoldMs,
51872
51931
  dispatchTimeoutMs,
51873
51932
  checkStatus,
51874
51933
  checkToolState,
@@ -51926,10 +51985,25 @@ async function dispatchAfterSessionIdle(args) {
51926
51985
  log(`[prompt-async-gate] ${sessionName} dispatching`, { sessionID, source });
51927
51986
  dispatchAttempted = true;
51928
51987
  const response = await withDispatchTimeout(dispatch(input), dispatchTimeoutMs, `[prompt-async-gate] ${sessionName} dispatch`);
51988
+ rememberRecentPromptDispatch({
51989
+ sessionID,
51990
+ dedupeKey,
51991
+ source,
51992
+ holdMs: semanticDedupeHoldMs
51993
+ });
51929
51994
  log(`[prompt-async-gate] ${sessionName} dispatched`, { sessionID, source });
51930
51995
  return { status: "dispatched", response };
51931
51996
  } catch (error) {
51932
- log(`[prompt-async-gate] ${sessionName} failed`, { sessionID, source, error: String(error) });
51997
+ if (dispatchAttempted) {
51998
+ rememberRecentPromptDispatch({
51999
+ sessionID,
52000
+ dedupeKey,
52001
+ source,
52002
+ holdMs: semanticDedupeHoldMs
52003
+ });
52004
+ }
52005
+ const errorText = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
52006
+ log(`[prompt-async-gate] ${sessionName} failed`, { sessionID, source, error: errorText });
51933
52007
  return { status: "failed", error, dispatchAttempted };
51934
52008
  } finally {
51935
52009
  finishPromptReservation(sessionID, reservation, dispatchAttempted, postDispatchHoldMs);
@@ -51939,6 +52013,7 @@ var init_session_idle_dispatch = __esm(() => {
51939
52013
  init_logger();
51940
52014
  init_session_idle_settle();
51941
52015
  init_pending_tool_turn();
52016
+ init_recent_dispatches();
51942
52017
  init_reservations();
51943
52018
  });
51944
52019
 
@@ -52038,6 +52113,7 @@ async function drainPromptQueue(sessionID, awaitedEntry) {
52038
52113
  dedupeKey: entry.dedupeKey,
52039
52114
  settleMs: entry.settleMs,
52040
52115
  postDispatchHoldMs: entry.postDispatchHoldMs,
52116
+ semanticDedupeHoldMs: entry.semanticDedupeHoldMs,
52041
52117
  dispatchTimeoutMs: entry.dispatchTimeoutMs,
52042
52118
  checkStatus: entry.checkStatus,
52043
52119
  checkToolState: entry.checkToolState,
@@ -52121,27 +52197,119 @@ var init_queue = __esm(() => {
52121
52197
  });
52122
52198
  });
52123
52199
 
52124
- // src/shared/prompt-async-gate.ts
52200
+ // src/shared/prompt-async-gate/semantic-dedupe.ts
52201
+ import { createHash } from "crypto";
52202
+ function isPlainRecord(value) {
52203
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
52204
+ return false;
52205
+ }
52206
+ const prototype = Object.getPrototypeOf(value);
52207
+ return prototype === Object.prototype || prototype === null;
52208
+ }
52209
+ function canonicalizePromptInputForDedupe(key, value, seen = new WeakSet, depth = 0) {
52210
+ if (key === "signal") {
52211
+ return "[AbortSignal]";
52212
+ }
52213
+ if (typeof value === "function") {
52214
+ return `[Function:${value.name}]`;
52215
+ }
52216
+ if (depth > MAX_PROMPT_DEDUPE_DEPTH) {
52217
+ return "[MaxDepth]";
52218
+ }
52219
+ if (Array.isArray(value)) {
52220
+ if (seen.has(value)) {
52221
+ return "[Circular]";
52222
+ }
52223
+ seen.add(value);
52224
+ try {
52225
+ return value.map((entry) => canonicalizePromptInputForDedupe("", entry, seen, depth + 1));
52226
+ } finally {
52227
+ seen.delete(value);
52228
+ }
52229
+ }
52230
+ if (!isPlainRecord(value)) {
52231
+ return value;
52232
+ }
52233
+ if (seen.has(value)) {
52234
+ return "[Circular]";
52235
+ }
52236
+ seen.add(value);
52237
+ try {
52238
+ const canonicalEntries = [];
52239
+ for (const entryKey of Object.keys(value).sort()) {
52240
+ canonicalEntries.push([
52241
+ entryKey,
52242
+ canonicalizePromptInputForDedupe(entryKey, value[entryKey], seen, depth + 1)
52243
+ ]);
52244
+ }
52245
+ return Object.fromEntries(canonicalEntries);
52246
+ } finally {
52247
+ seen.delete(value);
52248
+ }
52249
+ }
52250
+ function isContinuationTextPartLike(value) {
52251
+ if (!isPlainRecord(value)) {
52252
+ return false;
52253
+ }
52254
+ if (value.type !== "text" || typeof value.text !== "string") {
52255
+ return false;
52256
+ }
52257
+ if (!hasInternalInitiatorMarker(value.text)) {
52258
+ return false;
52259
+ }
52260
+ if (!isPlainRecord(value.metadata)) {
52261
+ return false;
52262
+ }
52263
+ return value.metadata.compaction_continue === true;
52264
+ }
52265
+ function hasContinuationPromptIntent(input) {
52266
+ if (!isPlainRecord(input) || !isPlainRecord(input.body) || !Array.isArray(input.body.parts)) {
52267
+ return false;
52268
+ }
52269
+ return input.body.parts.some((part) => isContinuationTextPartLike(part));
52270
+ }
52271
+ function normalizePromptInputForSemanticDedupe(input) {
52272
+ if (hasContinuationPromptIntent(input)) {
52273
+ return {
52274
+ __omo_internal_intent: "continuation"
52275
+ };
52276
+ }
52277
+ return canonicalizePromptInputForDedupe("", input);
52278
+ }
52125
52279
  function stringifyPromptInputForDedupe(input) {
52126
52280
  try {
52127
- const serialized = JSON.stringify(input, (key, value) => {
52128
- if (key === "signal") {
52129
- return "[AbortSignal]";
52130
- }
52131
- if (typeof value === "function") {
52132
- return `[Function:${value.name}]`;
52133
- }
52134
- return value;
52135
- });
52281
+ const serialized = JSON.stringify(normalizePromptInputForSemanticDedupe(input));
52136
52282
  return serialized ?? String(input);
52137
- } catch {
52138
- return String(input);
52283
+ } catch (error) {
52284
+ const errorTag = error instanceof Error ? error.name : String(error);
52285
+ return `${String(input)}:[unserializable:${errorTag}]`;
52139
52286
  }
52140
52287
  }
52141
- function createDefaultDedupeKey(source, input) {
52288
+ function createSemanticPromptDedupeKey(input) {
52142
52289
  const fingerprint = stringifyPromptInputForDedupe(input);
52143
- return `${source}:${fingerprint.length}:${fingerprint.slice(0, 8192)}`;
52290
+ const digest = createHash("sha256").update(fingerprint, "utf8").digest("hex");
52291
+ return `semantic:${digest}`;
52292
+ }
52293
+ function coalesceRecentSemanticPromptDispatch(args) {
52294
+ const recentDispatch = getRecentPromptDispatch(args.sessionID, args.dedupeKey);
52295
+ if (!recentDispatch) {
52296
+ return;
52297
+ }
52298
+ log("[prompt-async-gate] prompt coalesced with recent semantic dispatch", {
52299
+ sessionID: args.sessionID,
52300
+ source: args.source,
52301
+ queuedBy: recentDispatch.source
52302
+ });
52303
+ return { status: "queued", queuedBy: recentDispatch.source, position: 0 };
52144
52304
  }
52305
+ var MAX_PROMPT_DEDUPE_DEPTH = 64;
52306
+ var init_semantic_dedupe = __esm(() => {
52307
+ init_internal_initiator_marker();
52308
+ init_logger();
52309
+ init_recent_dispatches();
52310
+ });
52311
+
52312
+ // src/shared/prompt-async-gate.ts
52145
52313
  function hasObjectSessionPath(input) {
52146
52314
  return typeof input === "object" && input !== null && "path" in input && typeof input.path === "object" && input.path !== null && "id" in input.path && typeof input.path.id === "string";
52147
52315
  }
@@ -52171,9 +52339,10 @@ async function dispatchInternalPrompt(args) {
52171
52339
  source,
52172
52340
  settleMs = DEFAULT_SESSION_IDLE_SETTLE_MS
52173
52341
  } = args;
52174
- const dedupeKey = args.dedupeKey ?? createDefaultDedupeKey(source, input);
52342
+ const dedupeKey = args.dedupeKey ?? createSemanticPromptDedupeKey(input);
52175
52343
  const queueRetryMs = args.queueRetryMs ?? DEFAULT_PROMPT_QUEUE_RETRY_MS;
52176
52344
  const postDispatchHoldMs = args.postDispatchHoldMs ?? DEFAULT_PROMPT_ASYNC_POST_DISPATCH_HOLD_MS;
52345
+ const semanticDedupeHoldMs = args.semanticDedupeHoldMs ?? (postDispatchHoldMs > 0 ? DEFAULT_PROMPT_SEMANTIC_DEDUPE_HOLD_MS : 0);
52177
52346
  const dispatchTimeoutMs = args.dispatchTimeoutMs ?? DEFAULT_PROMPT_DISPATCH_TIMEOUT_MS;
52178
52347
  const sessionName = args.mode === "async" ? "promptAsync" : "prompt";
52179
52348
  const dispatch = (() => {
@@ -52206,6 +52375,10 @@ async function dispatchInternalPrompt(args) {
52206
52375
  if (queuedBy !== undefined || isPromptQueueDraining(sessionID)) {
52207
52376
  return { status: "reserved", reservedBy: queuedBy ?? source };
52208
52377
  }
52378
+ const recentDispatchResult2 = coalesceRecentSemanticPromptDispatch({ sessionID, dedupeKey, source });
52379
+ if (recentDispatchResult2) {
52380
+ return recentDispatchResult2;
52381
+ }
52209
52382
  return dispatchAfterSessionIdle({
52210
52383
  sessionName,
52211
52384
  client,
@@ -52215,6 +52388,7 @@ async function dispatchInternalPrompt(args) {
52215
52388
  dedupeKey,
52216
52389
  settleMs,
52217
52390
  postDispatchHoldMs,
52391
+ semanticDedupeHoldMs,
52218
52392
  dispatchTimeoutMs,
52219
52393
  checkStatus: args.checkStatus !== false,
52220
52394
  checkToolState: args.checkToolState !== false,
@@ -52222,6 +52396,10 @@ async function dispatchInternalPrompt(args) {
52222
52396
  });
52223
52397
  }
52224
52398
  if (args.queue !== false) {
52399
+ const recentDispatchResult2 = coalesceRecentSemanticPromptDispatch({ sessionID, dedupeKey, source });
52400
+ if (recentDispatchResult2) {
52401
+ return recentDispatchResult2;
52402
+ }
52225
52403
  return enqueueInternalPrompt({
52226
52404
  id: nextPromptQueueID(),
52227
52405
  sessionID,
@@ -52232,6 +52410,7 @@ async function dispatchInternalPrompt(args) {
52232
52410
  dedupeKey,
52233
52411
  settleMs,
52234
52412
  postDispatchHoldMs,
52413
+ semanticDedupeHoldMs,
52235
52414
  dispatchTimeoutMs,
52236
52415
  queueRetryMs,
52237
52416
  checkStatus: args.checkStatus !== false,
@@ -52239,6 +52418,10 @@ async function dispatchInternalPrompt(args) {
52239
52418
  dispatch: async (_dispatchInput) => dispatchWithPathCompatibility(dispatch, input)
52240
52419
  });
52241
52420
  }
52421
+ const recentDispatchResult = coalesceRecentSemanticPromptDispatch({ sessionID, dedupeKey, source });
52422
+ if (recentDispatchResult) {
52423
+ return recentDispatchResult;
52424
+ }
52242
52425
  return dispatchAfterSessionIdle({
52243
52426
  sessionName,
52244
52427
  client,
@@ -52248,6 +52431,7 @@ async function dispatchInternalPrompt(args) {
52248
52431
  dedupeKey,
52249
52432
  settleMs,
52250
52433
  postDispatchHoldMs,
52434
+ semanticDedupeHoldMs,
52251
52435
  dispatchTimeoutMs,
52252
52436
  checkStatus: args.checkStatus !== false,
52253
52437
  checkToolState: args.checkToolState !== false,
@@ -52261,8 +52445,10 @@ var init_prompt_async_gate = __esm(() => {
52261
52445
  init_logger();
52262
52446
  init_session_idle_settle();
52263
52447
  init_queue();
52448
+ init_recent_dispatches();
52264
52449
  init_reservations();
52265
52450
  init_session_idle_dispatch();
52451
+ init_semantic_dedupe();
52266
52452
  });
52267
52453
 
52268
52454
  // src/shared/prompt-failure-classifier.ts
@@ -53453,18 +53639,10 @@ function generateModelConfig(config) {
53453
53639
  const categories = {};
53454
53640
  for (const [role, req] of Object.entries(CLI_AGENT_MODEL_REQUIREMENTS)) {
53455
53641
  if (role === "librarian") {
53456
- let agentConfig;
53457
- if (avail.native.openai) {
53458
- agentConfig = { model: "openai/gpt-5.4-mini-fast" };
53459
- } else if (avail.opencodeGo) {
53460
- agentConfig = { model: "opencode-go/qwen3.5-plus" };
53461
- } else if (avail.zai) {
53462
- agentConfig = { model: ZAI_MODEL };
53463
- } else if (avail.vercelAiGateway) {
53464
- agentConfig = { model: "vercel/minimax/minimax-m2.7" };
53465
- }
53466
- if (agentConfig) {
53467
- agents[role] = attachAllFallbackModels(agentConfig, req.fallbackChain, avail);
53642
+ const resolved2 = resolveModelFromChain(req.fallbackChain, avail);
53643
+ if (resolved2) {
53644
+ const agentConfig = resolved2.variant ? { model: resolved2.model, variant: resolved2.variant } : { model: resolved2.model };
53645
+ agents[role] = attachFallbackModels(agentConfig, req.fallbackChain, avail);
53468
53646
  }
53469
53647
  continue;
53470
53648
  }
@@ -53475,7 +53653,7 @@ function generateModelConfig(config) {
53475
53653
  } else if (avail.native.claude) {
53476
53654
  agentConfig = { model: "anthropic/claude-haiku-4-5" };
53477
53655
  } else if (avail.opencodeZen) {
53478
- agentConfig = { model: "opencode/claude-haiku-4-5" };
53656
+ agentConfig = { model: "opencode/gpt-5-nano" };
53479
53657
  } else if (avail.opencodeGo) {
53480
53658
  agentConfig = { model: "opencode-go/qwen3.5-plus" };
53481
53659
  } else if (avail.copilot) {
@@ -53540,7 +53718,7 @@ function generateModelConfig(config) {
53540
53718
  };
53541
53719
  return isOpenAiOnlyAvailability(avail) ? applyOpenAiOnlyModelCatalog(generatedConfig) : generatedConfig;
53542
53720
  }
53543
- var ZAI_MODEL = "zai-coding-plan/glm-4.7", ULTIMATE_FALLBACK = "opencode/gpt-5-nano", SCHEMA_URL = "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json";
53721
+ var ULTIMATE_FALLBACK = "opencode/gpt-5-nano", SCHEMA_URL = "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json";
53544
53722
  var init_model_fallback = __esm(() => {
53545
53723
  init_model_fallback_requirements();
53546
53724
  init_openai_only_model_catalog();
@@ -54105,7 +54283,7 @@ var init_config_manager = __esm(() => {
54105
54283
  });
54106
54284
 
54107
54285
  // node_modules/.bun/posthog-node@5.35.3/node_modules/posthog-node/dist/extensions/error-tracking/modifiers/module.node.mjs
54108
- import { dirname as dirname13, posix, sep as sep3 } from "path";
54286
+ import { dirname as dirname14, posix, sep as sep3 } from "path";
54109
54287
  function createModulerModifier() {
54110
54288
  const getModuleFromFileName = createGetModuleFromFilename();
54111
54289
  return async (frames) => {
@@ -54114,7 +54292,7 @@ function createModulerModifier() {
54114
54292
  return frames;
54115
54293
  };
54116
54294
  }
54117
- function createGetModuleFromFilename(basePath = process.argv[1] ? dirname13(process.argv[1]) : process.cwd(), isWindows = sep3 === "\\") {
54295
+ function createGetModuleFromFilename(basePath = process.argv[1] ? dirname14(process.argv[1]) : process.cwd(), isWindows = sep3 === "\\") {
54118
54296
  const normalizedBase = isWindows ? normalizeWindowsPath(basePath) : basePath;
54119
54297
  return (filename) => {
54120
54298
  if (!filename)
@@ -56919,14 +57097,14 @@ async function addSourceContext(frames) {
56919
57097
  return frames;
56920
57098
  }
56921
57099
  function getContextLinesFromFile(path6, ranges, output) {
56922
- return new Promise((resolve8) => {
57100
+ return new Promise((resolve9) => {
56923
57101
  const stream = createReadStream(path6);
56924
57102
  const lineReaded = createInterface({
56925
57103
  input: stream
56926
57104
  });
56927
57105
  function destroyStreamAndResolve() {
56928
57106
  stream.destroy();
56929
- resolve8();
57107
+ resolve9();
56930
57108
  }
56931
57109
  let lineNumber = 0;
56932
57110
  let currentRangeIndex = 0;
@@ -58398,9 +58576,9 @@ var init_client = __esm(() => {
58398
58576
  if (this.disabled || this.optedOut)
58399
58577
  return;
58400
58578
  if (!this._waitUntilCycle) {
58401
- let resolve8;
58579
+ let resolve9;
58402
58580
  const promise = new Promise((r) => {
58403
- resolve8 = r;
58581
+ resolve9 = r;
58404
58582
  });
58405
58583
  try {
58406
58584
  waitUntil(promise);
@@ -58408,7 +58586,7 @@ var init_client = __esm(() => {
58408
58586
  return;
58409
58587
  }
58410
58588
  this._waitUntilCycle = {
58411
- resolve: resolve8,
58589
+ resolve: resolve9,
58412
58590
  startedAt: Date.now(),
58413
58591
  timer: undefined
58414
58592
  };
@@ -58434,11 +58612,11 @@ var init_client = __esm(() => {
58434
58612
  return cycle?.resolve;
58435
58613
  }
58436
58614
  async resolveWaitUntilFlush() {
58437
- const resolve8 = this._consumeWaitUntilCycle();
58615
+ const resolve9 = this._consumeWaitUntilCycle();
58438
58616
  try {
58439
58617
  await super.flush();
58440
58618
  } catch {} finally {
58441
- resolve8?.();
58619
+ resolve9?.();
58442
58620
  }
58443
58621
  }
58444
58622
  getPersistedProperty(key) {
@@ -58538,15 +58716,15 @@ var init_client = __esm(() => {
58538
58716
  return true;
58539
58717
  if (this.featureFlagsPoller === undefined)
58540
58718
  return false;
58541
- return new Promise((resolve8) => {
58719
+ return new Promise((resolve9) => {
58542
58720
  const timeout = setTimeout(() => {
58543
58721
  cleanup();
58544
- resolve8(false);
58722
+ resolve9(false);
58545
58723
  }, timeoutMs);
58546
58724
  const cleanup = this._events.on("localEvaluationFlagsLoaded", (count) => {
58547
58725
  clearTimeout(timeout);
58548
58726
  cleanup();
58549
- resolve8(count > 0);
58727
+ resolve9(count > 0);
58550
58728
  });
58551
58729
  });
58552
58730
  }
@@ -59033,13 +59211,13 @@ var init_client = __esm(() => {
59033
59211
  this.context?.enter(data, options);
59034
59212
  }
59035
59213
  async _shutdown(shutdownTimeoutMs) {
59036
- const resolve8 = this._consumeWaitUntilCycle();
59214
+ const resolve9 = this._consumeWaitUntilCycle();
59037
59215
  await this.featureFlagsPoller?.stopPoller(shutdownTimeoutMs);
59038
59216
  this.errorTracking.shutdown();
59039
59217
  try {
59040
59218
  return await super._shutdown(shutdownTimeoutMs);
59041
59219
  } finally {
59042
- resolve8?.();
59220
+ resolve9?.();
59043
59221
  }
59044
59222
  }
59045
59223
  async _requestRemoteConfigPayload(flagKey) {
@@ -59435,7 +59613,10 @@ var init_package = __esm(() => {
59435
59613
  types: "./index.d.ts",
59436
59614
  import: "./src/index.ts"
59437
59615
  },
59438
- "./telemetry": "./src/telemetry/index.ts",
59616
+ "./telemetry": {
59617
+ types: "./src/telemetry/index.ts",
59618
+ import: "./src/telemetry/index.ts"
59619
+ },
59439
59620
  "./marketplace.json": "./marketplace.json"
59440
59621
  },
59441
59622
  types: "./index.d.ts",
@@ -59543,10 +59724,10 @@ var init_data_path2 = __esm(() => {
59543
59724
  });
59544
59725
 
59545
59726
  // packages/omo-codex/src/telemetry/posthog-activity-state.ts
59546
- import { existsSync as existsSync22, mkdirSync as mkdirSync8, readFileSync as readFileSync13 } from "fs";
59547
- import { join as join27 } from "path";
59727
+ import { existsSync as existsSync24, mkdirSync as mkdirSync8, readFileSync as readFileSync13 } from "fs";
59728
+ import { join as join30 } from "path";
59548
59729
  function getPostHogActivityStateFilePath() {
59549
- return join27(getActivityStateDir(), POSTHOG_ACTIVITY_STATE_FILE);
59730
+ return join30(getActivityStateDir(), POSTHOG_ACTIVITY_STATE_FILE);
59550
59731
  }
59551
59732
  function getUtcDayString(date) {
59552
59733
  return date.toISOString().slice(0, 10);
@@ -59556,7 +59737,7 @@ function isPostHogActivityState(value) {
59556
59737
  }
59557
59738
  function readPostHogActivityState() {
59558
59739
  const stateFilePath = getPostHogActivityStateFilePath();
59559
- if (!existsSync22(stateFilePath)) {
59740
+ if (!existsSync24(stateFilePath)) {
59560
59741
  return {};
59561
59742
  }
59562
59743
  try {
@@ -59603,7 +59784,7 @@ var init_posthog_activity_state = __esm(() => {
59603
59784
  });
59604
59785
 
59605
59786
  // packages/omo-codex/src/telemetry/posthog.ts
59606
- import { createHash as createHash2 } from "crypto";
59787
+ import { createHash as createHash3 } from "crypto";
59607
59788
  import os4 from "os";
59608
59789
  function resolveOsProvider() {
59609
59790
  return osProviderOverride2 ?? os4;
@@ -59686,7 +59867,7 @@ function createPostHogClient(source, options) {
59686
59867
  };
59687
59868
  }
59688
59869
  function getPostHogDistinctId() {
59689
- return createHash2("sha256").update(`omo-codex:${resolveOsProvider().hostname()}`).digest("hex");
59870
+ return createHash3("sha256").update(`omo-codex:${resolveOsProvider().hostname()}`).digest("hex");
59690
59871
  }
59691
59872
  function createCliPostHog() {
59692
59873
  return createPostHogClient("cli", {
@@ -59910,12 +60091,12 @@ var require_isexe = __commonJS((exports2, module) => {
59910
60091
  if (typeof Promise !== "function") {
59911
60092
  throw new TypeError("callback not provided");
59912
60093
  }
59913
- return new Promise(function(resolve9, reject) {
60094
+ return new Promise(function(resolve11, reject) {
59914
60095
  isexe(path8, options || {}, function(er, is) {
59915
60096
  if (er) {
59916
60097
  reject(er);
59917
60098
  } else {
59918
- resolve9(is);
60099
+ resolve11(is);
59919
60100
  }
59920
60101
  });
59921
60102
  });
@@ -59977,27 +60158,27 @@ var require_which = __commonJS((exports2, module) => {
59977
60158
  opt = {};
59978
60159
  const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
59979
60160
  const found = [];
59980
- const step = (i2) => new Promise((resolve9, reject) => {
60161
+ const step = (i2) => new Promise((resolve11, reject) => {
59981
60162
  if (i2 === pathEnv.length)
59982
- return opt.all && found.length ? resolve9(found) : reject(getNotFoundError(cmd));
60163
+ return opt.all && found.length ? resolve11(found) : reject(getNotFoundError(cmd));
59983
60164
  const ppRaw = pathEnv[i2];
59984
60165
  const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
59985
60166
  const pCmd = path8.join(pathPart, cmd);
59986
60167
  const p2 = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
59987
- resolve9(subStep(p2, i2, 0));
60168
+ resolve11(subStep(p2, i2, 0));
59988
60169
  });
59989
- const subStep = (p2, i2, ii) => new Promise((resolve9, reject) => {
60170
+ const subStep = (p2, i2, ii) => new Promise((resolve11, reject) => {
59990
60171
  if (ii === pathExt.length)
59991
- return resolve9(step(i2 + 1));
60172
+ return resolve11(step(i2 + 1));
59992
60173
  const ext = pathExt[ii];
59993
60174
  isexe(p2 + ext, { pathExt: pathExtExe }, (er, is) => {
59994
60175
  if (!er && is) {
59995
60176
  if (opt.all)
59996
60177
  found.push(p2 + ext);
59997
60178
  else
59998
- return resolve9(p2 + ext);
60179
+ return resolve11(p2 + ext);
59999
60180
  }
60000
- return resolve9(subStep(p2, i2, ii + 1));
60181
+ return resolve11(subStep(p2, i2, ii + 1));
60001
60182
  });
60002
60183
  });
60003
60184
  return cb ? step(0).then((res) => cb(null, res), cb) : step(0);
@@ -60894,8 +61075,8 @@ var init_update_toasts = __esm(() => {
60894
61075
  });
60895
61076
 
60896
61077
  // src/hooks/auto-update-checker/hook/background-update-check.ts
60897
- import { existsSync as existsSync38 } from "fs";
60898
- import { join as join41 } from "path";
61078
+ import { existsSync as existsSync41 } from "fs";
61079
+ import { join as join47 } from "path";
60899
61080
  function getCacheWorkspaceDir(deps) {
60900
61081
  return deps.join(deps.getOpenCodeCacheDir(), "packages");
60901
61082
  }
@@ -61013,8 +61194,8 @@ var init_background_update_check = __esm(() => {
61013
61194
  init_checker();
61014
61195
  init_update_toasts();
61015
61196
  defaultDeps3 = {
61016
- existsSync: existsSync38,
61017
- join: join41,
61197
+ existsSync: existsSync41,
61198
+ join: join47,
61018
61199
  runBunInstallWithDetails,
61019
61200
  log,
61020
61201
  getOpenCodeCacheDir,
@@ -61173,7 +61354,7 @@ async function showSpinnerToast(ctx, version3, message) {
61173
61354
  duration: frameInterval + 50
61174
61355
  }
61175
61356
  }).catch(() => {});
61176
- await new Promise((resolve10) => setTimeout(resolve10, frameInterval));
61357
+ await new Promise((resolve12) => setTimeout(resolve12, frameInterval));
61177
61358
  }
61178
61359
  }
61179
61360
  var SISYPHUS_SPINNER;
@@ -61256,13 +61437,13 @@ v${latestVersion} available. Restart OpenCode to apply.` : "OpenCode is now on S
61256
61437
  }
61257
61438
  };
61258
61439
  }
61259
- var defaultDeps4, isRecord13 = (value) => {
61440
+ var defaultDeps4, isRecord16 = (value) => {
61260
61441
  return typeof value === "object" && value !== null;
61261
61442
  }, getParentID = (properties) => {
61262
- if (!isRecord13(properties))
61443
+ if (!isRecord16(properties))
61263
61444
  return;
61264
61445
  const { info } = properties;
61265
- if (!isRecord13(info))
61446
+ if (!isRecord16(info))
61266
61447
  return;
61267
61448
  const { parentID } = info;
61268
61449
  return typeof parentID === "string" && parentID.length > 0 ? parentID : undefined;
@@ -61326,7 +61507,7 @@ var {
61326
61507
  // package.json
61327
61508
  var package_default = {
61328
61509
  name: "oh-my-opencode",
61329
- version: "4.5.12",
61510
+ version: "4.7.0",
61330
61511
  description: "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
61331
61512
  main: "./dist/index.js",
61332
61513
  types: "dist/index.d.ts",
@@ -61335,6 +61516,7 @@ var package_default = {
61335
61516
  "packages/rules-engine",
61336
61517
  "packages/ast-grep-core",
61337
61518
  "packages/ast-grep-mcp",
61519
+ "packages/git-bash-mcp",
61338
61520
  "packages/utils",
61339
61521
  "packages/model-core",
61340
61522
  "packages/prompts-core",
@@ -61349,7 +61531,8 @@ var package_default = {
61349
61531
  "oh-my-opencode": "bin/oh-my-opencode.js",
61350
61532
  "oh-my-openagent": "bin/oh-my-opencode.js",
61351
61533
  omo: "bin/oh-my-opencode.js",
61352
- lazycodex: "bin/oh-my-opencode.js"
61534
+ lazycodex: "bin/oh-my-opencode.js",
61535
+ "lazycodex-ai": "bin/oh-my-opencode.js"
61353
61536
  },
61354
61537
  files: [
61355
61538
  "dist",
@@ -61361,6 +61544,7 @@ var package_default = {
61361
61544
  ".agents/skills",
61362
61545
  "packages/lsp-tools-mcp/dist",
61363
61546
  "packages/ast-grep-mcp/dist",
61547
+ "packages/git-bash-mcp/dist",
61364
61548
  "packages/shared-skills/package.json",
61365
61549
  "packages/shared-skills/index.mjs",
61366
61550
  "packages/shared-skills/skills",
@@ -61378,7 +61562,7 @@ var package_default = {
61378
61562
  "./schema.json": "./dist/oh-my-opencode.schema.json"
61379
61563
  },
61380
61564
  scripts: {
61381
- build: "bun run build:ast-grep-mcp && bun build src/index.ts --outdir dist --target bun --format esm --external @ast-grep/napi --external zod && bun run build:node-require-shim && tsc --emitDeclarationOnly && bun build src/cli/index.ts --outdir dist/cli --target bun --format esm --external @ast-grep/napi && bun run build:schema",
61565
+ build: "bun run build:ast-grep-mcp && bun run build:git-bash-mcp && bun build src/index.ts --outdir dist --target bun --format esm --external @ast-grep/napi --external zod && bun run build:node-require-shim && tsc --emitDeclarationOnly && bun build src/cli/index.ts --outdir dist/cli --target bun --format esm --external @ast-grep/napi && bun run build:schema",
61382
61566
  "build:lsp-tools-mcp": "npm --prefix packages/lsp-tools-mcp ci && npm --prefix packages/lsp-tools-mcp run build",
61383
61567
  "build:node-require-shim": "bun run script/patch-node-require-shim.ts",
61384
61568
  "build:all": "bun run build && bun run build:binaries",
@@ -61391,12 +61575,13 @@ var package_default = {
61391
61575
  prepublishOnly: "bun run clean && bun run build:lsp-tools-mcp && bun run build",
61392
61576
  "test:model-capabilities": "bun test src/shared/model-capability-aliases.test.ts src/shared/model-capability-guardrails.test.ts src/shared/model-capabilities.test.ts src/cli/doctor/checks/model-resolution.test.ts --bail",
61393
61577
  typecheck: "tsgo --noEmit && bun run typecheck:packages",
61394
- "typecheck:packages": "tsgo --noEmit -p packages/rules-engine/tsconfig.json && tsgo --noEmit -p packages/ast-grep-core/tsconfig.json && tsgo --noEmit -p packages/ast-grep-mcp/tsconfig.json && tsgo --noEmit -p packages/utils/tsconfig.json && tsgo --noEmit -p packages/model-core/tsconfig.json && tsgo --noEmit -p packages/prompts-core/tsconfig.json && tsgo --noEmit -p packages/comment-checker-core/tsconfig.json && tsgo --noEmit -p packages/hashline-core/tsconfig.json && tsgo --noEmit -p packages/boulder-state/tsconfig.json && tsgo --noEmit -p packages/agents-md-core/tsconfig.json && tsgo --noEmit -p packages/omo-codex/tsconfig.json",
61578
+ "typecheck:packages": "tsgo --noEmit -p packages/rules-engine/tsconfig.json && tsgo --noEmit -p packages/ast-grep-core/tsconfig.json && tsgo --noEmit -p packages/ast-grep-mcp/tsconfig.json && tsgo --noEmit -p packages/git-bash-mcp/tsconfig.json && tsgo --noEmit -p packages/utils/tsconfig.json && tsgo --noEmit -p packages/model-core/tsconfig.json && tsgo --noEmit -p packages/prompts-core/tsconfig.json && tsgo --noEmit -p packages/comment-checker-core/tsconfig.json && tsgo --noEmit -p packages/hashline-core/tsconfig.json && tsgo --noEmit -p packages/boulder-state/tsconfig.json && tsgo --noEmit -p packages/agents-md-core/tsconfig.json && tsgo --noEmit -p packages/omo-codex/tsconfig.json",
61395
61579
  "typecheck:script": "tsgo --noEmit -p script/tsconfig.json",
61396
61580
  test: "bun test",
61397
- "test:codex": "bun run build:ast-grep-mcp && bun run build:lsp-tools-mcp && npm --prefix packages/omo-codex/plugin ci && bun run --cwd packages/omo-codex/plugin build && bun test src/cli/cli-installer.platform.test.ts src/cli/install-codex/codex-cache.test.ts src/cli/install-codex/codex-config-agent-cleanup.test.ts src/cli/install-codex/codex-config-toml.test.ts src/cli/install-codex/install-codex.test.ts src/cli/install-codex/link-cached-plugin-agents.test.ts packages/omo-codex/src/**/*.test.ts packages/utils/src/jsonc-parser.test.ts packages/utils/src/frontmatter.test.ts packages/hashline-core/src/hash-computation.test.ts packages/hashline-core/src/smoke-untested-modules.test.ts packages/rules-engine/src/index.test.ts packages/rules-engine/src/security-boundary.test.ts packages/agents-md-core/src/injector.test.ts packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts && node --test packages/omo-codex/plugin/test/*.test.mjs packages/omo-codex/scripts/install-cache-copy.test.mjs packages/omo-codex/scripts/install-config.test.mjs packages/omo-codex/scripts/install-local.test.mjs packages/omo-codex/scripts/install-mcp-runtime.test.mjs packages/omo-codex/scripts/install-agent-links.test.mjs packages/omo-codex/scripts/install-bin-links.test.mjs packages/omo-codex/scripts/sync-telemetry-component.test.mjs",
61581
+ "test:codex": "bun run build:ast-grep-mcp && bun run build:lsp-tools-mcp && npm --prefix packages/omo-codex/plugin ci && bun run --cwd packages/omo-codex/plugin build && bun test src/cli/cli-installer.platform.test.ts src/cli/install-codex/codex-cache.test.ts src/cli/install-codex/codex-cleanup.test.ts src/cli/install-codex/codex-config-agent-cleanup.test.ts src/cli/install-codex/codex-config-reasoning.test.ts src/cli/install-codex/codex-config-toml.test.ts src/cli/install-codex/codex-project-local-cleanup.test.ts src/cli/install-codex/install-codex-project-local-cleanup.test.ts src/cli/install-codex/install-codex.test.ts src/cli/install-codex/install-codex-packaged.test.ts src/cli/install-codex/link-cached-plugin-agents.test.ts packages/omo-codex/src/**/*.test.ts packages/utils/src/jsonc-parser.test.ts packages/utils/src/frontmatter.test.ts packages/hashline-core/src/hash-computation.test.ts packages/hashline-core/src/smoke-untested-modules.test.ts packages/rules-engine/src/index.test.ts packages/rules-engine/src/security-boundary.test.ts packages/agents-md-core/src/injector.test.ts packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts && node --test packages/omo-codex/plugin/test/*.test.mjs packages/omo-codex/scripts/install-cache-copy.test.mjs packages/omo-codex/scripts/install-cli-args.test.mjs packages/omo-codex/scripts/install-config-autonomous.test.mjs packages/omo-codex/scripts/install-config-reasoning.test.mjs packages/omo-codex/scripts/install-config.test.mjs packages/omo-codex/scripts/install-project-local-cleanup.test.mjs packages/omo-codex/scripts/install-local-entrypoint.test.mjs packages/omo-codex/scripts/install-local-git-bash-preflight.test.mjs packages/omo-codex/scripts/install-local.test.mjs packages/omo-codex/scripts/install-mcp-runtime.test.mjs packages/omo-codex/scripts/install-packaged-local.test.mjs packages/omo-codex/scripts/install/git-bash.test.mjs packages/omo-codex/scripts/install-agent-links.test.mjs packages/omo-codex/scripts/install-bin-links.test.mjs packages/omo-codex/scripts/sync-telemetry-component.test.mjs",
61398
61582
  "test:windows-codex": "bun run test:codex",
61399
- "build:ast-grep-mcp": "bun run --cwd packages/ast-grep-mcp build"
61583
+ "build:ast-grep-mcp": "bun run --cwd packages/ast-grep-mcp build",
61584
+ "build:git-bash-mcp": "bun run --cwd packages/git-bash-mcp build"
61400
61585
  },
61401
61586
  keywords: [
61402
61587
  "opencode",
@@ -61428,7 +61613,6 @@ var package_default = {
61428
61613
  commander: "^14.0.3",
61429
61614
  "detect-libc": "^2.1.2",
61430
61615
  diff: "^9.0.0",
61431
- effect: "4.0.0-beta.65",
61432
61616
  "js-yaml": "^4.1.1",
61433
61617
  "jsonc-parser": "^3.3.1",
61434
61618
  picocolors: "^1.1.1",
@@ -61439,6 +61623,7 @@ var package_default = {
61439
61623
  devDependencies: {
61440
61624
  "@oh-my-opencode/ast-grep-core": "workspace:*",
61441
61625
  "@oh-my-opencode/ast-grep-mcp": "workspace:*",
61626
+ "@oh-my-opencode/git-bash-mcp": "workspace:*",
61442
61627
  "@oh-my-opencode/agents-md-core": "workspace:*",
61443
61628
  "@oh-my-opencode/boulder-state": "workspace:*",
61444
61629
  "@oh-my-opencode/comment-checker-core": "workspace:*",
@@ -61457,17 +61642,17 @@ var package_default = {
61457
61642
  zod: "^4.4.3"
61458
61643
  },
61459
61644
  optionalDependencies: {
61460
- "oh-my-opencode-darwin-arm64": "4.5.12",
61461
- "oh-my-opencode-darwin-x64": "4.5.12",
61462
- "oh-my-opencode-darwin-x64-baseline": "4.5.12",
61463
- "oh-my-opencode-linux-arm64": "4.5.12",
61464
- "oh-my-opencode-linux-arm64-musl": "4.5.12",
61465
- "oh-my-opencode-linux-x64": "4.5.12",
61466
- "oh-my-opencode-linux-x64-baseline": "4.5.12",
61467
- "oh-my-opencode-linux-x64-musl": "4.5.12",
61468
- "oh-my-opencode-linux-x64-musl-baseline": "4.5.12",
61469
- "oh-my-opencode-windows-x64": "4.5.12",
61470
- "oh-my-opencode-windows-x64-baseline": "4.5.12"
61645
+ "oh-my-opencode-darwin-arm64": "4.7.0",
61646
+ "oh-my-opencode-darwin-x64": "4.7.0",
61647
+ "oh-my-opencode-darwin-x64-baseline": "4.7.0",
61648
+ "oh-my-opencode-linux-arm64": "4.7.0",
61649
+ "oh-my-opencode-linux-arm64-musl": "4.7.0",
61650
+ "oh-my-opencode-linux-x64": "4.7.0",
61651
+ "oh-my-opencode-linux-x64-baseline": "4.7.0",
61652
+ "oh-my-opencode-linux-x64-musl": "4.7.0",
61653
+ "oh-my-opencode-linux-x64-musl-baseline": "4.7.0",
61654
+ "oh-my-opencode-windows-x64": "4.7.0",
61655
+ "oh-my-opencode-windows-x64-baseline": "4.7.0"
61471
61656
  },
61472
61657
  overrides: {
61473
61658
  hono: "^4.12.18",
@@ -61490,6 +61675,7 @@ var package_default = {
61490
61675
  init_shared();
61491
61676
  init_config_manager();
61492
61677
  var import_picocolors3 = __toESM(require_picocolors(), 1);
61678
+ import { createInterface as createInterface2 } from "readline/promises";
61493
61679
 
61494
61680
  // src/cli/install-validators.ts
61495
61681
  var import_picocolors = __toESM(require_picocolors(), 1);
@@ -61517,6 +61703,9 @@ function formatConfigSummary(config) {
61517
61703
  if (config.hasCodex) {
61518
61704
  lines.push(` ${SYMBOLS.info} Codex autonomous mode: ${config.codexAutonomous ? "enabled" : "disabled"}`);
61519
61705
  }
61706
+ if (!config.hasOpenCode)
61707
+ return lines.join(`
61708
+ `);
61520
61709
  lines.push("");
61521
61710
  const claudeDetail = config.hasClaude ? config.isMax20 ? "max20" : "standard" : undefined;
61522
61711
  lines.push(formatProvider("Claude", config.hasClaude, claudeDetail));
@@ -61524,7 +61713,7 @@ function formatConfigSummary(config) {
61524
61713
  lines.push(formatProvider("Gemini", config.hasGemini));
61525
61714
  lines.push(formatProvider("GitHub Copilot", config.hasCopilot, "fallback"));
61526
61715
  lines.push(formatProvider("OpenCode Zen", config.hasOpencodeZen, "opencode/ models"));
61527
- lines.push(formatProvider("Z.ai Coding Plan", config.hasZaiCodingPlan, "Librarian/Multimodal"));
61716
+ lines.push(formatProvider("Z.ai Coding Plan", config.hasZaiCodingPlan, "GLM fallbacks"));
61528
61717
  lines.push(formatProvider("Kimi For Coding", config.hasKimiForCoding, "Sisyphus/Prometheus fallback"));
61529
61718
  lines.push(formatProvider("Vercel AI Gateway", config.hasVercelAiGateway, "universal proxy"));
61530
61719
  lines.push("");
@@ -61665,7 +61854,7 @@ function argsToConfig(args) {
61665
61854
  hasKimiForCoding: hasOpenCode && args.kimiForCoding === "yes",
61666
61855
  hasOpencodeGo: hasOpenCode && args.opencodeGo === "yes",
61667
61856
  hasVercelAiGateway: hasOpenCode && args.vercelAiGateway === "yes",
61668
- codexAutonomous: hasCodex && args.codexAutonomous === true
61857
+ codexAutonomous: hasCodex && args.codexAutonomous !== false
61669
61858
  };
61670
61859
  }
61671
61860
  function detectedToInitialValues(detected) {
@@ -61742,9 +61931,9 @@ function getUnsupportedOpenCodeVersionMessage(openCodeVersion) {
61742
61931
 
61743
61932
  // src/cli/install-codex/install-codex.ts
61744
61933
  import { homedir as homedir5 } from "os";
61745
- import { join as join28, resolve as resolve8 } from "path";
61746
- import { existsSync as existsSync23 } from "fs";
61747
- import { mkdir as mkdir6, writeFile as writeFile6 } from "fs/promises";
61934
+ import { join as join31, resolve as resolve9 } from "path";
61935
+ import { existsSync as existsSync25 } from "fs";
61936
+ import { mkdir as mkdir6, writeFile as writeFile7 } from "fs/promises";
61748
61937
 
61749
61938
  // src/cli/install-codex/codex-cache.ts
61750
61939
  import { cp as cp2, lstat as lstat2, mkdir as mkdir2, readFile as readFile4, readdir as readdir2, readlink as readlink2, rename, rm as rm2, symlink, writeFile as writeFile2 } from "fs/promises";
@@ -61761,6 +61950,13 @@ var BUNDLED_MCP_RUNTIMES = [
61761
61950
  destinationArg: "./components/ast-grep-mcp/dist/cli.js",
61762
61951
  destinationDistFromPlugin: "components/ast-grep-mcp/dist"
61763
61952
  },
61953
+ {
61954
+ label: "Git Bash MCP",
61955
+ sourceArg: "../../git-bash-mcp/dist/cli.js",
61956
+ sourceDistFromPlugin: "../../git-bash-mcp/dist",
61957
+ destinationArg: "./components/git-bash-mcp/dist/cli.js",
61958
+ destinationDistFromPlugin: "components/git-bash-mcp/dist"
61959
+ },
61764
61960
  {
61765
61961
  label: "LSP MCP",
61766
61962
  sourceArg: "../../lsp-tools-mcp/dist/cli.js",
@@ -61833,6 +62029,7 @@ import { lstat, readFile as readFile2, readlink, rm } from "fs/promises";
61833
62029
  import { join as join20 } from "path";
61834
62030
  var LEGACY_CODEX_COMPONENT_BINS = [
61835
62031
  { name: "codex-comment-checker", component: "comment-checker" },
62032
+ { name: "codex-lsp", component: "lsp" },
61836
62033
  { name: "codex-rules", component: "rules" },
61837
62034
  { name: "codex-start-work-continuation", component: "start-work-continuation" },
61838
62035
  { name: "codex-telemetry", component: "telemetry" },
@@ -61956,8 +62153,10 @@ function isRecord9(value) {
61956
62153
 
61957
62154
  // src/cli/install-codex/codex-cache.ts
61958
62155
  async function installCachedPlugin(input) {
61959
- await maybeRunNpmInstall(input.sourcePath, input.runCommand);
61960
- await maybeRunNpmBuild(input.sourcePath, input.runCommand);
62156
+ if (input.buildSource !== false) {
62157
+ await maybeRunNpmInstall(input.sourcePath, input.runCommand);
62158
+ await maybeRunNpmBuild(input.sourcePath, input.runCommand);
62159
+ }
61961
62160
  const targetPath = join22(input.codexHome, "plugins", "cache", input.marketplaceName, input.name, input.version);
61962
62161
  await replaceDirectory(input.sourcePath, targetPath);
61963
62162
  await rewriteCachedPackageLocalFileDependencies(targetPath, input.sourcePath);
@@ -62178,11 +62377,38 @@ function isRecord10(value) {
62178
62377
  return typeof value === "object" && value !== null && !Array.isArray(value);
62179
62378
  }
62180
62379
 
62380
+ // src/cli/install-codex/codex-package-layout.ts
62381
+ import { existsSync as existsSync22 } from "fs";
62382
+ import { readFile as readFile5 } from "fs/promises";
62383
+ import { join as join23 } from "path";
62384
+ var PACKAGED_CODEX_INSTALLER_NAMES = new Set([
62385
+ "@code-yeongyu/lazycodex",
62386
+ "@code-yeongyu/lazycodex-ai",
62387
+ "lazycodex",
62388
+ "lazycodex-ai",
62389
+ "oh-my-opencode",
62390
+ "oh-my-openagent"
62391
+ ]);
62392
+ async function shouldBuildSourcePackages(repoRoot) {
62393
+ if (existsSync22(join23(repoRoot, "src", "index.ts")))
62394
+ return true;
62395
+ const packageJsonPath = join23(repoRoot, "package.json");
62396
+ if (!existsSync22(packageJsonPath))
62397
+ return true;
62398
+ const packageJson = JSON.parse(await readFile5(packageJsonPath, "utf8"));
62399
+ if (!isRecord11(packageJson) || typeof packageJson.name !== "string")
62400
+ return true;
62401
+ return !PACKAGED_CODEX_INSTALLER_NAMES.has(packageJson.name);
62402
+ }
62403
+ function isRecord11(value) {
62404
+ return typeof value === "object" && value !== null && !Array.isArray(value);
62405
+ }
62406
+
62181
62407
  // src/cli/install-codex/codex-config-toml.ts
62182
- import { mkdir as mkdir3, readFile as readFile5, writeFile as writeFile3 } from "fs/promises";
62408
+ import { mkdir as mkdir3, readFile as readFile7, writeFile as writeFile3 } from "fs/promises";
62183
62409
  import { dirname as dirname12 } from "path";
62184
62410
 
62185
- // src/cli/install-codex/toml-section-editor.ts
62411
+ // packages/omo-codex/scripts/install/toml-editor.mjs
62186
62412
  function findTomlSection(config, header) {
62187
62413
  const headerLine = `[${header}]`;
62188
62414
  const lines = config.match(/[^\n]*\n?|$/g) ?? [];
@@ -62210,10 +62436,24 @@ function replaceOrInsertSetting(config, section, key, value) {
62210
62436
  return config.slice(0, section.start) + replacement + config.slice(section.end);
62211
62437
  }
62212
62438
  function removeSetting(config, section, key) {
62213
- const linePattern = new RegExp(`^${escapeRegExp(key)}\\s*=.*(?:\\n|$)`, "m");
62439
+ const linePattern = new RegExp(`^\\s*${escapeRegExp(key)}\\s*=.*(?:\\n|$)`, "m");
62214
62440
  const replacement = section.text.replace(linePattern, "");
62215
62441
  return config.slice(0, section.start) + replacement + config.slice(section.end);
62216
62442
  }
62443
+ function replaceOrInsertRootSetting(config, key, value) {
62444
+ const sectionStart = findFirstTableStart(config);
62445
+ const root = config.slice(0, sectionStart);
62446
+ const suffix = config.slice(sectionStart);
62447
+ const linePattern = new RegExp(`^${escapeRegExp(key)}\\s*=.*$`, "m");
62448
+ const replacement = linePattern.test(root) ? root.replace(linePattern, `${key} = ${value}`) : `${root.trimEnd()}${root.trimEnd().length > 0 ? `
62449
+ ` : ""}${key} = ${value}
62450
+ `;
62451
+ if (suffix.length === 0)
62452
+ return replacement;
62453
+ return `${replacement.trimEnd()}
62454
+
62455
+ ${suffix.trimStart()}`;
62456
+ }
62217
62457
  function appendBlock(config, block) {
62218
62458
  const prefix = config.trimEnd();
62219
62459
  return `${prefix}${prefix.length > 0 ? `
@@ -62221,6 +62461,10 @@ function appendBlock(config, block) {
62221
62461
  ` : ""}${block.trimEnd()}
62222
62462
  `;
62223
62463
  }
62464
+ function findFirstTableStart(config) {
62465
+ const match = config.match(/^[[].*$/m);
62466
+ return match?.index ?? config.length;
62467
+ }
62224
62468
  function insertSetting(sectionText, key, value) {
62225
62469
  const lines = sectionText.split(`
62226
62470
  `);
@@ -62232,34 +62476,268 @@ function escapeRegExp(value) {
62232
62476
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
62233
62477
  }
62234
62478
 
62479
+ // packages/omo-codex/scripts/install/permissions.mjs
62480
+ function ensureAutonomousPermissions(config) {
62481
+ let next = replaceOrInsertRootSetting(config, "approval_policy", JSON.stringify("never"));
62482
+ next = replaceOrInsertRootSetting(next, "sandbox_mode", JSON.stringify("danger-full-access"));
62483
+ next = replaceOrInsertRootSetting(next, "network_access", JSON.stringify("enabled"));
62484
+ next = removeWindowsSandboxSetting(next);
62485
+ next = ensureNoticeEnabled(next, "hide_full_access_warning");
62486
+ return ensureNoticeEnabled(next, "hide_world_writable_warning");
62487
+ }
62488
+ function removeWindowsSandboxSetting(config) {
62489
+ const section = findTomlSection(config, "windows");
62490
+ if (!section)
62491
+ return config;
62492
+ return removeSetting(config, section, "sandbox");
62493
+ }
62494
+ function ensureNoticeEnabled(config, key) {
62495
+ const section = findTomlSection(config, "notice");
62496
+ if (!section)
62497
+ return appendNoticeBlock(config, key);
62498
+ return replaceOrInsertSetting(config, section, key, "true");
62499
+ }
62500
+ function appendNoticeBlock(config, key) {
62501
+ return appendBlock(config, `[notice]
62502
+ ${key} = true
62503
+ `);
62504
+ }
62505
+ // src/cli/install-codex/toml-section-editor.ts
62506
+ function findTomlSection2(config, header) {
62507
+ const headerLine = `[${header}]`;
62508
+ const lines = config.match(/[^\n]*\n?|$/g) ?? [];
62509
+ let offset = 0;
62510
+ let start = -1;
62511
+ for (const line of lines) {
62512
+ if (line.length === 0)
62513
+ break;
62514
+ const trimmed = line.trim();
62515
+ if (start === -1) {
62516
+ if (trimmed === headerLine)
62517
+ start = offset;
62518
+ } else if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
62519
+ return { start, end: offset, text: config.slice(start, offset) };
62520
+ }
62521
+ offset += line.length;
62522
+ }
62523
+ if (start === -1)
62524
+ return null;
62525
+ return { start, end: config.length, text: config.slice(start) };
62526
+ }
62527
+ function replaceOrInsertSetting2(config, section, key, value) {
62528
+ const linePattern = new RegExp(`^${escapeRegExp2(key)}\\s*=.*$`, "m");
62529
+ const replacement = linePattern.test(section.text) ? section.text.replace(linePattern, `${key} = ${value}`) : insertSetting2(section.text, key, value);
62530
+ return config.slice(0, section.start) + replacement + config.slice(section.end);
62531
+ }
62532
+ function removeSetting2(config, section, key) {
62533
+ const linePattern = new RegExp(`^\\s*${escapeRegExp2(key)}\\s*=.*(?:\\n|$)`, "m");
62534
+ const replacement = section.text.replace(linePattern, "");
62535
+ return config.slice(0, section.start) + replacement + config.slice(section.end);
62536
+ }
62537
+ function replaceOrInsertRootSetting2(config, key, value) {
62538
+ const sectionStart = findFirstTableStart2(config);
62539
+ const root = config.slice(0, sectionStart);
62540
+ const suffix = config.slice(sectionStart);
62541
+ const linePattern = new RegExp(`^${escapeRegExp2(key)}\\s*=.*$`, "m");
62542
+ const replacement = linePattern.test(root) ? root.replace(linePattern, `${key} = ${value}`) : `${root.trimEnd()}${root.trimEnd().length > 0 ? `
62543
+ ` : ""}${key} = ${value}
62544
+ `;
62545
+ if (suffix.length === 0)
62546
+ return replacement;
62547
+ return `${replacement.trimEnd()}
62548
+
62549
+ ${suffix.trimStart()}`;
62550
+ }
62551
+ function appendBlock2(config, block) {
62552
+ const prefix = config.trimEnd();
62553
+ return `${prefix}${prefix.length > 0 ? `
62554
+
62555
+ ` : ""}${block.trimEnd()}
62556
+ `;
62557
+ }
62558
+ function findFirstTableStart2(config) {
62559
+ const match = config.match(/^[[].*$/m);
62560
+ return match?.index ?? config.length;
62561
+ }
62562
+ function insertSetting2(sectionText, key, value) {
62563
+ const lines = sectionText.split(`
62564
+ `);
62565
+ lines.splice(1, 0, `${key} = ${value}`);
62566
+ return lines.join(`
62567
+ `);
62568
+ }
62569
+ function escapeRegExp2(value) {
62570
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
62571
+ }
62572
+
62573
+ // src/cli/install-codex/codex-config-reasoning.ts
62574
+ var MANAGED_KEYS = ["model", "model_context_window", "model_reasoning_effort", "plan_mode_reasoning_effort"];
62575
+ function ensureCodexReasoningConfig(config, catalog) {
62576
+ const current = readRootReasoningSettings(config);
62577
+ if (Object.keys(current).length > 0 && !matchesProfile(current, catalog.current) && !catalog.managedProfiles.some((profile) => matchesProfile(current, profile))) {
62578
+ return config;
62579
+ }
62580
+ let next = replaceOrInsertRootSetting2(config, "model", JSON.stringify(catalog.current.model));
62581
+ next = replaceOrInsertRootSetting2(next, "model_context_window", catalog.current.modelContextWindow.toString());
62582
+ next = replaceOrInsertRootSetting2(next, "model_reasoning_effort", JSON.stringify(catalog.current.modelReasoningEffort));
62583
+ next = replaceOrInsertRootSetting2(next, "plan_mode_reasoning_effort", JSON.stringify(catalog.current.planModeReasoningEffort));
62584
+ return next;
62585
+ }
62586
+ function readRootReasoningSettings(config) {
62587
+ const settings = {};
62588
+ for (const line of config.split(/\n/)) {
62589
+ if (isSectionHeader(line))
62590
+ break;
62591
+ for (const key of MANAGED_KEYS) {
62592
+ if (!isRootSetting(line, key))
62593
+ continue;
62594
+ const value = parseTomlScalar(line.slice(line.indexOf("=") + 1));
62595
+ if (key === "model" && typeof value === "string")
62596
+ settings.model = value;
62597
+ if (key === "model_context_window" && typeof value === "number")
62598
+ settings.modelContextWindow = value;
62599
+ if (key === "model_reasoning_effort" && typeof value === "string")
62600
+ settings.modelReasoningEffort = value;
62601
+ if (key === "plan_mode_reasoning_effort" && typeof value === "string")
62602
+ settings.planModeReasoningEffort = value;
62603
+ }
62604
+ }
62605
+ return settings;
62606
+ }
62607
+ function matchesProfile(current, profile) {
62608
+ for (const [key, value] of Object.entries(profile)) {
62609
+ if (current[key] !== value)
62610
+ return false;
62611
+ }
62612
+ return true;
62613
+ }
62614
+ function parseTomlScalar(value) {
62615
+ const trimmed = value.trim();
62616
+ if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
62617
+ try {
62618
+ return JSON.parse(trimmed);
62619
+ } catch (error) {
62620
+ if (error instanceof SyntaxError)
62621
+ return;
62622
+ throw error;
62623
+ }
62624
+ }
62625
+ const numeric = Number(trimmed);
62626
+ return Number.isFinite(numeric) ? numeric : undefined;
62627
+ }
62628
+ function isSectionHeader(line) {
62629
+ const trimmed = line.trim();
62630
+ return trimmed.startsWith("[") && trimmed.endsWith("]");
62631
+ }
62632
+ function isRootSetting(line, key) {
62633
+ const trimmed = line.trimStart();
62634
+ if (trimmed.startsWith("#") || trimmed.startsWith("["))
62635
+ return false;
62636
+ const match = trimmed.match(/^([A-Za-z_][A-Za-z0-9_]*)\s*=/);
62637
+ return match?.[1] === key;
62638
+ }
62639
+
62640
+ // src/cli/install-codex/codex-model-catalog.ts
62641
+ import { readFile as readFile6 } from "fs/promises";
62642
+ import { join as join24 } from "path";
62643
+ var FALLBACK_CODEX_MODEL_CATALOG = {
62644
+ current: {
62645
+ model: "gpt-5.5",
62646
+ modelContextWindow: 400000,
62647
+ modelReasoningEffort: "high",
62648
+ planModeReasoningEffort: "xhigh"
62649
+ },
62650
+ managedProfiles: [{ model: "gpt-5.2" }]
62651
+ };
62652
+ async function readCodexModelCatalog(codexPackageRoot) {
62653
+ const catalogPath = join24(codexPackageRoot, "plugin", "model-catalog.json");
62654
+ try {
62655
+ const parsed = JSON.parse(await readFile6(catalogPath, "utf8"));
62656
+ return parseCodexModelCatalog(parsed) ?? FALLBACK_CODEX_MODEL_CATALOG;
62657
+ } catch (error) {
62658
+ if (error instanceof Error)
62659
+ return FALLBACK_CODEX_MODEL_CATALOG;
62660
+ throw error;
62661
+ }
62662
+ }
62663
+ function parseCodexModelCatalog(value) {
62664
+ if (!isRecord12(value))
62665
+ return null;
62666
+ const current = value["current"];
62667
+ const managedProfiles = value["managedProfiles"];
62668
+ if (!isRecord12(current) || !Array.isArray(managedProfiles))
62669
+ return null;
62670
+ const model = current["model"];
62671
+ const modelContextWindow = current["model_context_window"];
62672
+ const modelReasoningEffort = current["model_reasoning_effort"];
62673
+ const planModeReasoningEffort = current["plan_mode_reasoning_effort"];
62674
+ if (typeof model !== "string" || typeof modelContextWindow !== "number" || typeof modelReasoningEffort !== "string" || typeof planModeReasoningEffort !== "string") {
62675
+ return null;
62676
+ }
62677
+ const parsedManagedProfiles = [];
62678
+ for (const profile of managedProfiles) {
62679
+ if (!isRecord12(profile))
62680
+ return null;
62681
+ const match = profile["match"];
62682
+ if (!isRecord12(match))
62683
+ return null;
62684
+ parsedManagedProfiles.push(parseProfileMatch(match));
62685
+ }
62686
+ return {
62687
+ current: { model, modelContextWindow, modelReasoningEffort, planModeReasoningEffort },
62688
+ managedProfiles: parsedManagedProfiles
62689
+ };
62690
+ }
62691
+ function parseProfileMatch(match) {
62692
+ const profile = {};
62693
+ if (typeof match["model"] === "string")
62694
+ profile.model = match["model"];
62695
+ if (typeof match["model_context_window"] === "number")
62696
+ profile.modelContextWindow = match["model_context_window"];
62697
+ if (typeof match["model_reasoning_effort"] === "string")
62698
+ profile.modelReasoningEffort = match["model_reasoning_effort"];
62699
+ if (typeof match["plan_mode_reasoning_effort"] === "string")
62700
+ profile.planModeReasoningEffort = match["plan_mode_reasoning_effort"];
62701
+ return profile;
62702
+ }
62703
+ function isRecord12(value) {
62704
+ return typeof value === "object" && value !== null && !Array.isArray(value);
62705
+ }
62706
+
62235
62707
  // src/cli/install-codex/codex-multi-agent-v2-config.ts
62236
62708
  var CODEX_MULTI_AGENT_V2_HEADER = "features.multi_agent_v2";
62237
62709
  var CODEX_MULTI_AGENT_V2_MAX_CONCURRENT_THREADS_PER_SESSION = 1e4;
62238
62710
  function ensureCodexMultiAgentV2Config(config) {
62239
- const normalizedConfig = removeFeatureFlagSetting(config, "multi_agent_v2");
62240
- const section = findTomlSection(normalizedConfig, CODEX_MULTI_AGENT_V2_HEADER);
62711
+ const normalizedConfig = removeLegacyAgentsMaxThreadsSetting(removeFeatureFlagSetting(config, "multi_agent_v2"));
62712
+ const section = findTomlSection2(normalizedConfig, CODEX_MULTI_AGENT_V2_HEADER);
62241
62713
  const maxThreadsValue = CODEX_MULTI_AGENT_V2_MAX_CONCURRENT_THREADS_PER_SESSION.toString();
62242
62714
  if (!section) {
62243
- return appendBlock(normalizedConfig, `[${CODEX_MULTI_AGENT_V2_HEADER}]
62715
+ return appendBlock2(normalizedConfig, `[${CODEX_MULTI_AGENT_V2_HEADER}]
62244
62716
  enabled = true
62245
62717
  max_concurrent_threads_per_session = ${maxThreadsValue}
62246
62718
  `);
62247
62719
  }
62248
- const enabledConfig = replaceOrInsertSetting(normalizedConfig, section, "enabled", "true");
62249
- const updatedSection = findTomlSection(enabledConfig, CODEX_MULTI_AGENT_V2_HEADER);
62720
+ const enabledConfig = replaceOrInsertSetting2(normalizedConfig, section, "enabled", "true");
62721
+ const updatedSection = findTomlSection2(enabledConfig, CODEX_MULTI_AGENT_V2_HEADER);
62250
62722
  if (!updatedSection) {
62251
- return appendBlock(enabledConfig, `[${CODEX_MULTI_AGENT_V2_HEADER}]
62723
+ return appendBlock2(enabledConfig, `[${CODEX_MULTI_AGENT_V2_HEADER}]
62252
62724
  enabled = true
62253
62725
  max_concurrent_threads_per_session = ${maxThreadsValue}
62254
62726
  `);
62255
62727
  }
62256
- return replaceOrInsertSetting(enabledConfig, updatedSection, "max_concurrent_threads_per_session", maxThreadsValue);
62728
+ return replaceOrInsertSetting2(enabledConfig, updatedSection, "max_concurrent_threads_per_session", maxThreadsValue);
62257
62729
  }
62258
62730
  function removeFeatureFlagSetting(config, featureName) {
62259
- const section = findTomlSection(config, "features");
62731
+ const section = findTomlSection2(config, "features");
62732
+ if (!section)
62733
+ return config;
62734
+ return removeSetting2(config, section, featureName);
62735
+ }
62736
+ function removeLegacyAgentsMaxThreadsSetting(config) {
62737
+ const section = findTomlSection2(config, "agents");
62260
62738
  if (!section)
62261
62739
  return config;
62262
- return removeSetting(config, section, featureName);
62740
+ return removeSetting2(config, section, "max_threads");
62263
62741
  }
62264
62742
 
62265
62743
  // src/cli/install-codex/codex-config-toml.ts
@@ -62276,7 +62754,7 @@ async function updateCodexConfig(input) {
62276
62754
  await mkdir3(dirname12(input.configPath), { recursive: true });
62277
62755
  let config = "";
62278
62756
  if (await exists2(input.configPath))
62279
- config = await readFile5(input.configPath, "utf8");
62757
+ config = await readFile7(input.configPath, "utf8");
62280
62758
  const pluginSet = new Set(input.pluginNames);
62281
62759
  for (const legacyMarketplaceName of legacyMarketplaceNames(input.marketplaceName)) {
62282
62760
  config = removeMarketplaceBlock(config, legacyMarketplaceName);
@@ -62288,6 +62766,7 @@ async function updateCodexConfig(input) {
62288
62766
  config = removeStaleManagedAgentBlocks(config, new Set((input.agentConfigs ?? []).map((agentConfig) => agentConfig.name)));
62289
62767
  config = ensureFeatureEnabled(config, "plugins");
62290
62768
  config = ensureFeatureEnabled(config, "plugin_hooks");
62769
+ config = ensureCodexReasoningConfig(config, await readCodexModelCatalog(input.repoRoot));
62291
62770
  config = ensureCodexMultiAgentV2Config(config);
62292
62771
  if (input.autonomousPermissions === true)
62293
62772
  config = ensureAutonomousPermissions(config);
@@ -62295,6 +62774,7 @@ async function updateCodexConfig(input) {
62295
62774
  for (const pluginName of input.pluginNames) {
62296
62775
  config = ensurePluginEnabled(config, `${pluginName}@${input.marketplaceName}`);
62297
62776
  }
62777
+ config = ensureOmoGitBashMcpPolicy(config, input);
62298
62778
  for (const state of input.trustedHookStates ?? []) {
62299
62779
  config = ensureHookTrusted(config, state.key, state.trustedHash);
62300
62780
  }
@@ -62353,48 +62833,12 @@ function removeStaleManagedAgentBlocks(config, keepAgentNames) {
62353
62833
  `);
62354
62834
  }
62355
62835
  function ensureFeatureEnabled(config, featureName) {
62356
- const section = findTomlSection(config, "features");
62836
+ const section = findTomlSection2(config, "features");
62357
62837
  if (!section)
62358
- return appendBlock(config, `[features]
62838
+ return appendBlock2(config, `[features]
62359
62839
  ${featureName} = true
62360
62840
  `);
62361
- return replaceOrInsertSetting(config, section, featureName, "true");
62362
- }
62363
- function ensureAutonomousPermissions(config) {
62364
- let next = replaceOrInsertRootSetting(config, "approval_policy", JSON.stringify("never"));
62365
- next = replaceOrInsertRootSetting(next, "sandbox_mode", JSON.stringify("danger-full-access"));
62366
- next = replaceOrInsertRootSetting(next, "network_access", JSON.stringify("enabled"));
62367
- next = ensureNoticeEnabled(next, "hide_full_access_warning");
62368
- return ensureNoticeEnabled(next, "hide_world_writable_warning");
62369
- }
62370
- function ensureNoticeEnabled(config, key) {
62371
- const section = findTomlSection(config, "notice");
62372
- if (!section)
62373
- return appendBlock(config, `[notice]
62374
- ${key} = true
62375
- `);
62376
- return replaceOrInsertSetting(config, section, key, "true");
62377
- }
62378
- function replaceOrInsertRootSetting(config, key, value) {
62379
- const sectionStart = findFirstTableStart(config);
62380
- const root = config.slice(0, sectionStart);
62381
- const suffix = config.slice(sectionStart);
62382
- const linePattern = new RegExp(`^${escapeRegExp2(key)}\\s*=.*$`, "m");
62383
- const replacement = linePattern.test(root) ? root.replace(linePattern, `${key} = ${value}`) : `${root.trimEnd()}${root.trimEnd().length > 0 ? `
62384
- ` : ""}${key} = ${value}
62385
- `;
62386
- if (suffix.length === 0)
62387
- return replacement;
62388
- return `${replacement.trimEnd()}
62389
-
62390
- ${suffix.trimStart()}`;
62391
- }
62392
- function findFirstTableStart(config) {
62393
- const match = config.match(/^[[].*$/m);
62394
- return match?.index ?? config.length;
62395
- }
62396
- function escapeRegExp2(value) {
62397
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
62841
+ return replaceOrInsertSetting2(config, section, featureName, "true");
62398
62842
  }
62399
62843
  function ensureMarketplaceBlock(config, marketplaceName, source) {
62400
62844
  const header = `marketplaces.${marketplaceName}`;
@@ -62410,38 +62854,54 @@ function ensureMarketplaceBlock(config, marketplaceName, source) {
62410
62854
  lines.push("");
62411
62855
  const block = lines.join(`
62412
62856
  `);
62413
- const section = findTomlSection(config, header);
62857
+ const section = findTomlSection2(config, header);
62414
62858
  if (section)
62415
62859
  return config.slice(0, section.start) + block + config.slice(section.end);
62416
- return appendBlock(config, block);
62860
+ return appendBlock2(config, block);
62417
62861
  }
62418
62862
  function ensurePluginEnabled(config, pluginKey) {
62419
62863
  const header = `plugins.${JSON.stringify(pluginKey)}`;
62420
- const section = findTomlSection(config, header);
62864
+ const section = findTomlSection2(config, header);
62421
62865
  if (!section)
62422
- return appendBlock(config, `[${header}]
62866
+ return appendBlock2(config, `[${header}]
62423
62867
  enabled = true
62424
62868
  `);
62425
- return replaceOrInsertSetting(config, section, "enabled", "true");
62869
+ return replaceOrInsertSetting2(config, section, "enabled", "true");
62870
+ }
62871
+ function ensurePluginMcpEnabled(config, pluginKey, serverName, enabled) {
62872
+ const header = `plugins.${JSON.stringify(pluginKey)}.mcp_servers.${serverName}`;
62873
+ const section = findTomlSection2(config, header);
62874
+ const enabledValue = enabled ? "true" : "false";
62875
+ if (!section)
62876
+ return appendBlock2(config, `[${header}]
62877
+ enabled = ${enabledValue}
62878
+ `);
62879
+ return replaceOrInsertSetting2(config, section, "enabled", enabledValue);
62880
+ }
62881
+ function ensureOmoGitBashMcpPolicy(config, input) {
62882
+ if (input.marketplaceName !== "sisyphuslabs" || !input.pluginNames.includes("omo"))
62883
+ return config;
62884
+ const enabled = (input.platform ?? process.platform) === "win32";
62885
+ return ensurePluginMcpEnabled(config, "omo@sisyphuslabs", "git_bash", enabled);
62426
62886
  }
62427
62887
  function ensureHookTrusted(config, key, trustedHash) {
62428
62888
  const header = `hooks.state.${JSON.stringify(key)}`;
62429
- const section = findTomlSection(config, header);
62889
+ const section = findTomlSection2(config, header);
62430
62890
  if (!section)
62431
- return appendBlock(config, `[${header}]
62891
+ return appendBlock2(config, `[${header}]
62432
62892
  trusted_hash = ${JSON.stringify(trustedHash)}
62433
62893
  `);
62434
- return replaceOrInsertSetting(config, section, "trusted_hash", JSON.stringify(trustedHash));
62894
+ return replaceOrInsertSetting2(config, section, "trusted_hash", JSON.stringify(trustedHash));
62435
62895
  }
62436
62896
  function ensureAgentConfig(config, agentConfig) {
62437
62897
  const header = `agents.${tomlKeySegment(agentConfig.name)}`;
62438
- const section = findTomlSection(config, header);
62898
+ const section = findTomlSection2(config, header);
62439
62899
  const configFile = JSON.stringify(agentConfig.configFile);
62440
62900
  if (!section)
62441
- return appendBlock(config, `[${header}]
62901
+ return appendBlock2(config, `[${header}]
62442
62902
  config_file = ${configFile}
62443
62903
  `);
62444
- return replaceOrInsertSetting(config, section, "config_file", configFile);
62904
+ return replaceOrInsertSetting2(config, section, "config_file", configFile);
62445
62905
  }
62446
62906
  function tomlKeySegment(value) {
62447
62907
  return /^[A-Za-z0-9_-]+$/.test(value) ? value : JSON.stringify(value);
@@ -62521,7 +62981,7 @@ function parseJsonString(value) {
62521
62981
  }
62522
62982
  async function exists2(path6) {
62523
62983
  try {
62524
- await readFile5(path6, "utf8");
62984
+ await readFile7(path6, "utf8");
62525
62985
  return true;
62526
62986
  } catch (error) {
62527
62987
  if (error instanceof Error)
@@ -62531,9 +62991,9 @@ async function exists2(path6) {
62531
62991
  }
62532
62992
 
62533
62993
  // src/cli/install-codex/codex-hook-trust.ts
62534
- import { createHash } from "crypto";
62535
- import { readFile as readFile6 } from "fs/promises";
62536
- import { join as join23 } from "path";
62994
+ import { createHash as createHash2 } from "crypto";
62995
+ import { readFile as readFile8 } from "fs/promises";
62996
+ import { join as join25 } from "path";
62537
62997
  var EVENT_LABELS = new Map([
62538
62998
  ["PreToolUse", "pre_tool_use"],
62539
62999
  ["PermissionRequest", "permission_request"],
@@ -62547,17 +63007,17 @@ var EVENT_LABELS = new Map([
62547
63007
  ["Stop", "stop"]
62548
63008
  ]);
62549
63009
  async function trustedHookStatesForPlugin(input) {
62550
- const manifestPath = join23(input.pluginRoot, ".codex-plugin", "plugin.json");
63010
+ const manifestPath = join25(input.pluginRoot, ".codex-plugin", "plugin.json");
62551
63011
  if (!await exists3(manifestPath))
62552
63012
  return [];
62553
- const manifest = JSON.parse(await readFile6(manifestPath, "utf8"));
62554
- if (!isRecord11(manifest) || typeof manifest.hooks !== "string")
63013
+ const manifest = JSON.parse(await readFile8(manifestPath, "utf8"));
63014
+ if (!isRecord13(manifest) || typeof manifest.hooks !== "string")
62555
63015
  return [];
62556
- const hooksPath = join23(input.pluginRoot, manifest.hooks);
63016
+ const hooksPath = join25(input.pluginRoot, manifest.hooks);
62557
63017
  if (!await exists3(hooksPath))
62558
63018
  return [];
62559
- const parsed = JSON.parse(await readFile6(hooksPath, "utf8"));
62560
- if (!isRecord11(parsed) || !isRecord11(parsed.hooks))
63019
+ const parsed = JSON.parse(await readFile8(hooksPath, "utf8"));
63020
+ if (!isRecord13(parsed) || !isRecord13(parsed.hooks))
62561
63021
  return [];
62562
63022
  const keySource = `${input.pluginName}@${input.marketplaceName}:${stripDotSlash(manifest.hooks)}`;
62563
63023
  const states = [];
@@ -62568,10 +63028,10 @@ async function trustedHookStatesForPlugin(input) {
62568
63028
  if (eventLabel === undefined)
62569
63029
  continue;
62570
63030
  for (const [groupIndex, group] of groups.entries()) {
62571
- if (!isRecord11(group) || !Array.isArray(group.hooks))
63031
+ if (!isRecord13(group) || !Array.isArray(group.hooks))
62572
63032
  continue;
62573
63033
  for (const [handlerIndex, handler] of group.hooks.entries()) {
62574
- if (!isRecord11(handler) || handler.type !== "command")
63034
+ if (!isRecord13(handler) || handler.type !== "command")
62575
63035
  continue;
62576
63036
  if (handler.async === true)
62577
63037
  continue;
@@ -62598,12 +63058,12 @@ function commandHookHash(eventName, matcher, handler) {
62598
63058
  if (typeof matcher === "string")
62599
63059
  identity.matcher = matcher;
62600
63060
  const canonical = JSON.stringify(canonicalJson(identity));
62601
- return `sha256:${createHash("sha256").update(canonical).digest("hex")}`;
63061
+ return `sha256:${createHash2("sha256").update(canonical).digest("hex")}`;
62602
63062
  }
62603
63063
  function canonicalJson(value) {
62604
63064
  if (Array.isArray(value))
62605
63065
  return value.map(canonicalJson);
62606
- if (!isRecord11(value))
63066
+ if (!isRecord13(value))
62607
63067
  return value;
62608
63068
  const result = {};
62609
63069
  for (const key of Object.keys(value).sort()) {
@@ -62616,19 +63076,113 @@ function stripDotSlash(value) {
62616
63076
  }
62617
63077
  async function exists3(path6) {
62618
63078
  try {
62619
- await readFile6(path6, "utf8");
63079
+ await readFile8(path6, "utf8");
62620
63080
  return true;
62621
63081
  } catch {
62622
63082
  return false;
62623
63083
  }
62624
63084
  }
62625
- function isRecord11(value) {
63085
+ function isRecord13(value) {
62626
63086
  return typeof value === "object" && value !== null && !Array.isArray(value);
62627
63087
  }
62628
63088
 
63089
+ // src/cli/install-codex/git-bash.ts
63090
+ import { execFileSync } from "child_process";
63091
+ import { existsSync as existsSync23 } from "fs";
63092
+ var GIT_BASH_ENV_KEY = "OMO_CODEX_GIT_BASH_PATH";
63093
+ var SKIP_GIT_BASH_AUTO_INSTALL_ENV_KEY = "OMO_CODEX_SKIP_GIT_BASH_AUTO_INSTALL";
63094
+ var PROGRAM_FILES_GIT_BASH = "C:\\Program Files\\Git\\bin\\bash.exe";
63095
+ var PROGRAM_FILES_X86_GIT_BASH = "C:\\Program Files (x86)\\Git\\bin\\bash.exe";
63096
+ var WINGET_INSTALL_ARGS = ["install", "--id", "Git.Git", "-e", "--source", "winget"];
63097
+ function resolveGitBash(input) {
63098
+ if (input.platform !== "win32")
63099
+ return { found: true, path: null, source: "not-required" };
63100
+ const checkedPaths = [];
63101
+ const envPath = nonEmptyEnvValue(input.env, GIT_BASH_ENV_KEY);
63102
+ if (envPath !== undefined) {
63103
+ checkedPaths.push(envPath);
63104
+ if (isBashExePath(envPath) && input.exists(envPath))
63105
+ return { found: true, path: envPath, source: "env" };
63106
+ return missingGitBash(checkedPaths);
63107
+ }
63108
+ for (const candidate of [
63109
+ { path: PROGRAM_FILES_GIT_BASH, source: "program-files" },
63110
+ { path: PROGRAM_FILES_X86_GIT_BASH, source: "program-files-x86" }
63111
+ ]) {
63112
+ checkedPaths.push(candidate.path);
63113
+ if (input.exists(candidate.path))
63114
+ return { found: true, path: candidate.path, source: candidate.source };
63115
+ }
63116
+ for (const pathCandidate of input.where("bash")) {
63117
+ const candidate = pathCandidate.trim();
63118
+ if (candidate.length === 0)
63119
+ continue;
63120
+ checkedPaths.push(candidate);
63121
+ if (isBashExePath(candidate) && input.exists(candidate))
63122
+ return { found: true, path: candidate, source: "path" };
63123
+ }
63124
+ return missingGitBash(checkedPaths);
63125
+ }
63126
+ function resolveGitBashForCurrentProcess(input = {}) {
63127
+ return resolveGitBash({
63128
+ platform: input.platform ?? process.platform,
63129
+ env: input.env ?? process.env,
63130
+ exists: existsSync23,
63131
+ where: whereCommand
63132
+ });
63133
+ }
63134
+ async function prepareGitBashForInstall(input) {
63135
+ const resolve8 = input.resolveGitBash ?? (() => resolveGitBashForCurrentProcess({ platform: input.platform, env: input.env }));
63136
+ const initialResolution = resolve8();
63137
+ if (input.platform !== "win32" || initialResolution.found)
63138
+ return initialResolution;
63139
+ if (input.env[SKIP_GIT_BASH_AUTO_INSTALL_ENV_KEY] === "1")
63140
+ return initialResolution;
63141
+ try {
63142
+ await input.runCommand("winget", WINGET_INSTALL_ARGS, { cwd: input.cwd });
63143
+ } catch (error) {
63144
+ if (!(error instanceof Error))
63145
+ throw error;
63146
+ return initialResolution;
63147
+ }
63148
+ return resolve8();
63149
+ }
63150
+ function missingGitBash(checkedPaths) {
63151
+ return {
63152
+ found: false,
63153
+ checkedPaths,
63154
+ installHint: [
63155
+ "Git Bash is required for native Windows Codex profile installs.",
63156
+ "Install it with: winget install --id Git.Git -e --source winget",
63157
+ `For a custom install, set ${GIT_BASH_ENV_KEY}=C:\\path\\to\\bash.exe`,
63158
+ "Then rerun `npx lazycodex-ai install`."
63159
+ ].join(`
63160
+ `)
63161
+ };
63162
+ }
63163
+ function nonEmptyEnvValue(env, key) {
63164
+ const value = env[key];
63165
+ if (value === undefined)
63166
+ return;
63167
+ const trimmed = value.trim();
63168
+ return trimmed.length === 0 ? undefined : trimmed;
63169
+ }
63170
+ function isBashExePath(path6) {
63171
+ return path6.toLowerCase().endsWith("bash.exe");
63172
+ }
63173
+ function whereCommand(command) {
63174
+ try {
63175
+ return execFileSync("where", [command], { encoding: "utf8" }).split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
63176
+ } catch (error) {
63177
+ if (error instanceof Error)
63178
+ return [];
63179
+ throw error;
63180
+ }
63181
+ }
63182
+
62629
63183
  // src/cli/install-codex/link-cached-plugin-agents.ts
62630
63184
  import { copyFile, lstat as lstat3, mkdir as mkdir4, readdir as readdir3, rm as rm3, symlink as symlink2, writeFile as writeFile4 } from "fs/promises";
62631
- import { basename as basename6, join as join24 } from "path";
63185
+ import { basename as basename6, join as join26 } from "path";
62632
63186
  var MANIFEST_FILE = ".installed-agents.json";
62633
63187
  async function linkCachedPluginAgents(input) {
62634
63188
  const platform = input.platform ?? process.platform;
@@ -62637,11 +63191,11 @@ async function linkCachedPluginAgents(input) {
62637
63191
  await writeManifest(input.pluginRoot, []);
62638
63192
  return [];
62639
63193
  }
62640
- const agentsDir = join24(input.codexHome, "agents");
63194
+ const agentsDir = join26(input.codexHome, "agents");
62641
63195
  await mkdir4(agentsDir, { recursive: true });
62642
63196
  const linked = [];
62643
63197
  for (const agentPath of bundledAgents) {
62644
- const linkPath = join24(agentsDir, basename6(agentPath));
63198
+ const linkPath = join26(agentsDir, basename6(agentPath));
62645
63199
  if (platform === "win32") {
62646
63200
  await replaceWithCopy(linkPath, agentPath);
62647
63201
  } else {
@@ -62653,7 +63207,7 @@ async function linkCachedPluginAgents(input) {
62653
63207
  return linked;
62654
63208
  }
62655
63209
  async function discoverBundledAgents(pluginRoot) {
62656
- const componentsRoot = join24(pluginRoot, "components");
63210
+ const componentsRoot = join26(pluginRoot, "components");
62657
63211
  if (!await exists4(componentsRoot))
62658
63212
  return [];
62659
63213
  const componentEntries = await readdir3(componentsRoot, { withFileTypes: true });
@@ -62661,14 +63215,14 @@ async function discoverBundledAgents(pluginRoot) {
62661
63215
  for (const entry of componentEntries) {
62662
63216
  if (!entry.isDirectory())
62663
63217
  continue;
62664
- const agentsRoot = join24(componentsRoot, entry.name, "agents");
63218
+ const agentsRoot = join26(componentsRoot, entry.name, "agents");
62665
63219
  if (!await exists4(agentsRoot))
62666
63220
  continue;
62667
63221
  const agentEntries = await readdir3(agentsRoot, { withFileTypes: true });
62668
63222
  for (const file of agentEntries) {
62669
63223
  if (!file.isFile() || !file.name.endsWith(".toml"))
62670
63224
  continue;
62671
- agents.push(join24(agentsRoot, file.name));
63225
+ agents.push(join26(agentsRoot, file.name));
62672
63226
  }
62673
63227
  }
62674
63228
  agents.sort();
@@ -62692,7 +63246,7 @@ async function prepareReplacement(linkPath) {
62692
63246
  await rm3(linkPath, { force: true });
62693
63247
  }
62694
63248
  async function writeManifest(pluginRoot, agentPaths) {
62695
- const manifestPath = join24(pluginRoot, MANIFEST_FILE);
63249
+ const manifestPath = join26(pluginRoot, MANIFEST_FILE);
62696
63250
  const payload = { agents: [...agentPaths].sort() };
62697
63251
  await writeFile4(manifestPath, `${JSON.stringify(payload, null, "\t")}
62698
63252
  `);
@@ -62707,14 +63261,14 @@ async function exists4(path6) {
62707
63261
  }
62708
63262
 
62709
63263
  // src/cli/install-codex/codex-marketplace.ts
62710
- import { readFile as readFile7 } from "fs/promises";
62711
- import { join as join25 } from "path";
63264
+ import { readFile as readFile9 } from "fs/promises";
63265
+ import { join as join27 } from "path";
62712
63266
  var DEFAULT_MARKETPLACE_PATH = "packages/omo-codex/marketplace.json";
62713
63267
  async function readMarketplace(repoRoot, options) {
62714
- const marketplacePath = options?.marketplacePath ?? join25(repoRoot, DEFAULT_MARKETPLACE_PATH);
62715
- const raw = await readFile7(marketplacePath, "utf8");
63268
+ const marketplacePath = options?.marketplacePath ?? join27(repoRoot, DEFAULT_MARKETPLACE_PATH);
63269
+ const raw = await readFile9(marketplacePath, "utf8");
62716
63270
  const parsed = JSON.parse(raw);
62717
- if (!isRecord12(parsed))
63271
+ if (!isRecord14(parsed))
62718
63272
  throw new Error("marketplace.json must be an object");
62719
63273
  if (typeof parsed.name !== "string" || parsed.name.trim() === "") {
62720
63274
  throw new Error("marketplace.json name must be a non-empty string");
@@ -62730,12 +63284,12 @@ async function readMarketplace(repoRoot, options) {
62730
63284
  function resolvePluginSource(repoRoot, plugin, options) {
62731
63285
  const sourcePath = localSourcePath(options?.pathOverride ?? plugin.source);
62732
63286
  const relativePath = sourcePath.slice(2);
62733
- return join25(repoRoot, ...relativePath.split(/[\\/]/));
63287
+ return join27(repoRoot, ...relativePath.split(/[\\/]/));
62734
63288
  }
62735
63289
  async function readPluginManifest(pluginRoot) {
62736
- const raw = await readFile7(join25(pluginRoot, ".codex-plugin", "plugin.json"), "utf8");
63290
+ const raw = await readFile9(join27(pluginRoot, ".codex-plugin", "plugin.json"), "utf8");
62737
63291
  const parsed = JSON.parse(raw);
62738
- if (!isRecord12(parsed))
63292
+ if (!isRecord14(parsed))
62739
63293
  throw new Error(`${pluginRoot} plugin.json must be an object`);
62740
63294
  if (typeof parsed.name !== "string" || parsed.name.trim() === "") {
62741
63295
  throw new Error(`${pluginRoot} plugin.json name must be a non-empty string`);
@@ -62761,7 +63315,7 @@ function validatePathSegment(value, label) {
62761
63315
  }
62762
63316
  }
62763
63317
  function normalizeMarketplacePlugin(plugin, index) {
62764
- if (!isRecord12(plugin))
63318
+ if (!isRecord14(plugin))
62765
63319
  throw new Error(`marketplace plugin ${index} must be an object`);
62766
63320
  if (typeof plugin.name !== "string" || plugin.name.trim() === "") {
62767
63321
  throw new Error(`marketplace plugin ${index} name must be a non-empty string`);
@@ -62773,7 +63327,7 @@ function normalizeMarketplacePlugin(plugin, index) {
62773
63327
  }
62774
63328
  return { name: plugin.name, source: plugin.source };
62775
63329
  }
62776
- if (isRecord12(plugin.source) && plugin.source.source === "local" && typeof plugin.source.path === "string") {
63330
+ if (isRecord14(plugin.source) && plugin.source.source === "local" && typeof plugin.source.path === "string") {
62777
63331
  validateLocalSourcePath(plugin.source.path);
62778
63332
  const local = { source: "local", path: plugin.source.path };
62779
63333
  return { name: plugin.name, source: local };
@@ -62800,13 +63354,13 @@ function validateLocalSourcePath(path6) {
62800
63354
  }
62801
63355
  return path6;
62802
63356
  }
62803
- function isRecord12(value) {
63357
+ function isRecord14(value) {
62804
63358
  return typeof value === "object" && value !== null && !Array.isArray(value);
62805
63359
  }
62806
63360
 
62807
63361
  // src/cli/install-codex/codex-marketplace-snapshot.ts
62808
63362
  import { cp as cp3, mkdir as mkdir5, rename as rename2, rm as rm4, writeFile as writeFile5 } from "fs/promises";
62809
- import { join as join26, sep as sep2 } from "path";
63363
+ import { join as join28, sep as sep2 } from "path";
62810
63364
  var INSTALLED_MARKETPLACES_DIR = ".tmp/marketplaces";
62811
63365
  async function writeInstalledMarketplaceSnapshot(input) {
62812
63366
  const marketplaceRoot = installedMarketplaceRoot(input.codexHome, input.marketplace.name);
@@ -62819,21 +63373,21 @@ async function writeInstalledMarketplaceSnapshot(input) {
62819
63373
  return snapshotPlugins;
62820
63374
  }
62821
63375
  function installedMarketplaceRoot(codexHome, marketplaceName) {
62822
- return join26(codexHome, INSTALLED_MARKETPLACES_DIR, marketplaceName);
63376
+ return join28(codexHome, INSTALLED_MARKETPLACES_DIR, marketplaceName);
62823
63377
  }
62824
63378
  async function writeMarketplaceManifest(marketplaceRoot, marketplace) {
62825
- const manifestDir = join26(marketplaceRoot, ".agents", "plugins");
63379
+ const manifestDir = join28(marketplaceRoot, ".agents", "plugins");
62826
63380
  await mkdir5(manifestDir, { recursive: true });
62827
- const tempPath = join26(manifestDir, `.marketplace-${process.pid}-${Date.now()}.json.tmp`);
63381
+ const tempPath = join28(manifestDir, `.marketplace-${process.pid}-${Date.now()}.json.tmp`);
62828
63382
  await writeFile5(tempPath, `${JSON.stringify(marketplace, null, "\t")}
62829
63383
  `);
62830
- await rename2(tempPath, join26(manifestDir, "marketplace.json"));
63384
+ await rename2(tempPath, join28(manifestDir, "marketplace.json"));
62831
63385
  }
62832
63386
  async function writeSnapshotPlugin(marketplaceRoot, plugin) {
62833
- const pluginsDir = join26(marketplaceRoot, "plugins");
63387
+ const pluginsDir = join28(marketplaceRoot, "plugins");
62834
63388
  await mkdir5(pluginsDir, { recursive: true });
62835
- const targetPath = join26(pluginsDir, plugin.name);
62836
- const tempPath = join26(pluginsDir, `.tmp-${plugin.name}-${process.pid}-${Date.now()}`);
63389
+ const targetPath = join28(pluginsDir, plugin.name);
63390
+ const tempPath = join28(pluginsDir, `.tmp-${plugin.name}-${process.pid}-${Date.now()}`);
62837
63391
  await rm4(tempPath, { recursive: true, force: true });
62838
63392
  await cp3(plugin.sourcePath, tempPath, {
62839
63393
  recursive: true,
@@ -62871,19 +63425,260 @@ var defaultRunCommand = async (command, args, options) => {
62871
63425
  }
62872
63426
  };
62873
63427
 
63428
+ // src/cli/install-codex/codex-project-local-cleanup.ts
63429
+ import { copyFile as copyFile2, lstat as lstat4, readFile as readFile10, writeFile as writeFile6 } from "fs/promises";
63430
+ import { dirname as dirname13, join as join29, resolve as resolve8 } from "path";
63431
+ var LEGACY_AGENT_CONFLICT_KEYS = ["max_threads"];
63432
+ var PROJECT_LOCAL_ARTIFACT_PATHS = [
63433
+ ".omx",
63434
+ ".codex/hooks.json",
63435
+ ".codex/agents",
63436
+ ".codex/prompts",
63437
+ ".codex/skills"
63438
+ ];
63439
+ async function repairNearestProjectLocalCodexArtifacts(input) {
63440
+ const project = await findProjectLocalCodexConfigs(input.startDirectory, input.codexHome);
63441
+ if (project === null) {
63442
+ return emptyProjectLocalCodexCleanupResult();
63443
+ }
63444
+ const artifacts = await collectProjectLocalArtifacts(project.artifactRoots);
63445
+ const configs = [];
63446
+ for (const configPath of project.configPaths) {
63447
+ const original = await readFile10(configPath, "utf8");
63448
+ const repair = repairProjectLocalCodexConfigText(original);
63449
+ if (!repair.changed) {
63450
+ configs.push({
63451
+ projectRoot: project.projectRoot,
63452
+ configPath,
63453
+ changed: false,
63454
+ removedKeys: repair.removedKeys
63455
+ });
63456
+ continue;
63457
+ }
63458
+ const backupPath = `${configPath}.backup-${formatBackupTimestamp(input.now?.() ?? new Date)}`;
63459
+ await copyFile2(configPath, backupPath);
63460
+ await writeFile6(configPath, `${repair.config.trimEnd()}
63461
+ `);
63462
+ configs.push({
63463
+ projectRoot: project.projectRoot,
63464
+ configPath,
63465
+ changed: true,
63466
+ removedKeys: repair.removedKeys,
63467
+ backupPath
63468
+ });
63469
+ }
63470
+ const changedConfigs = configs.filter((config) => config.changed);
63471
+ const nearestChangedConfig = lastValue(changedConfigs);
63472
+ const nearestConfig = lastValue(configs);
63473
+ return {
63474
+ projectRoot: project.projectRoot,
63475
+ configPath: nearestChangedConfig?.configPath ?? nearestConfig?.configPath ?? null,
63476
+ changed: changedConfigs.length > 0,
63477
+ removedKeys: uniqueRemovedKeys(changedConfigs),
63478
+ backupPath: nearestChangedConfig?.backupPath,
63479
+ configs,
63480
+ artifacts
63481
+ };
63482
+ }
63483
+ function emptyProjectLocalCodexCleanupResult() {
63484
+ return {
63485
+ projectRoot: null,
63486
+ configPath: null,
63487
+ changed: false,
63488
+ removedKeys: [],
63489
+ configs: [],
63490
+ artifacts: []
63491
+ };
63492
+ }
63493
+ function uniqueRemovedKeys(configs) {
63494
+ const keys = [];
63495
+ for (const config of configs) {
63496
+ for (const key of config.removedKeys) {
63497
+ if (!keys.includes(key))
63498
+ keys.push(key);
63499
+ }
63500
+ }
63501
+ return keys;
63502
+ }
63503
+ function lastValue(values) {
63504
+ return values.length > 0 ? values[values.length - 1] ?? null : null;
63505
+ }
63506
+ function repairProjectLocalCodexConfigText(config) {
63507
+ if (!isMultiAgentV2Enabled(config))
63508
+ return { config, changed: false, removedKeys: [] };
63509
+ let nextConfig = config;
63510
+ const removedKeys = [];
63511
+ for (const key of LEGACY_AGENT_CONFLICT_KEYS) {
63512
+ const section = findTomlSection2(nextConfig, "agents");
63513
+ if (section === null || !hasSetting(section.text, key))
63514
+ continue;
63515
+ nextConfig = removeSetting2(nextConfig, section, key);
63516
+ removedKeys.push(key);
63517
+ }
63518
+ return {
63519
+ config: nextConfig,
63520
+ changed: removedKeys.length > 0,
63521
+ removedKeys
63522
+ };
63523
+ }
63524
+ async function findProjectLocalCodexConfigs(startDirectory, codexHome) {
63525
+ if (startDirectory.includes("\x00"))
63526
+ return null;
63527
+ const startDirectoryStat = await maybeLstat(startDirectory);
63528
+ if (startDirectoryStat !== null && !startDirectoryStat.isDirectory()) {
63529
+ throw new ProjectLocalCleanupStartDirectoryError(startDirectory);
63530
+ }
63531
+ const codexHomeConfigPath = codexHome === undefined ? null : join29(resolve8(codexHome), "config.toml");
63532
+ let current = resolve8(startDirectory);
63533
+ const configPathsFromCwd = [];
63534
+ while (true) {
63535
+ const configPath = join29(current, ".codex", "config.toml");
63536
+ if (await isRegularProjectLocalConfig(current, configPath)) {
63537
+ if (codexHomeConfigPath === null || resolve8(configPath) !== codexHomeConfigPath) {
63538
+ configPathsFromCwd.push(configPath);
63539
+ }
63540
+ }
63541
+ if (await exists5(join29(current, ".git"))) {
63542
+ return configPathsFromCwd.length === 0 ? null : {
63543
+ projectRoot: current,
63544
+ configPaths: [...configPathsFromCwd].reverse(),
63545
+ artifactRoots: artifactRootsForConfigPaths(configPathsFromCwd)
63546
+ };
63547
+ }
63548
+ const parent = dirname13(current);
63549
+ if (parent === current) {
63550
+ const nearestConfigPath = configPathsFromCwd[0];
63551
+ return nearestConfigPath === undefined ? null : {
63552
+ projectRoot: dirname13(dirname13(nearestConfigPath)),
63553
+ configPaths: [nearestConfigPath],
63554
+ artifactRoots: [dirname13(dirname13(nearestConfigPath))]
63555
+ };
63556
+ }
63557
+ current = parent;
63558
+ }
63559
+ }
63560
+ async function isRegularProjectLocalConfig(directory, configPath) {
63561
+ const codexDirStat = await maybeLstat(join29(directory, ".codex"));
63562
+ if (codexDirStat === null || !codexDirStat.isDirectory() || codexDirStat.isSymbolicLink())
63563
+ return false;
63564
+ const configStat = await maybeLstat(configPath);
63565
+ return configStat !== null && configStat.isFile() && !configStat.isSymbolicLink();
63566
+ }
63567
+ function artifactRootsForConfigPaths(configPaths) {
63568
+ const roots = [];
63569
+ for (const configPath of configPaths) {
63570
+ const root = dirname13(dirname13(configPath));
63571
+ if (!roots.includes(root))
63572
+ roots.push(root);
63573
+ }
63574
+ return roots.reverse();
63575
+ }
63576
+ async function collectProjectLocalArtifacts(projectRoots) {
63577
+ const artifacts = [];
63578
+ const seenPaths = new Set;
63579
+ for (const projectRoot of projectRoots) {
63580
+ for (const relativePath of PROJECT_LOCAL_ARTIFACT_PATHS) {
63581
+ const artifactPath = join29(projectRoot, relativePath);
63582
+ if (seenPaths.has(artifactPath))
63583
+ continue;
63584
+ const entryStat = await maybeLstat(artifactPath);
63585
+ if (entryStat === null)
63586
+ continue;
63587
+ seenPaths.add(artifactPath);
63588
+ artifacts.push({
63589
+ relativePath,
63590
+ path: artifactPath,
63591
+ kind: entryStat.isDirectory() ? "directory" : entryStat.isFile() ? "file" : "other"
63592
+ });
63593
+ }
63594
+ }
63595
+ return artifacts;
63596
+ }
63597
+ function isMultiAgentV2Enabled(config) {
63598
+ const featuresSection = findTomlSection2(config, "features");
63599
+ if (featuresSection !== null && settingIsBooleanTrue(featuresSection.text, "multi_agent_v2"))
63600
+ return true;
63601
+ const multiAgentSection = findTomlSection2(config, "features.multi_agent_v2");
63602
+ return multiAgentSection !== null && settingIsBooleanTrue(multiAgentSection.text, "enabled");
63603
+ }
63604
+ function settingIsBooleanTrue(sectionText, key) {
63605
+ return new RegExp(`^\\s*${escapeRegExp2(key)}\\s*=\\s*true\\s*(?:#.*)?$`, "m").test(sectionText);
63606
+ }
63607
+ function hasSetting(sectionText, key) {
63608
+ return new RegExp(`^\\s*${escapeRegExp2(key)}\\s*=`, "m").test(sectionText);
63609
+ }
63610
+ function formatBackupTimestamp(date) {
63611
+ return date.toISOString().replace(/[:.]/g, "-");
63612
+ }
63613
+ async function maybeLstat(path6) {
63614
+ try {
63615
+ return await lstat4(path6);
63616
+ } catch (error) {
63617
+ if (nodeErrorCode(error) === "ENOENT")
63618
+ return null;
63619
+ throw error;
63620
+ }
63621
+ }
63622
+ async function exists5(path6) {
63623
+ return await maybeLstat(path6) !== null;
63624
+ }
63625
+ function nodeErrorCode(error) {
63626
+ if (!(error instanceof Error) || !("code" in error))
63627
+ return null;
63628
+ return typeof error.code === "string" ? error.code : null;
63629
+ }
63630
+
63631
+ class ProjectLocalCleanupStartDirectoryError extends Error {
63632
+ constructor(startDirectory) {
63633
+ super(`Project-local Codex cleanup start path is not a directory: ${startDirectory}`);
63634
+ this.name = "ProjectLocalCleanupStartDirectoryError";
63635
+ }
63636
+ }
63637
+
63638
+ // src/cli/install-codex/codex-project-local-cleanup-best-effort.ts
63639
+ async function repairProjectLocalCodexArtifactsBestEffort(input) {
63640
+ try {
63641
+ return await repairNearestProjectLocalCodexArtifacts({
63642
+ startDirectory: input.startDirectory,
63643
+ codexHome: input.codexHome,
63644
+ now: input.now
63645
+ });
63646
+ } catch (error) {
63647
+ input.log(`Skipped project-local Codex cleanup: ${formatUnknownError(error)}`);
63648
+ return emptyProjectLocalCodexCleanupResult();
63649
+ }
63650
+ }
63651
+ function formatUnknownError(error) {
63652
+ return error instanceof Error ? error.message : String(error);
63653
+ }
63654
+
62874
63655
  // src/cli/install-codex/install-codex.ts
62875
63656
  var SISYPHUS_LEGACY_CACHE_MARKETPLACES = ["lazycodex", "code-yeongyu-codex-plugins"];
62876
63657
  async function runCodexInstaller(options = {}) {
62877
- const repoRoot = resolve8(options.repoRoot ?? findRepoRoot({ importerDir: import.meta.dir, env: process.env }));
62878
- const codexHome = resolve8(options.codexHome ?? process.env.CODEX_HOME ?? join28(homedir5(), ".codex"));
62879
- const binDir = resolveCodexInstallerBinDir({ binDir: options.binDir, codexHome, env: process.env });
63658
+ const env = options.env ?? process.env;
63659
+ const platform = options.platform ?? process.platform;
63660
+ const repoRoot = resolve9(options.repoRoot ?? findRepoRoot({ importerDir: import.meta.dir, env }));
63661
+ const codexHome = resolve9(options.codexHome ?? env.CODEX_HOME ?? join31(homedir5(), ".codex"));
63662
+ const projectDirectory = resolve9(options.projectDirectory ?? env.OMO_CODEX_PROJECT ?? process.cwd());
63663
+ const binDir = resolveCodexInstallerBinDir({ binDir: options.binDir, codexHome, env });
62880
63664
  const runCommand = options.runCommand ?? defaultRunCommand;
62881
63665
  const log2 = options.log ?? (() => {
62882
63666
  return;
62883
63667
  });
62884
- const codexPackageRoot = join28(repoRoot, "packages", "omo-codex");
63668
+ const buildSource = await shouldBuildSourcePackages(repoRoot);
63669
+ const gitBashResolution = await prepareGitBashForInstall({
63670
+ platform,
63671
+ env,
63672
+ cwd: repoRoot,
63673
+ runCommand,
63674
+ resolveGitBash: platform === "win32" ? options.gitBashResolver ?? (() => resolveGitBashForCurrentProcess({ platform, env })) : undefined
63675
+ });
63676
+ if (!gitBashResolution.found) {
63677
+ throw new Error(gitBashResolution.installHint);
63678
+ }
63679
+ const codexPackageRoot = join31(repoRoot, "packages", "omo-codex");
62885
63680
  const marketplace = await readMarketplace(repoRoot, {
62886
- marketplacePath: join28(codexPackageRoot, "marketplace.json")
63681
+ marketplacePath: join31(codexPackageRoot, "marketplace.json")
62887
63682
  });
62888
63683
  const installed = [];
62889
63684
  const pluginSources = [];
@@ -62898,6 +63693,7 @@ async function runCodexInstaller(options = {}) {
62898
63693
  validatePathSegment(version2, "plugin version");
62899
63694
  log2(`Building ${entry.name}@${version2}`);
62900
63695
  const plugin = await installCachedPlugin({
63696
+ buildSource,
62901
63697
  codexHome,
62902
63698
  marketplaceName: marketplace.name,
62903
63699
  name: entry.name,
@@ -62905,7 +63701,7 @@ async function runCodexInstaller(options = {}) {
62905
63701
  sourcePath,
62906
63702
  version: version2
62907
63703
  });
62908
- const links = await linkCachedPluginBins({ binDir, pluginRoot: plugin.path });
63704
+ const links = await linkCachedPluginBins({ binDir, pluginRoot: plugin.path, platform });
62909
63705
  for (const link of links) {
62910
63706
  log2(`Linked ${link.name} -> ${link.target}`);
62911
63707
  }
@@ -62920,7 +63716,7 @@ async function runCodexInstaller(options = {}) {
62920
63716
  });
62921
63717
  for (const plugin of installed) {
62922
63718
  const pluginRoot = agentSourceRoots.get(plugin.name) ?? plugin.path;
62923
- const agentLinks = await linkCachedPluginAgents({ codexHome, pluginRoot });
63719
+ const agentLinks = await linkCachedPluginAgents({ codexHome, pluginRoot, platform });
62924
63720
  for (const link of agentLinks) {
62925
63721
  log2(`Linked agent ${link.name} -> ${link.target}`);
62926
63722
  const agentName = agentNameFromToml(link.name);
@@ -62944,41 +63740,57 @@ async function runCodexInstaller(options = {}) {
62944
63740
  pluginNames: marketplace.plugins.map((plugin) => plugin.name)
62945
63741
  });
62946
63742
  }
62947
- const marketplaceRoot = join28(codexHome, "plugins", "cache", marketplace.name);
63743
+ const marketplaceRoot = join31(codexHome, "plugins", "cache", marketplace.name);
62948
63744
  await writeCachedMarketplaceManifest({
62949
63745
  marketplaceName: marketplace.name,
62950
63746
  marketplaceRoot,
62951
63747
  plugins: installed
62952
63748
  });
62953
- const configPath = join28(codexHome, "config.toml");
63749
+ const configPath = join31(codexHome, "config.toml");
62954
63750
  await updateCodexConfig({
62955
63751
  configPath,
62956
63752
  repoRoot: codexPackageRoot,
62957
63753
  marketplaceName: marketplace.name,
62958
63754
  marketplaceSource: codexMarketplaceSource(marketplaceRoot),
62959
63755
  pluginNames: marketplace.plugins.map((plugin) => plugin.name),
63756
+ platform,
62960
63757
  trustedHookStates,
62961
63758
  agentConfigs: [...agentConfigs.values()].sort((left, right) => left.name.localeCompare(right.name)),
62962
- autonomousPermissions: options.autonomousPermissions === true
63759
+ autonomousPermissions: options.autonomousPermissions !== false
63760
+ });
63761
+ const projectCleanup = await repairProjectLocalCodexArtifactsBestEffort({
63762
+ startDirectory: projectDirectory,
63763
+ codexHome,
63764
+ log: log2
62963
63765
  });
63766
+ for (const configCleanup of projectCleanup.configs) {
63767
+ if (!configCleanup.changed)
63768
+ continue;
63769
+ log2(`Repaired project Codex config ${configCleanup.configPath} (backup: ${configCleanup.backupPath})`);
63770
+ }
63771
+ for (const artifact of projectCleanup.artifacts) {
63772
+ log2(`Found project-local legacy artifact ${artifact.path}; left in place`);
63773
+ }
62964
63774
  await trackCodexInstallTelemetry();
62965
63775
  return {
62966
63776
  marketplaceName: marketplace.name,
62967
63777
  installed,
62968
63778
  configPath,
62969
- codexHome
63779
+ codexHome,
63780
+ gitBashPath: gitBashResolution.path,
63781
+ projectCleanup
62970
63782
  };
62971
63783
  }
62972
63784
  function resolveCodexInstallerBinDir(input) {
62973
63785
  const explicitBinDir = input.binDir ?? input.env?.CODEX_LOCAL_BIN_DIR;
62974
63786
  if (explicitBinDir !== undefined && explicitBinDir.trim().length > 0)
62975
- return resolve8(explicitBinDir);
63787
+ return resolve9(explicitBinDir);
62976
63788
  const homeDir = input.homeDir ?? homedir5();
62977
- const defaultCodexHome = resolve8(homeDir, ".codex");
62978
- const resolvedCodexHome = resolve8(input.codexHome);
63789
+ const defaultCodexHome = resolve9(homeDir, ".codex");
63790
+ const resolvedCodexHome = resolve9(input.codexHome);
62979
63791
  if (resolvedCodexHome !== defaultCodexHome)
62980
- return join28(resolvedCodexHome, "bin");
62981
- return resolve8(homeDir, ".local", "bin");
63792
+ return join31(resolvedCodexHome, "bin");
63793
+ return resolve9(homeDir, ".local", "bin");
62982
63794
  }
62983
63795
  function agentNameFromToml(fileName) {
62984
63796
  return fileName.endsWith(".toml") ? fileName.slice(0, -".toml".length) : fileName;
@@ -62995,9 +63807,9 @@ async function agentSourceRootsForInstall(input) {
62995
63807
  return new Map(snapshotPlugins.map((plugin) => [plugin.name, plugin.path]));
62996
63808
  }
62997
63809
  async function writeCachedMarketplaceManifest(input) {
62998
- const marketplaceDir = join28(input.marketplaceRoot, ".agents", "plugins");
63810
+ const marketplaceDir = join31(input.marketplaceRoot, ".agents", "plugins");
62999
63811
  await mkdir6(marketplaceDir, { recursive: true });
63000
- await writeFile6(join28(marketplaceDir, "marketplace.json"), `${JSON.stringify({
63812
+ await writeFile7(join31(marketplaceDir, "marketplace.json"), `${JSON.stringify({
63001
63813
  name: input.marketplaceName,
63002
63814
  plugins: input.plugins.map((plugin) => ({
63003
63815
  name: plugin.name,
@@ -63009,36 +63821,33 @@ async function writeCachedMarketplaceManifest(input) {
63009
63821
  function legacyCacheMarketplaces(marketplaceName) {
63010
63822
  return marketplaceName === "sisyphuslabs" ? SISYPHUS_LEGACY_CACHE_MARKETPLACES : [];
63011
63823
  }
63012
- function codexMarketplaceSource(marketplaceRoot) {
63013
- return { sourceType: "local", source: marketplaceRoot };
63014
- }
63015
63824
  function findRepoRootFromImporter(importerDir) {
63016
63825
  let current = importerDir;
63017
63826
  for (let depth = 0;depth <= 5; depth += 1) {
63018
63827
  if (isRepoRootWithCodexPlugin(current))
63019
63828
  return current;
63020
- for (const wrapperPackageRoot of [join28(current, "node_modules", "oh-my-openagent"), join28(current, "oh-my-openagent")]) {
63829
+ for (const wrapperPackageRoot of [join31(current, "node_modules", "oh-my-openagent"), join31(current, "oh-my-openagent")]) {
63021
63830
  if (isRepoRootWithCodexPlugin(wrapperPackageRoot))
63022
63831
  return wrapperPackageRoot;
63023
63832
  }
63024
- current = resolve8(current, "..");
63833
+ current = resolve9(current, "..");
63025
63834
  }
63026
63835
  throw new Error("Unable to locate vendored Codex plugin: expected packages/omo-codex/plugin/.codex-plugin/plugin.json in this package or sibling oh-my-openagent package within 5 parent levels");
63027
63836
  }
63028
63837
  function findRepoRoot(input) {
63029
63838
  const wrapperPackageRoot = input.env?.OMO_WRAPPER_PACKAGE_ROOT;
63030
63839
  if (wrapperPackageRoot !== undefined && wrapperPackageRoot.trim().length > 0) {
63031
- const resolvedWrapperPackageRoot = resolve8(wrapperPackageRoot);
63840
+ const resolvedWrapperPackageRoot = resolve9(wrapperPackageRoot);
63032
63841
  if (isRepoRootWithCodexPlugin(resolvedWrapperPackageRoot))
63033
63842
  return resolvedWrapperPackageRoot;
63034
63843
  }
63035
63844
  return findRepoRootFromImporter(input.importerDir);
63036
63845
  }
63037
63846
  function isRepoRootWithCodexPlugin(repoRoot) {
63038
- return existsSyncLike(join28(repoRoot, "packages", "omo-codex", "plugin", ".codex-plugin", "plugin.json"));
63847
+ return existsSync25(join31(repoRoot, "packages", "omo-codex", "plugin", ".codex-plugin", "plugin.json"));
63039
63848
  }
63040
- function existsSyncLike(path7) {
63041
- return existsSync23(path7);
63849
+ function codexMarketplaceSource(marketplaceRoot) {
63850
+ return { sourceType: "local", source: marketplaceRoot };
63042
63851
  }
63043
63852
  async function trackCodexInstallTelemetry() {
63044
63853
  try {
@@ -63052,13 +63861,507 @@ async function trackCodexInstallTelemetry() {
63052
63861
  return;
63053
63862
  }
63054
63863
  }
63864
+ // src/cli/install-codex/codex-installation-detection.ts
63865
+ import { execFile } from "child_process";
63866
+ import { existsSync as existsSync26 } from "fs";
63867
+ import { homedir as homedir6 } from "os";
63868
+ import { join as join33, win32 as win322 } from "path";
63869
+
63870
+ // src/shared/bun-which-shim.ts
63871
+ import { accessSync as accessSync3, constants as constants4 } from "fs";
63872
+ import { delimiter, join as join32 } from "path";
63873
+ var runtime3 = globalThis;
63874
+ var IS_BUN2 = typeof runtime3.Bun !== "undefined";
63875
+ function isUnsafeCommandName(commandName) {
63876
+ if (commandName.includes("/") || commandName.includes("\\"))
63877
+ return true;
63878
+ if (commandName === "." || commandName === ".." || commandName.includes(".."))
63879
+ return true;
63880
+ if (/^[a-zA-Z]:/.test(commandName))
63881
+ return true;
63882
+ if (commandName.includes("\x00"))
63883
+ return true;
63884
+ return false;
63885
+ }
63886
+ function isExecutable(filePath) {
63887
+ try {
63888
+ accessSync3(filePath, constants4.X_OK);
63889
+ return true;
63890
+ } catch {
63891
+ return false;
63892
+ }
63893
+ }
63894
+ function resolvePathValue() {
63895
+ if (process.platform === "win32")
63896
+ return process.env.Path ?? process.env.PATH;
63897
+ return process.env.PATH;
63898
+ }
63899
+ function getWindowsCandidates(commandName) {
63900
+ if (process.platform !== "win32")
63901
+ return [commandName];
63902
+ return [commandName, `${commandName}.exe`, `${commandName}.cmd`, `${commandName}.bat`, `${commandName}.com`];
63903
+ }
63904
+ function bunWhich(commandName) {
63905
+ if (!commandName)
63906
+ return null;
63907
+ if (isUnsafeCommandName(commandName))
63908
+ return null;
63909
+ if (IS_BUN2)
63910
+ return runtime3.Bun?.which(commandName) ?? null;
63911
+ const pathValue = resolvePathValue();
63912
+ if (!pathValue)
63913
+ return null;
63914
+ const pathEntries = pathValue.split(delimiter).filter((pathEntry) => pathEntry.length > 0);
63915
+ if (pathEntries.length === 0)
63916
+ return null;
63917
+ const candidateNames = getWindowsCandidates(commandName);
63918
+ for (const pathEntry of pathEntries) {
63919
+ for (const candidateName of candidateNames) {
63920
+ const candidatePath = join32(pathEntry, candidateName);
63921
+ if (isExecutable(candidatePath))
63922
+ return candidatePath;
63923
+ }
63924
+ }
63925
+ return null;
63926
+ }
63927
+
63928
+ // src/cli/install-codex/codex-installation-detection.ts
63929
+ var CODEX_PATH_CHECK_LABEL = "codex (PATH)";
63930
+ var WINDOWS_START_APPS_ARGS = [
63931
+ "-NoProfile",
63932
+ "-Command",
63933
+ "Get-StartApps -Name 'Codex' | Select-Object -First 1 -ExpandProperty AppID"
63934
+ ];
63935
+ async function detectCodexInstallation(input = {}) {
63936
+ const platform = input.platform ?? process.platform;
63937
+ const env = input.env ?? process.env;
63938
+ const homeDir = input.homeDir ?? homedir6();
63939
+ const exists6 = input.exists ?? existsSync26;
63940
+ const which = input.which ?? bunWhich;
63941
+ const checkedPaths = [CODEX_PATH_CHECK_LABEL];
63942
+ const cliPath = nonEmptyValue(which("codex"));
63943
+ if (cliPath !== undefined)
63944
+ return { found: true, source: "cli", path: cliPath };
63945
+ if (platform === "darwin") {
63946
+ return detectMacCodexInstallation({ homeDir, exists: exists6, checkedPaths });
63947
+ }
63948
+ if (platform === "win32") {
63949
+ return detectWindowsCodexInstallation({
63950
+ env,
63951
+ exists: exists6,
63952
+ checkedPaths,
63953
+ runCommand: input.runCommand ?? defaultRunCommand2
63954
+ });
63955
+ }
63956
+ return missingCodexInstallation(checkedPaths, "Install OpenAI Codex CLI and make sure `codex` is on PATH.");
63957
+ }
63958
+ function formatCodexInstallationWarning(detection) {
63959
+ return [
63960
+ "Codex CLI or desktop app was not detected. LazyCodex will still install the Codex plugin, but Codex itself must be installed to use it.",
63961
+ detection.hint,
63962
+ `Checked: ${detection.checkedPaths.join(", ")}`
63963
+ ].join(`
63964
+ `);
63965
+ }
63966
+ function detectMacCodexInstallation(input) {
63967
+ for (const appPath of macCodexAppPaths(input.homeDir)) {
63968
+ input.checkedPaths.push(appPath);
63969
+ if (input.exists(appPath))
63970
+ return { found: true, source: "mac-app", path: appPath };
63971
+ }
63972
+ for (const dmgPath of macCodexDmgPaths(input.homeDir)) {
63973
+ input.checkedPaths.push(dmgPath);
63974
+ if (input.exists(dmgPath)) {
63975
+ return missingCodexInstallation(input.checkedPaths, `Open ${dmgPath} to install Codex Desktop, or install OpenAI Codex CLI and make sure \`codex\` is on PATH.`, dmgPath);
63976
+ }
63977
+ }
63978
+ return missingCodexInstallation(input.checkedPaths, "Install OpenAI Codex CLI, or install Codex Desktop for macOS.");
63979
+ }
63980
+ async function detectWindowsCodexInstallation(input) {
63981
+ for (const candidate of windowsCodexCliPaths(input.env)) {
63982
+ input.checkedPaths.push(candidate);
63983
+ if (input.exists(candidate))
63984
+ return { found: true, source: "windows-standard-cli", path: candidate };
63985
+ }
63986
+ input.checkedPaths.push("Windows Start Apps: Codex");
63987
+ const appId = await findWindowsCodexStartApp(input.runCommand);
63988
+ if (appId !== null)
63989
+ return { found: true, source: "windows-start-app", appId };
63990
+ return missingCodexInstallation(input.checkedPaths, "Install OpenAI Codex CLI, or install Codex Desktop from Microsoft Store.");
63991
+ }
63992
+ async function findWindowsCodexStartApp(runCommand) {
63993
+ try {
63994
+ const result = await runCommand("powershell.exe", WINDOWS_START_APPS_ARGS);
63995
+ if (!result.success)
63996
+ return null;
63997
+ return nonEmptyValue(result.stdout.trim()) ?? null;
63998
+ } catch (error) {
63999
+ if (error instanceof Error)
64000
+ return null;
64001
+ throw error;
64002
+ }
64003
+ }
64004
+ function macCodexAppPaths(homeDir) {
64005
+ return ["/Applications/Codex.app", join33(homeDir, "Applications", "Codex.app")];
64006
+ }
64007
+ function macCodexDmgPaths(homeDir) {
64008
+ const downloads = join33(homeDir, "Downloads");
64009
+ return [join33(downloads, "codex.dmg"), join33(downloads, "Codex.dmg")];
64010
+ }
64011
+ function windowsCodexCliPaths(env) {
64012
+ const candidates = [];
64013
+ const explicitInstallDir = nonEmptyValue(env.CODEX_INSTALL_DIR);
64014
+ if (explicitInstallDir !== undefined)
64015
+ candidates.push(win322.join(explicitInstallDir, "codex.exe"));
64016
+ const localAppData = nonEmptyValue(env.LOCALAPPDATA) ?? nonEmptyValue(env.LocalAppData);
64017
+ if (localAppData !== undefined) {
64018
+ candidates.push(win322.join(localAppData, "Programs", "OpenAI", "Codex", "bin", "codex.exe"));
64019
+ }
64020
+ return dedupe(candidates);
64021
+ }
64022
+ function missingCodexInstallation(checkedPaths, hint, downloadedInstallerPath) {
64023
+ if (downloadedInstallerPath !== undefined) {
64024
+ return { found: false, checkedPaths, hint, downloadedInstallerPath };
64025
+ }
64026
+ return { found: false, checkedPaths, hint };
64027
+ }
64028
+ function nonEmptyValue(value) {
64029
+ if (value === null || value === undefined)
64030
+ return;
64031
+ const trimmed = value.trim();
64032
+ return trimmed.length > 0 ? trimmed : undefined;
64033
+ }
64034
+ function dedupe(values) {
64035
+ const seen = new Set;
64036
+ const deduped = [];
64037
+ for (const value of values) {
64038
+ if (seen.has(value))
64039
+ continue;
64040
+ seen.add(value);
64041
+ deduped.push(value);
64042
+ }
64043
+ return deduped;
64044
+ }
64045
+ function defaultRunCommand2(command, args) {
64046
+ return new Promise((resolve10) => {
64047
+ execFile(command, [...args], { encoding: "utf8", windowsHide: true }, (error, stdout) => {
64048
+ resolve10({ success: error === null, stdout });
64049
+ });
64050
+ });
64051
+ }
64052
+ // src/cli/install-codex/codex-cleanup.ts
64053
+ import { lstat as lstat6, readFile as readFile12, readdir as readdir4, rm as rm5 } from "fs/promises";
64054
+ import { homedir as homedir7 } from "os";
64055
+ import { isAbsolute as isAbsolute6, join as join34, relative as relative5, resolve as resolve10 } from "path";
64056
+
64057
+ // src/cli/install-codex/codex-cleanup-config.ts
64058
+ import { lstat as lstat5, mkdir as mkdir7, readFile as readFile11, writeFile as writeFile8 } from "fs/promises";
64059
+ import { dirname as dirname15 } from "path";
64060
+ var MANAGED_MARKETPLACES = ["sisyphuslabs", "lazycodex", "code-yeongyu-codex-plugins"];
64061
+ var MANAGED_CODEX_AGENT_NAMES2 = [
64062
+ "codex-ultrawork-reviewer",
64063
+ "explorer",
64064
+ "librarian",
64065
+ "metis",
64066
+ "momus",
64067
+ "plan"
64068
+ ];
64069
+ function cleanupCodexLightConfigText(config) {
64070
+ let nextConfig = config;
64071
+ for (const marketplace of MANAGED_MARKETPLACES) {
64072
+ nextConfig = removeTomlSections2(nextConfig, (header) => header === `marketplaces.${marketplace}`);
64073
+ nextConfig = removeTomlSections2(nextConfig, (header) => isManagedPluginHeader(header, marketplace));
64074
+ nextConfig = removeTomlSections2(nextConfig, (header) => isManagedHookStateHeader(header, marketplace));
64075
+ }
64076
+ nextConfig = removeManagedAgentBlocks(nextConfig);
64077
+ return nextConfig.replace(/\n{3,}/g, `
64078
+
64079
+ `);
64080
+ }
64081
+ async function cleanupCodexConfig(configPath, now) {
64082
+ if (!await configExists(configPath))
64083
+ return { changed: false };
64084
+ const original = await readFile11(configPath, "utf8");
64085
+ const next = cleanupCodexLightConfigText(original);
64086
+ if (next === original)
64087
+ return { changed: false };
64088
+ const backupPath = `${configPath}.backup-${formatBackupTimestamp2(now?.() ?? new Date)}`;
64089
+ await mkdir7(dirname15(configPath), { recursive: true });
64090
+ await writeFile8(backupPath, original);
64091
+ await writeFile8(configPath, `${next.trimEnd()}
64092
+ `);
64093
+ return { changed: true, backupPath };
64094
+ }
64095
+ function removeManagedAgentBlocks(config) {
64096
+ const managedAgentNames = new Set(MANAGED_CODEX_AGENT_NAMES2);
64097
+ return splitTomlSections2(config).filter((section) => {
64098
+ if (section.header === null)
64099
+ return true;
64100
+ const agentName = parseAgentHeaderName2(section.header);
64101
+ if (agentName === null || !managedAgentNames.has(agentName))
64102
+ return true;
64103
+ return !section.text.includes(`config_file = ${JSON.stringify(`./agents/${agentName}.toml`)}`);
64104
+ }).map((section) => section.text).join("");
64105
+ }
64106
+ function isManagedPluginHeader(header, marketplace) {
64107
+ const pluginKey = parsePluginHeaderKey2(header);
64108
+ return pluginKey !== null && pluginKey.endsWith(`@${marketplace}`);
64109
+ }
64110
+ function isManagedHookStateHeader(header, marketplace) {
64111
+ const prefix = "hooks.state.";
64112
+ if (!header.startsWith(prefix))
64113
+ return false;
64114
+ const hookKey = parseJsonString2(header.slice(prefix.length));
64115
+ if (hookKey === null)
64116
+ return false;
64117
+ const separator = hookKey.indexOf(":");
64118
+ if (separator === -1)
64119
+ return false;
64120
+ return hookKey.slice(0, separator).endsWith(`@${marketplace}`);
64121
+ }
64122
+ function removeTomlSections2(config, shouldRemove) {
64123
+ return splitTomlSections2(config).filter((section) => section.header === null || !shouldRemove(section.header)).map((section) => section.text).join("");
64124
+ }
64125
+ function splitTomlSections2(config) {
64126
+ const lines = config.match(/[^\n]*\n?|$/g) ?? [];
64127
+ const sections = [];
64128
+ let current = { header: null, text: "" };
64129
+ for (const line of lines) {
64130
+ if (line.length === 0)
64131
+ break;
64132
+ const header = parseTomlHeader2(line);
64133
+ if (header !== null) {
64134
+ if (current.text.length > 0)
64135
+ sections.push(current);
64136
+ current = { header, text: line };
64137
+ } else {
64138
+ current.text += line;
64139
+ }
64140
+ }
64141
+ if (current.text.length > 0)
64142
+ sections.push(current);
64143
+ return sections;
64144
+ }
64145
+ function parseTomlHeader2(line) {
64146
+ const trimmed = line.trim();
64147
+ if (!trimmed.startsWith("[") || !trimmed.endsWith("]") || trimmed.startsWith("[["))
64148
+ return null;
64149
+ return trimmed.slice(1, -1);
64150
+ }
64151
+ function parsePluginHeaderKey2(header) {
64152
+ const prefix = "plugins.";
64153
+ if (!header.startsWith(prefix))
64154
+ return null;
64155
+ return parseLeadingJsonString2(header.slice(prefix.length));
64156
+ }
64157
+ function parseAgentHeaderName2(header) {
64158
+ const prefix = "agents.";
64159
+ if (!header.startsWith(prefix))
64160
+ return null;
64161
+ const key = header.slice(prefix.length);
64162
+ return key.startsWith('"') ? parseLeadingJsonString2(key) : key;
64163
+ }
64164
+ function parseLeadingJsonString2(value) {
64165
+ if (!value.startsWith('"'))
64166
+ return parseJsonString2(value);
64167
+ let escaped = false;
64168
+ for (let index = 1;index < value.length; index += 1) {
64169
+ const char = value[index];
64170
+ if (escaped) {
64171
+ escaped = false;
64172
+ continue;
64173
+ }
64174
+ if (char === "\\") {
64175
+ escaped = true;
64176
+ continue;
64177
+ }
64178
+ if (char === '"')
64179
+ return parseJsonString2(value.slice(0, index + 1));
64180
+ }
64181
+ return null;
64182
+ }
64183
+ function parseJsonString2(value) {
64184
+ try {
64185
+ const parsed = JSON.parse(value);
64186
+ return typeof parsed === "string" ? parsed : null;
64187
+ } catch (error) {
64188
+ if (error instanceof Error)
64189
+ return null;
64190
+ return null;
64191
+ }
64192
+ }
64193
+ function formatBackupTimestamp2(date) {
64194
+ return date.toISOString().replace(/[:.]/g, "-");
64195
+ }
64196
+ async function configExists(path7) {
64197
+ try {
64198
+ await lstat5(path7);
64199
+ return true;
64200
+ } catch (error) {
64201
+ if (nodeErrorCode2(error) === "ENOENT")
64202
+ return false;
64203
+ throw error;
64204
+ }
64205
+ }
64206
+ function nodeErrorCode2(error) {
64207
+ if (!(error instanceof Error) || !("code" in error))
64208
+ return null;
64209
+ return typeof error.code === "string" ? error.code : null;
64210
+ }
64211
+
64212
+ // src/cli/install-codex/codex-cleanup.ts
64213
+ var INSTALLED_AGENTS_MANIFEST = ".installed-agents.json";
64214
+ async function cleanupCodexLight(input = {}) {
64215
+ const env = input.env ?? process.env;
64216
+ const codexHome = resolve10(input.codexHome ?? env.CODEX_HOME ?? join34(homedir7(), ".codex"));
64217
+ const configPath = join34(codexHome, "config.toml");
64218
+ const agentPaths = await collectInstalledAgentPaths(codexHome);
64219
+ const configCleanup = await cleanupCodexConfig(configPath, input.now);
64220
+ const agentCleanup = await removeManifestListedAgentLinks(codexHome, agentPaths);
64221
+ const removedPaths = [];
64222
+ for (const path7 of managedGlobalStatePaths(codexHome)) {
64223
+ if (await removePathIfExists(path7))
64224
+ removedPaths.push(path7);
64225
+ }
64226
+ const projectDirectory = input.projectDirectory ?? env.OMO_CODEX_PROJECT ?? process.cwd();
64227
+ const projectCleanup = await repairProjectLocalCodexArtifactsBestEffort({
64228
+ startDirectory: projectDirectory,
64229
+ codexHome,
64230
+ now: input.now,
64231
+ log: () => {
64232
+ return;
64233
+ }
64234
+ });
64235
+ return {
64236
+ codexHome,
64237
+ configPath,
64238
+ configChanged: configCleanup.changed,
64239
+ configBackupPath: configCleanup.backupPath,
64240
+ removedPaths,
64241
+ removedAgentLinks: agentCleanup.removed,
64242
+ skippedAgentLinks: agentCleanup.skipped,
64243
+ projectCleanup
64244
+ };
64245
+ }
64246
+ function managedGlobalStatePaths(codexHome) {
64247
+ return [
64248
+ join34(codexHome, "plugins", "cache", "sisyphuslabs"),
64249
+ join34(codexHome, ".tmp", "marketplaces", "sisyphuslabs")
64250
+ ];
64251
+ }
64252
+ async function collectInstalledAgentPaths(codexHome) {
64253
+ const manifestPaths = [
64254
+ join34(codexHome, ".tmp", "marketplaces", "sisyphuslabs", "plugins", "omo", INSTALLED_AGENTS_MANIFEST)
64255
+ ];
64256
+ const versionRoot = join34(codexHome, "plugins", "cache", "sisyphuslabs", "omo");
64257
+ if (await exists6(versionRoot)) {
64258
+ const entries = await readdir4(versionRoot, { withFileTypes: true });
64259
+ for (const entry of entries) {
64260
+ if (entry.isDirectory())
64261
+ manifestPaths.push(join34(versionRoot, entry.name, INSTALLED_AGENTS_MANIFEST));
64262
+ }
64263
+ }
64264
+ const paths = new Set;
64265
+ for (const manifestPath of manifestPaths) {
64266
+ for (const path7 of await readInstalledAgentManifest(manifestPath)) {
64267
+ paths.add(path7);
64268
+ }
64269
+ }
64270
+ return [...paths].sort();
64271
+ }
64272
+ async function readInstalledAgentManifest(manifestPath) {
64273
+ if (!await exists6(manifestPath))
64274
+ return [];
64275
+ const parsed = JSON.parse(await readFile12(manifestPath, "utf8"));
64276
+ if (!isRecord15(parsed) || !Array.isArray(parsed.agents))
64277
+ return [];
64278
+ return parsed.agents.filter((path7) => typeof path7 === "string");
64279
+ }
64280
+ async function removeManifestListedAgentLinks(codexHome, paths) {
64281
+ const agentsDir = join34(codexHome, "agents");
64282
+ const removed = [];
64283
+ const skipped = [];
64284
+ for (const path7 of paths) {
64285
+ if (!isSafeManagedAgentPath(agentsDir, path7)) {
64286
+ skipped.push(path7);
64287
+ continue;
64288
+ }
64289
+ const entryStat = await maybeLstat2(path7);
64290
+ if (entryStat === null)
64291
+ continue;
64292
+ if (entryStat.isDirectory() && !entryStat.isSymbolicLink()) {
64293
+ skipped.push(path7);
64294
+ continue;
64295
+ }
64296
+ await rm5(path7, { force: true });
64297
+ removed.push(path7);
64298
+ }
64299
+ return { removed, skipped };
64300
+ }
64301
+ function isSafeManagedAgentPath(agentsDir, path7) {
64302
+ if (!isAbsolute6(path7))
64303
+ return false;
64304
+ const relativePath = relative5(agentsDir, path7);
64305
+ if (relativePath === "" || relativePath.startsWith("..") || isAbsolute6(relativePath))
64306
+ return false;
64307
+ const fileName = relativePath.split(/[\\/]/).pop();
64308
+ if (fileName === undefined)
64309
+ return false;
64310
+ return MANAGED_CODEX_AGENT_NAMES2.some((agentName) => fileName === `${agentName}.toml`);
64311
+ }
64312
+ async function removePathIfExists(path7) {
64313
+ if (!await exists6(path7))
64314
+ return false;
64315
+ await rm5(path7, { recursive: true, force: true });
64316
+ return true;
64317
+ }
64318
+ async function exists6(path7) {
64319
+ return await maybeLstat2(path7) !== null;
64320
+ }
64321
+ async function maybeLstat2(path7) {
64322
+ try {
64323
+ return await lstat6(path7);
64324
+ } catch (error) {
64325
+ if (nodeErrorCode3(error) === "ENOENT")
64326
+ return null;
64327
+ throw error;
64328
+ }
64329
+ }
64330
+ function nodeErrorCode3(error) {
64331
+ if (!(error instanceof Error) || !("code" in error))
64332
+ return null;
64333
+ return typeof error.code === "string" ? error.code : null;
64334
+ }
64335
+ function isRecord15(value) {
64336
+ return typeof value === "object" && value !== null && !Array.isArray(value);
64337
+ }
63055
64338
  // src/cli/star-request.ts
64339
+ import { execFile as execFile2 } from "child_process";
64340
+ import { promisify as promisify2 } from "util";
63056
64341
  var STAR_REPOSITORIES = [
63057
64342
  "code-yeongyu/oh-my-openagent",
63058
64343
  "code-yeongyu/lazycodex"
63059
64344
  ];
63060
- function formatGitHubStarCommand(repository) {
63061
- return `gh api --silent --method PUT /user/starred/${repository} >/dev/null 2>&1 || true`;
64345
+ var PLATFORM_REPOSITORIES = {
64346
+ opencode: ["code-yeongyu/oh-my-openagent"],
64347
+ codex: STAR_REPOSITORIES,
64348
+ both: STAR_REPOSITORIES
64349
+ };
64350
+ var execFileAsync = promisify2(execFile2);
64351
+ async function runGitHubStarCommand(repository) {
64352
+ await execFileAsync("gh", ["api", "--silent", "--method", "PUT", `/user/starred/${repository}`]);
64353
+ }
64354
+ async function starGitHubRepositories(platform = "both", runCommand = runGitHubStarCommand) {
64355
+ const results = [];
64356
+ for (const repository of PLATFORM_REPOSITORIES[platform]) {
64357
+ try {
64358
+ await runCommand(repository);
64359
+ results.push({ repository, ok: true });
64360
+ } catch (error) {
64361
+ results.push({ repository, ok: false, error: error instanceof Error ? error.message : String(error) });
64362
+ }
64363
+ }
64364
+ return results;
63062
64365
  }
63063
64366
 
63064
64367
  // src/cli/cli-installer.ts
@@ -63133,10 +64436,10 @@ async function runCliInstaller(args, version2) {
63133
64436
  printSuccess(`Config written ${SYMBOLS.arrow} ${import_picocolors3.default.dim(omoResult.configPath)}`);
63134
64437
  }
63135
64438
  printBox(formatConfigSummary(config), isUpdate ? "Updated Configuration" : "Installation Complete");
63136
- if (!config.hasClaude) {
64439
+ if (config.hasOpenCode && !config.hasClaude) {
63137
64440
  printInfo("Note: Sisyphus agent performs best with Claude Opus 4.5+. " + "Other models work but may have reduced orchestration quality.");
63138
64441
  }
63139
- if (!config.hasClaude && !config.hasOpenAI && !config.hasGemini && !config.hasCopilot && !config.hasOpencodeZen && !config.hasVercelAiGateway) {
64442
+ if (config.hasOpenCode && !config.hasClaude && !config.hasOpenAI && !config.hasGemini && !config.hasCopilot && !config.hasOpencodeZen && !config.hasVercelAiGateway) {
63140
64443
  printWarning("No model providers configured. Using opencode/big-pickle as fallback.");
63141
64444
  }
63142
64445
  console.log(`${SYMBOLS.star} ${import_picocolors3.default.bold(import_picocolors3.default.green(isUpdate ? "Configuration updated!" : "Installation complete!"))}`);
@@ -63165,11 +64468,9 @@ async function runCliInstaller(args, version2) {
63165
64468
  printBox(`${import_picocolors3.default.bold("Pro Tip:")} Include ${import_picocolors3.default.cyan("ultrawork")} (or ${import_picocolors3.default.cyan("ulw")}) in your prompt.
63166
64469
  ` + `All features work like magic-parallel agents, background tasks,
63167
64470
  ` + `deep exploration, and relentless execution until completion.`, "The Magic Word");
63168
- console.log(`${SYMBOLS.star} ${import_picocolors3.default.yellow("If you found this helpful, consider starring the repo!")}`);
63169
- for (const repository of STAR_REPOSITORIES) {
63170
- console.log(` ${import_picocolors3.default.dim(formatGitHubStarCommand(repository))}`);
64471
+ if (args.tui) {
64472
+ await maybePromptForGitHubStars(config.platform);
63171
64473
  }
63172
- console.log();
63173
64474
  console.log(import_picocolors3.default.dim("oMoMoMoMo... Enjoy!"));
63174
64475
  console.log();
63175
64476
  if (hasOpenCode && (config.hasClaude || config.hasGemini || config.hasCopilot) && !args.skipAuth) {
@@ -63180,6 +64481,34 @@ async function runCliInstaller(args, version2) {
63180
64481
  }
63181
64482
  return 0;
63182
64483
  }
64484
+ async function maybePromptForGitHubStars(platform) {
64485
+ if (!process.stdin.isTTY || !process.stdout.isTTY)
64486
+ return;
64487
+ const readline = createInterface2({ input: process.stdin, output: process.stdout });
64488
+ try {
64489
+ const answer = await readline.question(`${SYMBOLS.star} ${import_picocolors3.default.yellow("Star the repos on GitHub?")} ${import_picocolors3.default.dim("[y/N]")} `);
64490
+ if (!isYes(answer))
64491
+ return;
64492
+ } finally {
64493
+ readline.close();
64494
+ }
64495
+ const results = await starGitHubRepositories(platform);
64496
+ const failed = results.filter((result) => !result.ok);
64497
+ if (failed.length === 0) {
64498
+ printSuccess("Starred GitHub repositories");
64499
+ console.log();
64500
+ return;
64501
+ }
64502
+ printWarning("Could not star every repository. Make sure GitHub CLI is installed and authenticated.");
64503
+ for (const result of failed) {
64504
+ console.log(` ${SYMBOLS.bullet} ${result.repository}`);
64505
+ }
64506
+ console.log();
64507
+ }
64508
+ function isYes(value) {
64509
+ const normalized = value.trim().toLowerCase();
64510
+ return normalized === "y" || normalized === "yes";
64511
+ }
63183
64512
 
63184
64513
  // node_modules/.bun/@clack+core@1.3.1/node_modules/@clack/core/dist/index.mjs
63185
64514
  import { styleText as v } from "util";
@@ -63828,6 +65157,24 @@ var Q = class extends m {
63828
65157
  }
63829
65158
  }
63830
65159
  };
65160
+
65161
+ class X extends m {
65162
+ get cursor() {
65163
+ return this.value ? 0 : 1;
65164
+ }
65165
+ get _value() {
65166
+ return this.cursor === 0;
65167
+ }
65168
+ constructor(t) {
65169
+ super(t, false), this.value = !!t.initialValue, this.on("userInput", () => {
65170
+ this.value = this._value;
65171
+ }), this.on("confirm", (s) => {
65172
+ this.output.write(import_sisteransi.cursor.move(0, -1)), this.value = s, this.state = "submit", this.close();
65173
+ }), this.on("cursor", () => {
65174
+ this.value = !this.value;
65175
+ });
65176
+ }
65177
+ }
63831
65178
  var Z = { Y: { type: "year", len: 4 }, M: { type: "month", len: 2 }, D: { type: "day", len: 2 } };
63832
65179
  function P(r) {
63833
65180
  return [...r].map((t) => Z[t]);
@@ -64265,6 +65612,33 @@ var F2 = ({ cursor: t, options: i2, style: s, output: r = process.stdout, maxIte
64265
65612
  S2.push(G2);
64266
65613
  return h2 && S2.push(l), S2;
64267
65614
  };
65615
+ var ue = (t) => {
65616
+ const i2 = t.active ?? "Yes", s = t.inactive ?? "No";
65617
+ return new X({ active: i2, inactive: s, signal: t.signal, input: t.input, output: t.output, initialValue: t.initialValue ?? true, render() {
65618
+ const r = t.withGuide ?? h.withGuide, u = `${P2(this.state)} `, n = r ? `${e("gray", $2)} ` : "", a = W(t.output, t.message, n, u), c = `${r ? `${e("gray", $2)}
65619
+ ` : ""}${a}
65620
+ `, o = this.value ? i2 : s;
65621
+ switch (this.state) {
65622
+ case "submit": {
65623
+ const l = r ? `${e("gray", $2)} ` : "";
65624
+ return `${c}${l}${e("dim", o)}`;
65625
+ }
65626
+ case "cancel": {
65627
+ const l = r ? `${e("gray", $2)} ` : "";
65628
+ return `${c}${l}${e(["strikethrough", "dim"], o)}${r ? `
65629
+ ${e("gray", $2)}` : ""}`;
65630
+ }
65631
+ default: {
65632
+ const l = r ? `${e("cyan", $2)} ` : "", d = r ? e("cyan", x2) : "";
65633
+ return `${c}${l}${this.value ? `${e("green", z2)} ${i2}` : `${e("dim", U2)} ${e("dim", i2)}`}${t.vertical ? r ? `
65634
+ ${e("cyan", $2)} ` : `
65635
+ ` : ` ${e("dim", "/")} `}${this.value ? `${e("dim", U2)} ${e("dim", s)}` : `${e("green", z2)} ${s}`}
65636
+ ${d}
65637
+ `;
65638
+ }
65639
+ }
65640
+ } }).prompt();
65641
+ };
64268
65642
  var R2 = { message: (t = [], { symbol: i2 = e("gray", $2), secondarySymbol: s = e("gray", $2), output: r = process.stdout, spacing: u = 1, withGuide: n } = {}) => {
64269
65643
  const a = [], c = n ?? h.withGuide, o = c ? s : "", l = c ? `${i2} ` : "", d = c ? `${s} ` : "";
64270
65644
  for (let p2 = 0;p2 < u; p2++)
@@ -64606,14 +65980,7 @@ async function resolveCodexAutonomous(hasCodex, override) {
64606
65980
  return false;
64607
65981
  if (override !== undefined)
64608
65982
  return override;
64609
- return selectOrCancel({
64610
- message: "Configure Codex for autonomous full-permissions mode?",
64611
- options: [
64612
- { value: true, label: "Yes", hint: "Recommended: approval never, danger-full-access, network enabled" },
64613
- { value: false, label: "No", hint: "Leave existing Codex permissions unchanged" }
64614
- ],
64615
- initialValue: true
64616
- });
65983
+ return true;
64617
65984
  }
64618
65985
 
64619
65986
  // src/cli/tui-installer.ts
@@ -64687,15 +66054,19 @@ async function runTuiInstaller(args, version2) {
64687
66054
  }
64688
66055
  spinner.stop(`Config written to ${import_picocolors4.default.cyan(omoResult.configPath)}`);
64689
66056
  }
64690
- if (!config.hasClaude) {
66057
+ if (config.hasOpenCode && !config.hasClaude) {
64691
66058
  R2.info(`${import_picocolors4.default.bold("Note:")} Sisyphus agent performs best with Claude Opus 4.5+.
64692
66059
  ` + `Other models work but may have reduced orchestration quality.`);
64693
66060
  }
64694
- if (!config.hasClaude && !config.hasOpenAI && !config.hasGemini && !config.hasCopilot && !config.hasOpencodeZen && !config.hasVercelAiGateway) {
66061
+ if (config.hasOpenCode && !config.hasClaude && !config.hasOpenAI && !config.hasGemini && !config.hasCopilot && !config.hasOpencodeZen && !config.hasVercelAiGateway) {
64695
66062
  R2.warn("No model providers configured. Using opencode/big-pickle as fallback.");
64696
66063
  }
64697
66064
  Se(formatConfigSummary(config), isUpdate ? "Updated Configuration" : "Installation Complete");
64698
66065
  if (config.hasCodex) {
66066
+ const codexInstallation = await detectCodexInstallation();
66067
+ if (!codexInstallation.found) {
66068
+ R2.warn(formatCodexInstallationWarning(codexInstallation));
66069
+ }
64699
66070
  spinner.start("Installing Codex harness adapter");
64700
66071
  try {
64701
66072
  const codexResult = await runCodexInstaller({ autonomousPermissions: config.codexAutonomous });
@@ -64720,9 +66091,20 @@ async function runTuiInstaller(args, version2) {
64720
66091
  Se(`Include ${import_picocolors4.default.cyan("ultrawork")} (or ${import_picocolors4.default.cyan("ulw")}) in your prompt.
64721
66092
  ` + `All features work like magic-parallel agents, background tasks,
64722
66093
  ` + `deep exploration, and relentless execution until completion.`, "The Magic Word");
64723
- R2.message(`${import_picocolors4.default.yellow("\u2605")} If you found this helpful, consider starring the repo!`);
64724
- for (const repository of STAR_REPOSITORIES) {
64725
- R2.message(` ${import_picocolors4.default.dim(formatGitHubStarCommand(repository))}`);
66094
+ const shouldStar = await ue({
66095
+ message: "Star the repos on GitHub?",
66096
+ initialValue: false
66097
+ });
66098
+ if (!q(shouldStar) && shouldStar) {
66099
+ spinner.start("Starring GitHub repositories");
66100
+ const results = await starGitHubRepositories(selectedPlatform);
66101
+ const failed = results.filter((result) => !result.ok);
66102
+ if (failed.length === 0) {
66103
+ spinner.stop("GitHub repositories starred");
66104
+ } else {
66105
+ spinner.stop("Could not star every repository");
66106
+ R2.warn("Make sure GitHub CLI is installed and authenticated.");
66107
+ }
64726
66108
  }
64727
66109
  ye(import_picocolors4.default.green("oMoMoMoMo... Enjoy!"));
64728
66110
  if (config.hasOpenCode && (config.hasClaude || config.hasGemini || config.hasCopilot) && !args.skipAuth) {
@@ -64751,6 +66133,71 @@ async function install(args) {
64751
66133
  return args.tui ? runTuiInstaller(args, VERSION) : runCliInstaller(args, VERSION);
64752
66134
  }
64753
66135
 
66136
+ // src/cli/cleanup.ts
66137
+ function resolveCleanupPlatform(options, invocationName = process.env.OMO_INVOCATION_NAME) {
66138
+ if (options.platform !== undefined)
66139
+ return options.platform;
66140
+ return invocationName === "lazycodex" || invocationName === "lazycodex-ai" ? "codex" : undefined;
66141
+ }
66142
+ async function cleanup(options) {
66143
+ if (options.platform !== "codex") {
66144
+ console.error("Error: cleanup currently supports only --platform=codex");
66145
+ return 1;
66146
+ }
66147
+ const result = await cleanupCodexLight({
66148
+ codexHome: options.codexHome,
66149
+ projectDirectory: options.project
66150
+ });
66151
+ if (options.json === true) {
66152
+ console.log(JSON.stringify(result, null, 2));
66153
+ return 0;
66154
+ }
66155
+ console.log(`Codex Light cleanup complete: ${result.codexHome}`);
66156
+ if (result.configChanged) {
66157
+ console.log(`- Updated ${result.configPath}`);
66158
+ if (result.configBackupPath !== undefined)
66159
+ console.log(`- Backup ${result.configBackupPath}`);
66160
+ } else {
66161
+ console.log(`- No managed Codex config blocks found in ${result.configPath}`);
66162
+ }
66163
+ for (const path7 of result.removedPaths) {
66164
+ console.log(`- Removed ${path7}`);
66165
+ }
66166
+ for (const path7 of result.removedAgentLinks) {
66167
+ console.log(`- Removed managed agent link ${path7}`);
66168
+ }
66169
+ for (const path7 of result.skippedAgentLinks) {
66170
+ console.log(`- Skipped agent path outside managed scope ${path7}`);
66171
+ }
66172
+ if (result.projectCleanup.changed) {
66173
+ console.log(`- Repaired project-local Codex config ${result.projectCleanup.configPath}`);
66174
+ }
66175
+ for (const artifact of result.projectCleanup.artifacts) {
66176
+ console.log(`- Left project-local artifact in place ${artifact.path}`);
66177
+ }
66178
+ return 0;
66179
+ }
66180
+
66181
+ // src/cli/cleanup-command.ts
66182
+ function configureCleanupCommand(program2) {
66183
+ program2.command("cleanup").description("Clean managed Codex Light state and repair project-local legacy Codex artifacts").addOption(new Option("--platform <platform>", "Cleanup target platform: codex").choices(["codex"])).option("--codex-home <path>", "Codex home to clean (defaults to CODEX_HOME or ~/.codex)").option("--project <path>", "Project directory to inspect for project-local .codex/.omx artifacts").option("--json", "Output structured JSON result").addHelpText("after", `
66184
+ Examples:
66185
+ $ npx lazycodex-ai cleanup
66186
+ $ omo cleanup --platform=codex
66187
+ $ omo cleanup --platform=codex --project /path/to/project
66188
+ `).action(async (options) => {
66189
+ const rootOptions = program2.opts();
66190
+ const platform = resolveCleanupPlatform({ platform: options.platform ?? rootOptions.platform });
66191
+ const exitCode = await cleanup({
66192
+ platform,
66193
+ codexHome: options.codexHome,
66194
+ project: options.project,
66195
+ json: options.json ?? false
66196
+ });
66197
+ process.exit(exitCode);
66198
+ });
66199
+ }
66200
+
64754
66201
  // src/cli/run/runner.ts
64755
66202
  var import_picocolors14 = __toESM(require_picocolors(), 1);
64756
66203
 
@@ -64758,6 +66205,7 @@ var import_picocolors14 = __toESM(require_picocolors(), 1);
64758
66205
  function createEventState() {
64759
66206
  return {
64760
66207
  mainSessionIdle: false,
66208
+ mainSessionStarted: false,
64761
66209
  mainSessionError: false,
64762
66210
  lastError: null,
64763
66211
  lastOutput: "",
@@ -65173,10 +66621,12 @@ function handleSessionStatus(ctx, payload, state) {
65173
66621
  return;
65174
66622
  if (props?.status?.type === "busy") {
65175
66623
  state.mainSessionIdle = false;
66624
+ state.mainSessionStarted = true;
65176
66625
  } else if (props?.status?.type === "idle") {
65177
66626
  state.mainSessionIdle = true;
65178
66627
  } else if (props?.status?.type === "retry") {
65179
66628
  state.mainSessionIdle = false;
66629
+ state.mainSessionStarted = true;
65180
66630
  }
65181
66631
  }
65182
66632
  function handleSessionError(ctx, payload, state) {
@@ -65216,6 +66666,7 @@ function handleMessagePartUpdated(ctx, payload, state) {
65216
66666
  const padded = writePaddedText(newText, state.thinkingAtLineStart);
65217
66667
  process.stdout.write(import_picocolors7.default.dim(padded.output));
65218
66668
  state.thinkingAtLineStart = padded.atLineStart;
66669
+ state.mainSessionStarted = true;
65219
66670
  state.hasReceivedMeaningfulWork = true;
65220
66671
  }
65221
66672
  state.lastReasoningText = reasoningText;
@@ -65228,6 +66679,7 @@ function handleMessagePartUpdated(ctx, payload, state) {
65228
66679
  const padded = writePaddedText(newText, state.textAtLineStart);
65229
66680
  process.stdout.write(padded.output);
65230
66681
  state.textAtLineStart = padded.atLineStart;
66682
+ state.mainSessionStarted = true;
65231
66683
  state.hasReceivedMeaningfulWork = true;
65232
66684
  }
65233
66685
  state.lastPartText = part.text;
@@ -65239,6 +66691,7 @@ function handleMessagePartUpdated(ctx, payload, state) {
65239
66691
  }
65240
66692
  }
65241
66693
  if (part.type === "tool") {
66694
+ state.mainSessionStarted = true;
65242
66695
  handleToolPart(ctx, part, state);
65243
66696
  }
65244
66697
  }
@@ -65264,6 +66717,7 @@ function handleMessagePartDelta(ctx, payload, state) {
65264
66717
  process.stdout.write(import_picocolors7.default.dim(padded2.output));
65265
66718
  state.thinkingAtLineStart = padded2.atLineStart;
65266
66719
  state.lastReasoningText += delta;
66720
+ state.mainSessionStarted = true;
65267
66721
  state.hasReceivedMeaningfulWork = true;
65268
66722
  return;
65269
66723
  }
@@ -65272,6 +66726,7 @@ function handleMessagePartDelta(ctx, payload, state) {
65272
66726
  process.stdout.write(padded.output);
65273
66727
  state.textAtLineStart = padded.atLineStart;
65274
66728
  state.lastPartText += delta;
66729
+ state.mainSessionStarted = true;
65275
66730
  state.hasReceivedMeaningfulWork = true;
65276
66731
  }
65277
66732
  function handleToolPart(_ctx, part, state) {
@@ -65317,11 +66772,15 @@ function handleMessageUpdated(ctx, payload, state) {
65317
66772
  if (messageID && role) {
65318
66773
  state.messageRoleById[messageID] = role;
65319
66774
  }
66775
+ if (messageID) {
66776
+ state.mainSessionStarted = true;
66777
+ }
65320
66778
  if (props?.info?.role !== "assistant")
65321
66779
  return;
65322
66780
  const isNewMessage = !messageID || messageID !== state.currentMessageId;
65323
66781
  if (isNewMessage) {
65324
66782
  state.currentMessageId = messageID;
66783
+ state.mainSessionStarted = true;
65325
66784
  state.hasReceivedMeaningfulWork = true;
65326
66785
  state.messageCount++;
65327
66786
  state.lastPartText = "";
@@ -65357,6 +66816,7 @@ function handleToolExecute(ctx, payload, state) {
65357
66816
  return;
65358
66817
  const toolName = props?.name || "unknown";
65359
66818
  state.currentTool = toolName;
66819
+ state.mainSessionStarted = true;
65360
66820
  const header = formatToolHeader(toolName, props?.input ?? {});
65361
66821
  const suffix = header.description ? ` ${import_picocolors7.default.dim(header.description)}` : "";
65362
66822
  state.hasReceivedMeaningfulWork = true;
@@ -65451,7 +66911,7 @@ async function processEvents(ctx, stream, state) {
65451
66911
  }
65452
66912
  // src/plugin-config.ts
65453
66913
  import * as fs4 from "fs";
65454
- import { homedir as homedir6 } from "os";
66914
+ import { homedir as homedir8 } from "os";
65455
66915
  import * as path7 from "path";
65456
66916
 
65457
66917
  // node_modules/.bun/zod@4.4.3/node_modules/zod/v4/classic/external.js
@@ -67065,8 +68525,8 @@ function emoji() {
67065
68525
  }
67066
68526
  var ipv4 = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/;
67067
68527
  var ipv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/;
67068
- var mac = (delimiter) => {
67069
- const escapedDelim = escapeRegex(delimiter ?? ":");
68528
+ var mac = (delimiter2) => {
68529
+ const escapedDelim = escapeRegex(delimiter2 ?? ":");
67070
68530
  return new RegExp(`^(?:[0-9A-F]{2}${escapedDelim}){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}${escapedDelim}){5}[0-9a-f]{2}$`);
67071
68531
  };
67072
68532
  var cidrv4 = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$/;
@@ -71813,9 +73273,9 @@ var error17 = () => {
71813
73273
  if (issue2.values.length === 2) {
71814
73274
  return `\u05E2\u05E8\u05DA \u05DC\u05D0 \u05EA\u05E7\u05D9\u05DF: \u05D4\u05D0\u05E4\u05E9\u05E8\u05D5\u05D9\u05D5\u05EA \u05D4\u05DE\u05EA\u05D0\u05D9\u05DE\u05D5\u05EA \u05D4\u05DF ${stringified[0]} \u05D0\u05D5 ${stringified[1]}`;
71815
73275
  }
71816
- const lastValue = stringified[stringified.length - 1];
73276
+ const lastValue2 = stringified[stringified.length - 1];
71817
73277
  const restValues = stringified.slice(0, -1).join(", ");
71818
- return `\u05E2\u05E8\u05DA \u05DC\u05D0 \u05EA\u05E7\u05D9\u05DF: \u05D4\u05D0\u05E4\u05E9\u05E8\u05D5\u05D9\u05D5\u05EA \u05D4\u05DE\u05EA\u05D0\u05D9\u05DE\u05D5\u05EA \u05D4\u05DF ${restValues} \u05D0\u05D5 ${lastValue}`;
73278
+ return `\u05E2\u05E8\u05DA \u05DC\u05D0 \u05EA\u05E7\u05D9\u05DF: \u05D4\u05D0\u05E4\u05E9\u05E8\u05D5\u05D9\u05D5\u05EA \u05D4\u05DE\u05EA\u05D0\u05D9\u05DE\u05D5\u05EA \u05D4\u05DF ${restValues} \u05D0\u05D5 ${lastValue2}`;
71819
73279
  }
71820
73280
  case "too_big": {
71821
73281
  const sizing = getSizing(issue2.origin);
@@ -80025,7 +81485,6 @@ var GitMasterConfigSchema = exports_external.object({
80025
81485
  // src/config/schema/hooks.ts
80026
81486
  var HookNameSchema = exports_external.enum([
80027
81487
  "todo-continuation-enforcer",
80028
- "context-window-monitor",
80029
81488
  "session-recovery",
80030
81489
  "session-notification",
80031
81490
  "comment-checker",
@@ -80480,7 +81939,7 @@ function addAgentOrderWarnings(configPath, agentOrder) {
80480
81939
  });
80481
81940
  }
80482
81941
  function resolveHomeDirectory() {
80483
- return process.env.HOME ?? process.env.USERPROFILE ?? homedir6();
81942
+ return process.env.HOME ?? process.env.USERPROFILE ?? homedir8();
80484
81943
  }
80485
81944
  function resolveConfigPathAfterLegacyMigration(detectedPath) {
80486
81945
  if (!path7.basename(detectedPath).startsWith(LEGACY_CONFIG_BASENAME)) {
@@ -80776,7 +82235,7 @@ function loadPluginConfig(directory, ctx) {
80776
82235
  // node_modules/.bun/@opencode-ai+sdk@1.15.10/node_modules/@opencode-ai/sdk/dist/gen/core/serverSentEvents.gen.js
80777
82236
  var createSseClient = ({ onSseError, onSseEvent, responseTransformer, responseValidator, sseDefaultRetryDelay, sseMaxRetryAttempts, sseMaxRetryDelay, sseSleepFn, url: url2, ...options }) => {
80778
82237
  let lastEventId;
80779
- const sleep = sseSleepFn ?? ((ms) => new Promise((resolve9) => setTimeout(resolve9, ms)));
82238
+ const sleep = sseSleepFn ?? ((ms) => new Promise((resolve11) => setTimeout(resolve11, ms)));
80780
82239
  const createStream = async function* () {
80781
82240
  let retryDelay = sseDefaultRetryDelay ?? 3000;
80782
82241
  let attempt = 0;
@@ -82239,7 +83698,7 @@ async function createOpencodeServer(options) {
82239
83698
  }
82240
83699
  });
82241
83700
  let clear = () => {};
82242
- const url2 = await new Promise((resolve9, reject) => {
83701
+ const url2 = await new Promise((resolve11, reject) => {
82243
83702
  const id = setTimeout(() => {
82244
83703
  clear();
82245
83704
  stop(proc);
@@ -82265,7 +83724,7 @@ async function createOpencodeServer(options) {
82265
83724
  }
82266
83725
  clearTimeout(id);
82267
83726
  resolved = true;
82268
- resolve9(match[1]);
83727
+ resolve11(match[1]);
82269
83728
  return;
82270
83729
  }
82271
83730
  }
@@ -82320,7 +83779,7 @@ var import_picocolors9 = __toESM(require_picocolors(), 1);
82320
83779
 
82321
83780
  // src/cli/run/opencode-binary-resolver.ts
82322
83781
  init_spawn_with_windows_hide();
82323
- import { delimiter, dirname as dirname15, join as join30 } from "path";
83782
+ import { delimiter as delimiter2, dirname as dirname17, join as join36 } from "path";
82324
83783
  var OPENCODE_COMMANDS = ["opencode", "opencode-desktop"];
82325
83784
  var WINDOWS_SUFFIXES = ["", ".exe", ".cmd", ".bat", ".ps1"];
82326
83785
  function getCommandCandidates(platform) {
@@ -82341,9 +83800,9 @@ function collectCandidateBinaryPaths(pathEnv, which = Bun.which, platform = proc
82341
83800
  for (const command of commandCandidates) {
82342
83801
  addCandidate(which(command));
82343
83802
  }
82344
- for (const entry of (pathEnv ?? "").split(delimiter).filter(Boolean)) {
83803
+ for (const entry of (pathEnv ?? "").split(delimiter2).filter(Boolean)) {
82345
83804
  for (const command of commandCandidates) {
82346
- addCandidate(join30(entry, command));
83805
+ addCandidate(join36(entry, command));
82347
83806
  }
82348
83807
  }
82349
83808
  return candidates;
@@ -82370,9 +83829,9 @@ async function findWorkingOpencodeBinary(pathEnv = process.env.PATH, probe = can
82370
83829
  return null;
82371
83830
  }
82372
83831
  function buildPathWithBinaryFirst(pathEnv, binaryPath) {
82373
- const preferredDir = dirname15(binaryPath);
82374
- const existing = (pathEnv ?? "").split(delimiter).filter((entry) => entry.length > 0 && entry !== preferredDir);
82375
- return [preferredDir, ...existing].join(delimiter);
83832
+ const preferredDir = dirname17(binaryPath);
83833
+ const existing = (pathEnv ?? "").split(delimiter2).filter((entry) => entry.length > 0 && entry !== preferredDir);
83834
+ return [preferredDir, ...existing].join(delimiter2);
82376
83835
  }
82377
83836
  async function withWorkingOpencodePath(startServer, finder = findWorkingOpencodeBinary) {
82378
83837
  const originalPath = process.env.PATH;
@@ -82421,6 +83880,7 @@ function isPortRangeExhausted(error51) {
82421
83880
  async function startServer(options, deps) {
82422
83881
  const { signal, port } = options;
82423
83882
  const { client: client3, server: server2 } = await deps.withWorkingOpencodePath(() => deps.createOpencode({ signal, port, hostname: "127.0.0.1" }));
83883
+ deps.injectServerAuthIntoClient(client3);
82424
83884
  console.log(import_picocolors9.default.dim("Server listening at"), import_picocolors9.default.cyan(server2.url));
82425
83885
  return { client: client3, cleanup: () => server2.close() };
82426
83886
  }
@@ -82534,7 +83994,7 @@ async function resolveSession(options) {
82534
83994
  if (attempt < SESSION_CREATE_MAX_RETRIES) {
82535
83995
  const delay = SESSION_CREATE_RETRY_DELAY_MS * attempt;
82536
83996
  console.log(import_picocolors10.default.dim(` Retrying in ${delay}ms...`));
82537
- await new Promise((resolve9) => setTimeout(resolve9, delay));
83997
+ await new Promise((resolve11) => setTimeout(resolve11, delay));
82538
83998
  }
82539
83999
  continue;
82540
84000
  }
@@ -82545,7 +84005,7 @@ async function resolveSession(options) {
82545
84005
  if (attempt < SESSION_CREATE_MAX_RETRIES) {
82546
84006
  const delay = SESSION_CREATE_RETRY_DELAY_MS * attempt;
82547
84007
  console.log(import_picocolors10.default.dim(` Retrying in ${delay}ms...`));
82548
- await new Promise((resolve9) => setTimeout(resolve9, delay));
84008
+ await new Promise((resolve11) => setTimeout(resolve11, delay));
82549
84009
  }
82550
84010
  }
82551
84011
  throw new Error("Failed to create session after all retries");
@@ -82674,11 +84134,10 @@ var normalizeAgentName = (agent) => {
82674
84134
  return;
82675
84135
  const configKey = getAgentConfigKey(trimmed);
82676
84136
  const displayName = getAgentDisplayName(configKey);
82677
- const runtimeName = getAgentDisplayName(configKey);
82678
84137
  const isKnownAgent = displayName !== configKey;
82679
84138
  return {
82680
84139
  configKey,
82681
- resolvedName: isKnownAgent ? runtimeName : trimmed
84140
+ resolvedName: isKnownAgent ? configKey : trimmed
82682
84141
  };
82683
84142
  };
82684
84143
  var isAgentDisabled = (agentConfigKey, config2) => {
@@ -82702,19 +84161,18 @@ var resolveRunAgent = (options, pluginConfig, env = process.env) => {
82702
84161
  const configAgent = normalizeAgentName(pluginConfig.default_run_agent);
82703
84162
  const resolved = cliAgent ?? envAgent ?? configAgent ?? {
82704
84163
  configKey: DEFAULT_AGENT,
82705
- resolvedName: getAgentDisplayName(DEFAULT_AGENT)
84164
+ resolvedName: DEFAULT_AGENT
82706
84165
  };
82707
84166
  if (isAgentDisabled(resolved.configKey, pluginConfig)) {
82708
84167
  const fallback = pickFallbackAgent(pluginConfig);
82709
84168
  const fallbackDisplayName = getAgentDisplayName(fallback);
82710
- const fallbackRuntimeName = getAgentDisplayName(fallback);
82711
84169
  const fallbackDisabled = isAgentDisabled(fallback, pluginConfig);
82712
84170
  if (fallbackDisabled) {
82713
84171
  console.log(import_picocolors11.default.yellow(`Requested agent "${resolved.resolvedName}" is disabled and no enabled core agent was found. Proceeding with "${fallbackDisplayName}".`));
82714
- return fallbackRuntimeName;
84172
+ return fallback;
82715
84173
  }
82716
84174
  console.log(import_picocolors11.default.yellow(`Requested agent "${resolved.resolvedName}" is disabled. Falling back to "${fallbackDisplayName}".`));
82717
- return fallbackRuntimeName;
84175
+ return fallback;
82718
84176
  }
82719
84177
  return resolved.resolvedName;
82720
84178
  };
@@ -82756,7 +84214,7 @@ var BOULDER_STATE_PATH = `${BOULDER_DIR}/${BOULDER_FILE}`;
82756
84214
  var NOTEPAD_DIR = "notepads";
82757
84215
  var NOTEPAD_BASE_PATH = `${BOULDER_DIR}/${NOTEPAD_DIR}`;
82758
84216
  // packages/boulder-state/src/top-level-task.ts
82759
- import { existsSync as existsSync25, readFileSync as readFileSync15 } from "fs";
84217
+ import { existsSync as existsSync28, readFileSync as readFileSync15 } from "fs";
82760
84218
  var TODO_HEADING_PATTERN = /^##\s+TODOs\b/i;
82761
84219
  var FINAL_VERIFICATION_HEADING_PATTERN = /^##\s+Final Verification Wave\b/i;
82762
84220
  var SECOND_LEVEL_HEADING_PATTERN = /^##\s+/;
@@ -82779,7 +84237,7 @@ function buildTaskRef(section, taskLabel) {
82779
84237
  };
82780
84238
  }
82781
84239
  function readCurrentTopLevelTask(planPath) {
82782
- if (!existsSync25(planPath)) {
84240
+ if (!existsSync28(planPath)) {
82783
84241
  return null;
82784
84242
  }
82785
84243
  try {
@@ -82808,13 +84266,13 @@ function readCurrentTopLevelTask(planPath) {
82808
84266
  }
82809
84267
  }
82810
84268
  // packages/boulder-state/src/storage/path.ts
82811
- import { existsSync as existsSync26 } from "fs";
82812
- import { isAbsolute as isAbsolute6, join as join31, relative as relative5, resolve as resolve9 } from "path";
84269
+ import { existsSync as existsSync29 } from "fs";
84270
+ import { isAbsolute as isAbsolute7, join as join37, relative as relative6, resolve as resolve11 } from "path";
82813
84271
  function getBoulderFilePath(directory) {
82814
- return join31(directory, BOULDER_DIR, BOULDER_FILE);
84272
+ return join37(directory, BOULDER_DIR, BOULDER_FILE);
82815
84273
  }
82816
84274
  function resolveTrackedPath(baseDirectory, trackedPath) {
82817
- return isAbsolute6(trackedPath) ? resolve9(trackedPath) : resolve9(baseDirectory, trackedPath);
84275
+ return isAbsolute7(trackedPath) ? resolve11(trackedPath) : resolve11(baseDirectory, trackedPath);
82818
84276
  }
82819
84277
  function resolveBoulderPlanPath(directory, state) {
82820
84278
  const absolutePlanPath = resolveTrackedPath(directory, state.active_plan);
@@ -82822,20 +84280,20 @@ function resolveBoulderPlanPath(directory, state) {
82822
84280
  if (!worktreePath) {
82823
84281
  return absolutePlanPath;
82824
84282
  }
82825
- const absoluteDirectory = resolve9(directory);
82826
- const relativePlanPath = relative5(absoluteDirectory, absolutePlanPath);
82827
- if (relativePlanPath.length === 0 || relativePlanPath.startsWith("..") || isAbsolute6(relativePlanPath)) {
84283
+ const absoluteDirectory = resolve11(directory);
84284
+ const relativePlanPath = relative6(absoluteDirectory, absolutePlanPath);
84285
+ if (relativePlanPath.length === 0 || relativePlanPath.startsWith("..") || isAbsolute7(relativePlanPath)) {
82828
84286
  return absolutePlanPath;
82829
84287
  }
82830
84288
  const absoluteWorktreePath = resolveTrackedPath(directory, worktreePath);
82831
- const worktreePlanPath = resolve9(absoluteWorktreePath, relativePlanPath);
82832
- return existsSync26(worktreePlanPath) ? worktreePlanPath : absolutePlanPath;
84289
+ const worktreePlanPath = resolve11(absoluteWorktreePath, relativePlanPath);
84290
+ return existsSync29(worktreePlanPath) ? worktreePlanPath : absolutePlanPath;
82833
84291
  }
82834
84292
  function resolveBoulderPlanPathForWork(directory, work) {
82835
84293
  return resolveBoulderPlanPath(directory, work);
82836
84294
  }
82837
84295
  // packages/boulder-state/src/storage/plan-progress.ts
82838
- import { existsSync as existsSync27, readFileSync as readFileSync16, readdirSync as readdirSync4, statSync as statSync4 } from "fs";
84296
+ import { existsSync as existsSync30, readFileSync as readFileSync16, readdirSync as readdirSync4, statSync as statSync4 } from "fs";
82839
84297
  var TODO_HEADING_PATTERN2 = /^##\s+TODOs\b/i;
82840
84298
  var FINAL_VERIFICATION_HEADING_PATTERN2 = /^##\s+Final Verification Wave\b/i;
82841
84299
  var SECOND_LEVEL_HEADING_PATTERN2 = /^##\s+/;
@@ -82844,7 +84302,7 @@ var CHECKED_CHECKBOX_PATTERN = /^(\s*)[-*]\s*\[[xX]\]\s*(.+)$/;
82844
84302
  var TODO_TASK_PATTERN2 = /^\d+\.\s+/;
82845
84303
  var FINAL_WAVE_TASK_PATTERN2 = /^F\d+\.\s+/i;
82846
84304
  function getPlanProgress(planPath) {
82847
- if (!existsSync27(planPath)) {
84305
+ if (!existsSync30(planPath)) {
82848
84306
  return { total: 0, completed: 0, isComplete: false };
82849
84307
  }
82850
84308
  try {
@@ -82964,10 +84422,10 @@ function selectMirrorWork(state) {
82964
84422
  return sorted[0] ?? null;
82965
84423
  }
82966
84424
  // packages/boulder-state/src/storage/read-state.ts
82967
- import { existsSync as existsSync28, readFileSync as readFileSync17 } from "fs";
84425
+ import { existsSync as existsSync31, readFileSync as readFileSync17 } from "fs";
82968
84426
  function readBoulderState(directory) {
82969
84427
  const filePath = getBoulderFilePath(directory);
82970
- if (!existsSync28(filePath)) {
84428
+ if (!existsSync31(filePath)) {
82971
84429
  return null;
82972
84430
  }
82973
84431
  try {
@@ -83047,14 +84505,14 @@ function getSessionAgent(sessionID) {
83047
84505
  // src/features/run-continuation-state/constants.ts
83048
84506
  var CONTINUATION_MARKER_DIR = ".omo/run-continuation";
83049
84507
  // src/features/run-continuation-state/storage.ts
83050
- import { existsSync as existsSync29, mkdirSync as mkdirSync9, readFileSync as readFileSync18, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "fs";
83051
- import { join as join32 } from "path";
84508
+ import { existsSync as existsSync32, mkdirSync as mkdirSync9, readFileSync as readFileSync18, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "fs";
84509
+ import { join as join38 } from "path";
83052
84510
  function getMarkerPath(directory, sessionID) {
83053
- return join32(directory, CONTINUATION_MARKER_DIR, `${sessionID}.json`);
84511
+ return join38(directory, CONTINUATION_MARKER_DIR, `${sessionID}.json`);
83054
84512
  }
83055
84513
  function readContinuationMarker(directory, sessionID) {
83056
84514
  const markerPath = getMarkerPath(directory, sessionID);
83057
- if (!existsSync29(markerPath))
84515
+ if (!existsSync32(markerPath))
83058
84516
  return null;
83059
84517
  try {
83060
84518
  const raw = readFileSync18(markerPath, "utf-8");
@@ -83120,7 +84578,7 @@ async function isSessionInBoulderLineage(input) {
83120
84578
  init_shared();
83121
84579
  init_compaction_marker();
83122
84580
  import { readFileSync as readFileSync19, readdirSync as readdirSync5 } from "fs";
83123
- import { join as join33 } from "path";
84581
+ import { join as join39 } from "path";
83124
84582
  var defaultSessionLastAgentDeps = {
83125
84583
  getMessageDir,
83126
84584
  isSqliteBackend,
@@ -83174,7 +84632,7 @@ async function getLastAgentFromSession(sessionID, client3, deps = {}) {
83174
84632
  try {
83175
84633
  const messages = readdirSync5(messageDir).filter((fileName) => fileName.endsWith(".json")).map((fileName) => {
83176
84634
  try {
83177
- const content = readFileSync19(join33(messageDir, fileName), "utf-8");
84635
+ const content = readFileSync19(join39(messageDir, fileName), "utf-8");
83178
84636
  const parsed = JSON.parse(content);
83179
84637
  return {
83180
84638
  fileName,
@@ -83210,8 +84668,8 @@ init_agent_display_names();
83210
84668
 
83211
84669
  // src/hooks/ralph-loop/storage.ts
83212
84670
  init_frontmatter2();
83213
- import { existsSync as existsSync30, readFileSync as readFileSync20, writeFileSync as writeFileSync7, unlinkSync as unlinkSync5, mkdirSync as mkdirSync10 } from "fs";
83214
- import { dirname as dirname16, join as join34 } from "path";
84671
+ import { existsSync as existsSync33, readFileSync as readFileSync20, writeFileSync as writeFileSync7, unlinkSync as unlinkSync5, mkdirSync as mkdirSync10 } from "fs";
84672
+ import { dirname as dirname18, join as join40 } from "path";
83215
84673
 
83216
84674
  // src/hooks/ralph-loop/constants.ts
83217
84675
  var DEFAULT_STATE_FILE = ".omo/ralph-loop.local.md";
@@ -83220,11 +84678,11 @@ var DEFAULT_COMPLETION_PROMISE = "DONE";
83220
84678
 
83221
84679
  // src/hooks/ralph-loop/storage.ts
83222
84680
  function getStateFilePath(directory, customPath) {
83223
- return customPath ? join34(directory, customPath) : join34(directory, DEFAULT_STATE_FILE);
84681
+ return customPath ? join40(directory, customPath) : join40(directory, DEFAULT_STATE_FILE);
83224
84682
  }
83225
84683
  function readState(directory, customPath) {
83226
84684
  const filePath = getStateFilePath(directory, customPath);
83227
- if (!existsSync30(filePath)) {
84685
+ if (!existsSync33(filePath)) {
83228
84686
  return null;
83229
84687
  }
83230
84688
  try {
@@ -83449,13 +84907,14 @@ async function pollForCompletion(ctx, eventState, abortController, options = {})
83449
84907
  const minStabilizationMs = rawMinStabilizationMs > 0 ? rawMinStabilizationMs : MIN_STABILIZATION_MS;
83450
84908
  const eventWatchdogMs = options.eventWatchdogMs ?? DEFAULT_EVENT_WATCHDOG_MS;
83451
84909
  const secondaryMeaningfulWorkTimeoutMs = options.secondaryMeaningfulWorkTimeoutMs ?? DEFAULT_SECONDARY_MEANINGFUL_WORK_TIMEOUT_MS;
84910
+ const requireMeaningfulWork = options.requireMeaningfulWork ?? false;
83452
84911
  let consecutiveCompleteChecks = 0;
83453
84912
  let errorCycleCount = 0;
83454
84913
  let firstWorkTimestamp = null;
83455
84914
  let secondaryTimeoutChecked = false;
83456
84915
  const pollStartTimestamp = Date.now();
83457
84916
  while (!abortController.signal.aborted) {
83458
- await new Promise((resolve10) => setTimeout(resolve10, pollIntervalMs));
84917
+ await new Promise((resolve12) => setTimeout(resolve12, pollIntervalMs));
83459
84918
  if (abortController.signal.aborted) {
83460
84919
  return 130;
83461
84920
  }
@@ -83508,21 +84967,24 @@ Session ended with error: ${eventState.lastError}`));
83508
84967
  consecutiveCompleteChecks = 0;
83509
84968
  continue;
83510
84969
  }
84970
+ if (requireMeaningfulWork) {
84971
+ if (Date.now() - pollStartTimestamp <= secondaryMeaningfulWorkTimeoutMs) {
84972
+ consecutiveCompleteChecks = 0;
84973
+ continue;
84974
+ }
84975
+ const hasActiveWork = await hasActiveSessionWork(ctx);
84976
+ if (hasActiveWork) {
84977
+ consecutiveCompleteChecks = 0;
84978
+ continue;
84979
+ }
84980
+ console.error(import_picocolors13.default.red(`
84981
+
84982
+ Session never produced assistant output, tool activity, or reasoning after the prompt started.`));
84983
+ return 1;
84984
+ }
83511
84985
  if (Date.now() - pollStartTimestamp > secondaryMeaningfulWorkTimeoutMs && !secondaryTimeoutChecked) {
83512
84986
  secondaryTimeoutChecked = true;
83513
- const childrenRes = await ctx.client.session.children({
83514
- path: { id: ctx.sessionID },
83515
- query: { directory: ctx.directory }
83516
- });
83517
- const children = normalizeSDKResponse(childrenRes, []);
83518
- const todosRes = await ctx.client.session.todo({
83519
- path: { id: ctx.sessionID },
83520
- query: { directory: ctx.directory }
83521
- });
83522
- const todos = normalizeSDKResponse(todosRes, []);
83523
- const hasActiveChildren = Array.isArray(children) && children.length > 0;
83524
- const hasActiveTodos = Array.isArray(todos) && todos.some(isIncompleteTodo);
83525
- const hasActiveWork = hasActiveChildren || hasActiveTodos;
84987
+ const hasActiveWork = await hasActiveSessionWork(ctx);
83526
84988
  if (hasActiveWork) {
83527
84989
  eventState.hasReceivedMeaningfulWork = true;
83528
84990
  console.log(import_picocolors13.default.yellow(`
@@ -83556,6 +85018,21 @@ All tasks completed.`));
83556
85018
  }
83557
85019
  return 130;
83558
85020
  }
85021
+ async function hasActiveSessionWork(ctx) {
85022
+ const childrenRes = await ctx.client.session.children({
85023
+ path: { id: ctx.sessionID },
85024
+ query: { directory: ctx.directory }
85025
+ });
85026
+ const children = normalizeSDKResponse(childrenRes, []);
85027
+ const todosRes = await ctx.client.session.todo({
85028
+ path: { id: ctx.sessionID },
85029
+ query: { directory: ctx.directory }
85030
+ });
85031
+ const todos = normalizeSDKResponse(todosRes, []);
85032
+ const hasActiveChildren = Array.isArray(children) && children.length > 0;
85033
+ const hasActiveTodos = Array.isArray(todos) && todos.some(isIncompleteTodo);
85034
+ return hasActiveChildren || hasActiveTodos;
85035
+ }
83559
85036
  async function getMainSessionStatus(ctx) {
83560
85037
  try {
83561
85038
  const statusesRes = await ctx.client.session.status({
@@ -83575,6 +85052,88 @@ async function getMainSessionStatus(ctx) {
83575
85052
  }
83576
85053
  }
83577
85054
 
85055
+ // src/cli/run/prompt-start.ts
85056
+ init_shared();
85057
+ var DEFAULT_PROMPT_START_TIMEOUT_MS = 30000;
85058
+ var DEFAULT_PROMPT_START_POLL_INTERVAL_MS = 100;
85059
+ function createAbortError() {
85060
+ const error51 = new Error("Prompt start wait aborted");
85061
+ error51.name = "AbortError";
85062
+ return error51;
85063
+ }
85064
+ function sleep(delayMs) {
85065
+ return new Promise((resolve12) => setTimeout(resolve12, delayMs));
85066
+ }
85067
+ function hasPromptStartEvidence(eventState) {
85068
+ return eventState.mainSessionStarted || eventState.hasReceivedMeaningfulWork || eventState.messageCount > 0 || eventState.currentTool !== null;
85069
+ }
85070
+ async function readMainSessionStatus(ctx) {
85071
+ if (typeof ctx.client.session.status !== "function") {
85072
+ return null;
85073
+ }
85074
+ try {
85075
+ const statusRes = await ctx.client.session.status({
85076
+ query: { directory: ctx.directory }
85077
+ });
85078
+ const statuses = normalizeSDKResponse(statusRes, {});
85079
+ return statuses[ctx.sessionID]?.type ?? "idle";
85080
+ } catch (error51) {
85081
+ if (ctx.verbose) {
85082
+ console.error(`[run] failed to read session status while waiting for prompt start: ${String(error51)}`);
85083
+ }
85084
+ return null;
85085
+ }
85086
+ }
85087
+ async function hasPersistedMessages(ctx) {
85088
+ if (typeof ctx.client.session.messages !== "function") {
85089
+ return false;
85090
+ }
85091
+ try {
85092
+ const messagesRes = await ctx.client.session.messages({
85093
+ path: { id: ctx.sessionID },
85094
+ query: { directory: ctx.directory }
85095
+ });
85096
+ const messages = normalizeSDKResponse(messagesRes, []);
85097
+ return messages.length > 0;
85098
+ } catch (error51) {
85099
+ if (ctx.verbose) {
85100
+ console.error(`[run] failed to read session messages while waiting for prompt start: ${String(error51)}`);
85101
+ }
85102
+ return false;
85103
+ }
85104
+ }
85105
+ async function waitForPromptStart(ctx, eventState, abortController, options = {}) {
85106
+ const timeoutMs = options.timeoutMs ?? DEFAULT_PROMPT_START_TIMEOUT_MS;
85107
+ const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_PROMPT_START_POLL_INTERVAL_MS;
85108
+ const startedAt = Date.now();
85109
+ while (!abortController.signal.aborted) {
85110
+ if (hasPromptStartEvidence(eventState)) {
85111
+ return;
85112
+ }
85113
+ if (eventState.mainSessionError) {
85114
+ throw new Error(`Session errored before prompt started: ${eventState.lastError ?? "unknown error"}`);
85115
+ }
85116
+ const status = await readMainSessionStatus(ctx);
85117
+ if (status === "busy" || status === "retry") {
85118
+ eventState.mainSessionStarted = true;
85119
+ eventState.mainSessionIdle = false;
85120
+ return;
85121
+ }
85122
+ if (status === "idle") {
85123
+ eventState.mainSessionIdle = true;
85124
+ }
85125
+ if (await hasPersistedMessages(ctx)) {
85126
+ eventState.mainSessionStarted = true;
85127
+ return;
85128
+ }
85129
+ if (Date.now() - startedAt >= timeoutMs) {
85130
+ throw new Error(`Prompt did not start within ${timeoutMs}ms; no busy status, message event, or persisted message was observed.`);
85131
+ }
85132
+ await sleep(pollIntervalMs);
85133
+ }
85134
+ throw createAbortError();
85135
+ }
85136
+
83578
85137
  // src/cli/run/agent-profile-colors.ts
83579
85138
  init_shared();
83580
85139
  async function loadAgentProfileColors(client3) {
@@ -83684,7 +85243,7 @@ function createTimestampedStdoutController(stdout = process.stdout) {
83684
85243
  // src/shared/posthog.ts
83685
85244
  init_index_node();
83686
85245
  import os5 from "os";
83687
- import { createHash as createHash3 } from "crypto";
85246
+ import { createHash as createHash4 } from "crypto";
83688
85247
  init_plugin_identity();
83689
85248
 
83690
85249
  // src/shared/posthog-activity-state.ts
@@ -83692,11 +85251,11 @@ init_data_path();
83692
85251
  init_logger();
83693
85252
  init_plugin_identity();
83694
85253
  init_write_file_atomically();
83695
- import { existsSync as existsSync31, mkdirSync as mkdirSync11, readFileSync as readFileSync21 } from "fs";
83696
- import { join as join35 } from "path";
85254
+ import { existsSync as existsSync34, mkdirSync as mkdirSync11, readFileSync as readFileSync21 } from "fs";
85255
+ import { join as join41 } from "path";
83697
85256
  var POSTHOG_ACTIVITY_STATE_FILE2 = "posthog-activity.json";
83698
85257
  function getPostHogActivityStateFilePath2() {
83699
- return join35(getDataDir(), CACHE_DIR_NAME, POSTHOG_ACTIVITY_STATE_FILE2);
85258
+ return join41(getDataDir(), CACHE_DIR_NAME, POSTHOG_ACTIVITY_STATE_FILE2);
83700
85259
  }
83701
85260
  function getUtcDayString2(date5) {
83702
85261
  return date5.toISOString().slice(0, 10);
@@ -83706,7 +85265,7 @@ function isPostHogActivityState2(value) {
83706
85265
  }
83707
85266
  function readPostHogActivityState2() {
83708
85267
  const stateFilePath = getPostHogActivityStateFilePath2();
83709
- if (!existsSync31(stateFilePath)) {
85268
+ if (!existsSync34(stateFilePath)) {
83710
85269
  return {};
83711
85270
  }
83712
85271
  try {
@@ -83727,7 +85286,7 @@ function readPostHogActivityState2() {
83727
85286
  function writePostHogActivityState2(nextState) {
83728
85287
  const stateFilePath = getPostHogActivityStateFilePath2();
83729
85288
  try {
83730
- mkdirSync11(join35(getDataDir(), CACHE_DIR_NAME), { recursive: true });
85289
+ mkdirSync11(join41(getDataDir(), CACHE_DIR_NAME), { recursive: true });
83731
85290
  writeFileAtomically(stateFilePath, `${JSON.stringify(nextState, null, 2)}
83732
85291
  `);
83733
85292
  } catch (error51) {
@@ -83775,8 +85334,11 @@ var NO_OP_POSTHOG2 = {
83775
85334
  function isFalsy(value) {
83776
85335
  return value === "0" || value === "false" || value === "no";
83777
85336
  }
85337
+ function isTruthy(value) {
85338
+ return value === "1" || value === "true" || value === "yes";
85339
+ }
83778
85340
  function shouldDisablePostHog2() {
83779
- if (process.env.OMO_DISABLE_POSTHOG === "true" || process.env.OMO_DISABLE_POSTHOG === "1") {
85341
+ if (isTruthy(process.env.OMO_DISABLE_POSTHOG?.trim().toLowerCase())) {
83780
85342
  return true;
83781
85343
  }
83782
85344
  return isFalsy(process.env.OMO_SEND_ANONYMOUS_TELEMETRY?.trim().toLowerCase());
@@ -83858,7 +85420,7 @@ function createPostHogClient2(source, options) {
83858
85420
  };
83859
85421
  }
83860
85422
  function getPostHogDistinctId2() {
83861
- return createHash3("sha256").update(`${PUBLISHED_PACKAGE_NAME}:${resolveOsProvider2().hostname()}`).digest("hex");
85423
+ return createHash4("sha256").update(`${PUBLISHED_PACKAGE_NAME}:${resolveOsProvider2().hostname()}`).digest("hex");
83862
85424
  }
83863
85425
  function createCliPostHog2() {
83864
85426
  return createPostHogClient2("cli", {
@@ -83877,7 +85439,7 @@ var EVENT_PROCESSOR_SHUTDOWN_TIMEOUT_MS = 2000;
83877
85439
  async function waitForEventProcessorShutdown(eventProcessor, timeoutMs = EVENT_PROCESSOR_SHUTDOWN_TIMEOUT_MS) {
83878
85440
  const completed = await Promise.race([
83879
85441
  eventProcessor.then(() => true),
83880
- new Promise((resolve10) => setTimeout(() => resolve10(false), timeoutMs))
85442
+ new Promise((resolve12) => setTimeout(() => resolve12(false), timeoutMs))
83881
85443
  ]);
83882
85444
  }
83883
85445
  async function run(options) {
@@ -83908,7 +85470,7 @@ async function run(options) {
83908
85470
  attach: options.attach,
83909
85471
  signal: abortController.signal
83910
85472
  });
83911
- const cleanup = () => {
85473
+ const cleanup2 = () => {
83912
85474
  serverCleanup();
83913
85475
  };
83914
85476
  const restoreInput = suppressRunInput();
@@ -83916,7 +85478,7 @@ async function run(options) {
83916
85478
  console.log(import_picocolors14.default.yellow(`
83917
85479
  Interrupted. Shutting down...`));
83918
85480
  restoreInput();
83919
- cleanup();
85481
+ cleanup2();
83920
85482
  process.exit(130);
83921
85483
  };
83922
85484
  process.on("SIGINT", handleSigint);
@@ -83974,10 +85536,13 @@ Interrupted. Shutting down...`));
83974
85536
  if (!promptMayHaveBeenAccepted && !isInternalPromptDispatchAccepted(promptResult)) {
83975
85537
  throw new Error(`Session ${sessionID} is not idle; promptAsync skipped by gate: ${promptResult.status}`);
83976
85538
  }
83977
- const exitCode = await pollForCompletion(ctx, eventState, abortController);
85539
+ await waitForPromptStart(ctx, eventState, abortController);
85540
+ const exitCode = await pollForCompletion(ctx, eventState, abortController, {
85541
+ requireMeaningfulWork: true
85542
+ });
83978
85543
  abortController.abort();
83979
85544
  await waitForEventProcessorShutdown(eventProcessor);
83980
- cleanup();
85545
+ cleanup2();
83981
85546
  const durationMs = Date.now() - startTime;
83982
85547
  if (options.onComplete) {
83983
85548
  await executeOnCompleteHook({
@@ -83999,7 +85564,7 @@ Interrupted. Shutting down...`));
83999
85564
  }
84000
85565
  return exitCode;
84001
85566
  } catch (err) {
84002
- cleanup();
85567
+ cleanup2();
84003
85568
  throw err;
84004
85569
  } finally {
84005
85570
  process.removeListener("SIGINT", handleSigint);
@@ -84172,13 +85737,13 @@ async function getLocalVersion(options = {}) {
84172
85737
  }
84173
85738
  }
84174
85739
  // src/cli/doctor/checks/system.ts
84175
- import { existsSync as existsSync42, readFileSync as readFileSync31 } from "fs";
85740
+ import { existsSync as existsSync45, readFileSync as readFileSync31 } from "fs";
84176
85741
 
84177
85742
  // src/cli/doctor/checks/system-binary.ts
84178
85743
  init_extract_semver();
84179
- import { existsSync as existsSync39, accessSync as accessSync3, constants as constants6 } from "fs";
84180
- import { homedir as homedir9 } from "os";
84181
- import { join as join42 } from "path";
85744
+ import { existsSync as existsSync42, accessSync as accessSync4, constants as constants7 } from "fs";
85745
+ import { homedir as homedir11 } from "os";
85746
+ import { join as join48 } from "path";
84182
85747
 
84183
85748
  // src/cli/doctor/spawn-with-timeout.ts
84184
85749
  init_spawn_with_windows_hide();
@@ -84191,8 +85756,8 @@ async function spawnWithTimeout(command, options, timeoutMs = DEFAULT_SPAWN_TIME
84191
85756
  return { stdout: "", stderr: "", exitCode: 1, timedOut: false };
84192
85757
  }
84193
85758
  let timer;
84194
- const timeoutPromise = new Promise((resolve10) => {
84195
- timer = setTimeout(() => resolve10("timeout"), timeoutMs);
85759
+ const timeoutPromise = new Promise((resolve12) => {
85760
+ timer = setTimeout(() => resolve12("timeout"), timeoutMs);
84196
85761
  });
84197
85762
  const processPromise = (async () => {
84198
85763
  await proc.exited;
@@ -84212,31 +85777,31 @@ async function spawnWithTimeout(command, options, timeoutMs = DEFAULT_SPAWN_TIME
84212
85777
 
84213
85778
  // src/cli/doctor/checks/system-binary.ts
84214
85779
  var WINDOWS_EXECUTABLE_EXTS = [".exe", ".cmd", ".bat", ".ps1"];
84215
- function isExecutable(path14) {
85780
+ function isExecutable2(path14) {
84216
85781
  try {
84217
- accessSync3(path14, constants6.X_OK);
85782
+ accessSync4(path14, constants7.X_OK);
84218
85783
  return true;
84219
85784
  } catch {
84220
85785
  return false;
84221
85786
  }
84222
85787
  }
84223
85788
  function getDesktopAppPaths(platform) {
84224
- const home = homedir9();
85789
+ const home = homedir11();
84225
85790
  switch (platform) {
84226
85791
  case "darwin":
84227
85792
  return [
84228
85793
  "/Applications/OpenCode.app/Contents/MacOS/OpenCode",
84229
- join42(home, "Applications", "OpenCode.app", "Contents", "MacOS", "OpenCode")
85794
+ join48(home, "Applications", "OpenCode.app", "Contents", "MacOS", "OpenCode")
84230
85795
  ];
84231
85796
  case "win32": {
84232
85797
  const programFiles = process.env.ProgramFiles;
84233
85798
  const localAppData = process.env.LOCALAPPDATA;
84234
85799
  const paths = [];
84235
85800
  if (programFiles) {
84236
- paths.push(join42(programFiles, "OpenCode", "OpenCode.exe"));
85801
+ paths.push(join48(programFiles, "OpenCode", "OpenCode.exe"));
84237
85802
  }
84238
85803
  if (localAppData) {
84239
- paths.push(join42(localAppData, "OpenCode", "OpenCode.exe"));
85804
+ paths.push(join48(localAppData, "OpenCode", "OpenCode.exe"));
84240
85805
  }
84241
85806
  return paths;
84242
85807
  }
@@ -84244,8 +85809,8 @@ function getDesktopAppPaths(platform) {
84244
85809
  return [
84245
85810
  "/usr/bin/opencode",
84246
85811
  "/usr/lib/opencode/opencode",
84247
- join42(home, "Applications", "opencode-desktop-linux-x86_64.AppImage"),
84248
- join42(home, "Applications", "opencode-desktop-linux-aarch64.AppImage")
85812
+ join48(home, "Applications", "opencode-desktop-linux-x86_64.AppImage"),
85813
+ join48(home, "Applications", "opencode-desktop-linux-aarch64.AppImage")
84249
85814
  ];
84250
85815
  default:
84251
85816
  return [];
@@ -84257,7 +85822,7 @@ function buildVersionCommand(binaryPath, platform) {
84257
85822
  }
84258
85823
  return [binaryPath, "--version"];
84259
85824
  }
84260
- function findDesktopBinary(platform = process.platform, checkExists = existsSync39) {
85825
+ function findDesktopBinary(platform = process.platform, checkExists = existsSync42) {
84261
85826
  for (const desktopPath of getDesktopAppPaths(platform)) {
84262
85827
  if (checkExists(desktopPath)) {
84263
85828
  return { binary: "opencode", path: desktopPath };
@@ -84265,7 +85830,7 @@ function findDesktopBinary(platform = process.platform, checkExists = existsSync
84265
85830
  }
84266
85831
  return null;
84267
85832
  }
84268
- async function findOpenCodeBinary(platform = process.platform, checkExists = existsSync39) {
85833
+ async function findOpenCodeBinary(platform = process.platform, checkExists = existsSync42) {
84269
85834
  for (const binary2 of OPENCODE_BINARIES2) {
84270
85835
  const path14 = Bun.which(binary2);
84271
85836
  if (path14 && checkExists(path14)) {
@@ -84273,12 +85838,12 @@ async function findOpenCodeBinary(platform = process.platform, checkExists = exi
84273
85838
  }
84274
85839
  }
84275
85840
  const pathEnv = process.env.PATH ?? "";
84276
- const delimiter2 = platform === "win32" ? ";" : ":";
85841
+ const delimiter3 = platform === "win32" ? ";" : ":";
84277
85842
  const candidates = getCommandCandidates2(platform);
84278
- for (const entry of pathEnv.split(delimiter2).filter(Boolean)) {
85843
+ for (const entry of pathEnv.split(delimiter3).filter(Boolean)) {
84279
85844
  for (const command of candidates) {
84280
- const fullPath = join42(entry, command);
84281
- if (checkExists(fullPath) && isExecutable(fullPath)) {
85845
+ const fullPath = join48(entry, command);
85846
+ if (checkExists(fullPath) && isExecutable2(fullPath)) {
84282
85847
  return { binary: command, path: fullPath };
84283
85848
  }
84284
85849
  }
@@ -84320,12 +85885,12 @@ function compareVersions3(current, minimum) {
84320
85885
 
84321
85886
  // src/cli/doctor/checks/system-plugin.ts
84322
85887
  init_shared();
84323
- import { existsSync as existsSync40, readFileSync as readFileSync29 } from "fs";
85888
+ import { existsSync as existsSync43, readFileSync as readFileSync29 } from "fs";
84324
85889
  function detectConfigPath() {
84325
85890
  const paths = getOpenCodeConfigPaths({ binary: "opencode", version: null });
84326
- if (existsSync40(paths.configJsonc))
85891
+ if (existsSync43(paths.configJsonc))
84327
85892
  return paths.configJsonc;
84328
- if (existsSync40(paths.configJson))
85893
+ if (existsSync43(paths.configJson))
84329
85894
  return paths.configJson;
84330
85895
  return null;
84331
85896
  }
@@ -84409,35 +85974,35 @@ function getPluginInfo() {
84409
85974
  init_file_utils2();
84410
85975
  init_checker();
84411
85976
  init_auto_update_checker();
84412
- import { existsSync as existsSync41, readFileSync as readFileSync30 } from "fs";
85977
+ import { existsSync as existsSync44, readFileSync as readFileSync30 } from "fs";
84413
85978
  import { createRequire } from "module";
84414
- import { homedir as homedir10 } from "os";
84415
- import { join as join43 } from "path";
85979
+ import { homedir as homedir12 } from "os";
85980
+ import { join as join49 } from "path";
84416
85981
  init_shared();
84417
85982
  function getPlatformDefaultCacheDir(platform = process.platform) {
84418
85983
  if (platform === "darwin")
84419
- return join43(homedir10(), "Library", "Caches");
85984
+ return join49(homedir12(), "Library", "Caches");
84420
85985
  if (platform === "win32")
84421
- return process.env.LOCALAPPDATA ?? join43(homedir10(), "AppData", "Local");
84422
- return join43(homedir10(), ".cache");
85986
+ return process.env.LOCALAPPDATA ?? join49(homedir12(), "AppData", "Local");
85987
+ return join49(homedir12(), ".cache");
84423
85988
  }
84424
85989
  function resolveOpenCodeCacheDir() {
84425
85990
  const xdgCacheHome = process.env.XDG_CACHE_HOME;
84426
85991
  if (xdgCacheHome)
84427
- return join43(xdgCacheHome, "opencode");
85992
+ return join49(xdgCacheHome, "opencode");
84428
85993
  const fromShared = getOpenCodeCacheDir();
84429
- const platformDefault = join43(getPlatformDefaultCacheDir(), "opencode");
84430
- if (existsSync41(fromShared) || !existsSync41(platformDefault))
85994
+ const platformDefault = join49(getPlatformDefaultCacheDir(), "opencode");
85995
+ if (existsSync44(fromShared) || !existsSync44(platformDefault))
84431
85996
  return fromShared;
84432
85997
  return platformDefault;
84433
85998
  }
84434
85999
  function resolveExistingDir(dirPath) {
84435
- if (!existsSync41(dirPath))
86000
+ if (!existsSync44(dirPath))
84436
86001
  return dirPath;
84437
86002
  return resolveSymlink(dirPath);
84438
86003
  }
84439
86004
  function readPackageJson(filePath) {
84440
- if (!existsSync41(filePath))
86005
+ if (!existsSync44(filePath))
84441
86006
  return null;
84442
86007
  try {
84443
86008
  const content = readFileSync30(filePath, "utf-8");
@@ -84455,11 +86020,11 @@ function normalizeVersion(value) {
84455
86020
  function createPackageCandidates(rootDir) {
84456
86021
  return ACCEPTED_PACKAGE_NAMES.map((packageName) => ({
84457
86022
  packageName,
84458
- installedPackagePath: join43(rootDir, "node_modules", packageName, "package.json")
86023
+ installedPackagePath: join49(rootDir, "node_modules", packageName, "package.json")
84459
86024
  }));
84460
86025
  }
84461
86026
  function selectInstalledPackage(candidate) {
84462
- return candidate.packageCandidates.find((packageCandidate) => existsSync41(packageCandidate.installedPackagePath)) ?? candidate.packageCandidates[0];
86027
+ return candidate.packageCandidates.find((packageCandidate) => existsSync44(packageCandidate.installedPackagePath)) ?? candidate.packageCandidates[0];
84463
86028
  }
84464
86029
  function getExpectedVersion(cachePackage, packageName) {
84465
86030
  return normalizeVersion(cachePackage?.dependencies?.[packageName]) ?? normalizeVersion(cachePackage?.dependencies?.[PACKAGE_NAME]);
@@ -84470,7 +86035,7 @@ function resolveInstalledPackageJsonPath() {
84470
86035
  for (const packageName of ACCEPTED_PACKAGE_NAMES) {
84471
86036
  try {
84472
86037
  const packageJsonPath = require2.resolve(`${packageName}/package.json`);
84473
- if (existsSync41(packageJsonPath)) {
86038
+ if (existsSync44(packageJsonPath)) {
84474
86039
  return { packageName, packageJsonPath };
84475
86040
  }
84476
86041
  } catch {
@@ -84489,20 +86054,20 @@ function getLoadedPluginVersion() {
84489
86054
  const candidates = [
84490
86055
  {
84491
86056
  cacheDir: configDir,
84492
- cachePackagePath: join43(configDir, "package.json"),
86057
+ cachePackagePath: join49(configDir, "package.json"),
84493
86058
  packageCandidates: createPackageCandidates(configDir)
84494
86059
  },
84495
86060
  {
84496
86061
  cacheDir,
84497
- cachePackagePath: join43(cacheDir, "package.json"),
86062
+ cachePackagePath: join49(cacheDir, "package.json"),
84498
86063
  packageCandidates: createPackageCandidates(cacheDir)
84499
86064
  }
84500
86065
  ];
84501
- const selectedCandidate = candidates.find((candidate) => candidate.packageCandidates.some((packageCandidate) => existsSync41(packageCandidate.installedPackagePath))) ?? candidates[0];
86066
+ const selectedCandidate = candidates.find((candidate) => candidate.packageCandidates.some((packageCandidate) => existsSync44(packageCandidate.installedPackagePath))) ?? candidates[0];
84502
86067
  const { cacheDir: selectedDir, cachePackagePath } = selectedCandidate;
84503
86068
  const selectedPackage = selectInstalledPackage(selectedCandidate);
84504
86069
  const candidateInstalledPath = selectedPackage.installedPackagePath;
84505
- const candidateExists = existsSync41(candidateInstalledPath);
86070
+ const candidateExists = existsSync44(candidateInstalledPath);
84506
86071
  const resolvedFallback = candidateExists ? null : resolveInstalledPackageJsonPath();
84507
86072
  const installedPackagePath = resolvedFallback?.packageJsonPath ?? candidateInstalledPath;
84508
86073
  const resolvedPackageName = resolvedFallback?.packageName ?? selectedPackage.packageName;
@@ -84541,7 +86106,7 @@ var defaultDeps5 = {
84541
86106
  function isConfigValid(configPath) {
84542
86107
  if (!configPath)
84543
86108
  return true;
84544
- if (!existsSync42(configPath))
86109
+ if (!existsSync45(configPath))
84545
86110
  return false;
84546
86111
  try {
84547
86112
  parseJsonc(readFileSync31(configPath, "utf-8"));
@@ -84667,29 +86232,29 @@ async function checkSystem(deps = defaultDeps5) {
84667
86232
 
84668
86233
  // src/cli/doctor/checks/config.ts
84669
86234
  import { readFileSync as readFileSync34 } from "fs";
84670
- import { join as join47 } from "path";
86235
+ import { join as join53 } from "path";
84671
86236
  init_shared();
84672
86237
  init_plugin_identity();
84673
86238
 
84674
86239
  // src/cli/doctor/checks/model-resolution-cache.ts
84675
86240
  init_shared();
84676
- import { existsSync as existsSync43, readFileSync as readFileSync32 } from "fs";
84677
- import { homedir as homedir11 } from "os";
84678
- import { join as join44 } from "path";
86241
+ import { existsSync as existsSync46, readFileSync as readFileSync32 } from "fs";
86242
+ import { homedir as homedir13 } from "os";
86243
+ import { join as join50 } from "path";
84679
86244
  function getUserConfigDir2() {
84680
86245
  const xdgConfig = process.env.XDG_CONFIG_HOME;
84681
86246
  if (xdgConfig)
84682
- return join44(xdgConfig, "opencode");
84683
- return join44(homedir11(), ".config", "opencode");
86247
+ return join50(xdgConfig, "opencode");
86248
+ return join50(homedir13(), ".config", "opencode");
84684
86249
  }
84685
86250
  function loadCustomProviderNames() {
84686
86251
  const configDir = getUserConfigDir2();
84687
86252
  const candidatePaths = [
84688
- join44(configDir, "opencode.json"),
84689
- join44(configDir, "opencode.jsonc")
86253
+ join50(configDir, "opencode.json"),
86254
+ join50(configDir, "opencode.jsonc")
84690
86255
  ];
84691
86256
  for (const configPath of candidatePaths) {
84692
- if (!existsSync43(configPath))
86257
+ if (!existsSync46(configPath))
84693
86258
  continue;
84694
86259
  try {
84695
86260
  const content = readFileSync32(configPath, "utf-8");
@@ -84702,9 +86267,9 @@ function loadCustomProviderNames() {
84702
86267
  return [];
84703
86268
  }
84704
86269
  function loadAvailableModelsFromCache() {
84705
- const cacheFile = join44(getOpenCodeCacheDir(), "models.json");
86270
+ const cacheFile = join50(getOpenCodeCacheDir(), "models.json");
84706
86271
  const customProviders = loadCustomProviderNames();
84707
- if (!existsSync43(cacheFile)) {
86272
+ if (!existsSync46(cacheFile)) {
84708
86273
  if (customProviders.length > 0) {
84709
86274
  return { providers: customProviders, modelCount: 0, cacheExists: true };
84710
86275
  }
@@ -84736,8 +86301,8 @@ init_model_capabilities2();
84736
86301
  init_shared();
84737
86302
  init_plugin_identity();
84738
86303
  import { readFileSync as readFileSync33 } from "fs";
84739
- import { join as join45 } from "path";
84740
- var PROJECT_CONFIG_DIR = join45(process.cwd(), ".opencode");
86304
+ import { join as join51 } from "path";
86305
+ var PROJECT_CONFIG_DIR = join51(process.cwd(), ".opencode");
84741
86306
  function loadOmoConfig() {
84742
86307
  const projectDetected = detectPluginConfigFile(PROJECT_CONFIG_DIR, {
84743
86308
  basenames: [CONFIG_BASENAME],
@@ -84769,7 +86334,7 @@ function loadOmoConfig() {
84769
86334
 
84770
86335
  // src/cli/doctor/checks/model-resolution-details.ts
84771
86336
  init_shared();
84772
- import { join as join46 } from "path";
86337
+ import { join as join52 } from "path";
84773
86338
 
84774
86339
  // src/cli/doctor/checks/model-resolution-variant.ts
84775
86340
  function formatModelWithVariant(model, variant) {
@@ -84811,7 +86376,7 @@ function formatCapabilityResolutionLabel(mode) {
84811
86376
  }
84812
86377
  function buildModelResolutionDetails(options) {
84813
86378
  const details = [];
84814
- const cacheFile = join46(getOpenCodeCacheDir(), "models.json");
86379
+ const cacheFile = join52(getOpenCodeCacheDir(), "models.json");
84815
86380
  details.push("\u2550\u2550\u2550 Available Models (from cache) \u2550\u2550\u2550");
84816
86381
  details.push("");
84817
86382
  if (options.available.cacheExists) {
@@ -84966,7 +86531,7 @@ async function checkModels() {
84966
86531
  }
84967
86532
 
84968
86533
  // src/cli/doctor/checks/config.ts
84969
- var PROJECT_CONFIG_DIR2 = join47(process.cwd(), ".opencode");
86534
+ var PROJECT_CONFIG_DIR2 = join53(process.cwd(), ".opencode");
84970
86535
  function findConfigPath() {
84971
86536
  const projectConfig = detectPluginConfigFile(PROJECT_CONFIG_DIR2, {
84972
86537
  basenames: [CONFIG_BASENAME],
@@ -85092,27 +86657,27 @@ async function checkConfig() {
85092
86657
  }
85093
86658
 
85094
86659
  // src/cli/doctor/checks/dependencies.ts
85095
- import { existsSync as existsSync44 } from "fs";
86660
+ import { existsSync as existsSync47 } from "fs";
85096
86661
  import { createRequire as createRequire2 } from "module";
85097
- import { dirname as dirname20, join as join49 } from "path";
86662
+ import { dirname as dirname22, join as join55 } from "path";
85098
86663
 
85099
86664
  // src/hooks/comment-checker/downloader.ts
85100
- import { join as join48 } from "path";
85101
- import { homedir as homedir12, tmpdir as tmpdir3 } from "os";
86665
+ import { join as join54 } from "path";
86666
+ import { homedir as homedir14, tmpdir as tmpdir3 } from "os";
85102
86667
  init_binary_downloader();
85103
86668
  init_logger();
85104
86669
  init_plugin_identity();
85105
86670
  var DEBUG = process.env.COMMENT_CHECKER_DEBUG === "1";
85106
- var DEBUG_FILE = join48(tmpdir3(), "comment-checker-debug.log");
86671
+ var DEBUG_FILE = join54(tmpdir3(), "comment-checker-debug.log");
85107
86672
  function getCacheDir2() {
85108
86673
  if (process.platform === "win32") {
85109
86674
  const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
85110
- const base2 = localAppData || join48(homedir12(), "AppData", "Local");
85111
- return join48(base2, CACHE_DIR_NAME, "bin");
86675
+ const base2 = localAppData || join54(homedir14(), "AppData", "Local");
86676
+ return join54(base2, CACHE_DIR_NAME, "bin");
85112
86677
  }
85113
86678
  const xdgCache = process.env.XDG_CACHE_HOME;
85114
- const base = xdgCache || join48(homedir12(), ".cache");
85115
- return join48(base, CACHE_DIR_NAME, "bin");
86679
+ const base = xdgCache || join54(homedir14(), ".cache");
86680
+ return join54(base, CACHE_DIR_NAME, "bin");
85116
86681
  }
85117
86682
  function getBinaryName() {
85118
86683
  return process.platform === "win32" ? "comment-checker.exe" : "comment-checker";
@@ -85176,15 +86741,15 @@ async function checkAstGrepNapi() {
85176
86741
  path: null
85177
86742
  };
85178
86743
  } catch {
85179
- const { existsSync: existsSync45 } = await import("fs");
85180
- const { join: join50 } = await import("path");
85181
- const { homedir: homedir13 } = await import("os");
86744
+ const { existsSync: existsSync48 } = await import("fs");
86745
+ const { join: join56 } = await import("path");
86746
+ const { homedir: homedir15 } = await import("os");
85182
86747
  const pathsToCheck = [
85183
- join50(homedir13(), ".config", "opencode", "node_modules", "@ast-grep", "napi"),
85184
- join50(process.cwd(), "node_modules", "@ast-grep", "napi")
86748
+ join56(homedir15(), ".config", "opencode", "node_modules", "@ast-grep", "napi"),
86749
+ join56(process.cwd(), "node_modules", "@ast-grep", "napi")
85185
86750
  ];
85186
86751
  for (const napiPath of pathsToCheck) {
85187
- if (existsSync45(napiPath)) {
86752
+ if (existsSync48(napiPath)) {
85188
86753
  return {
85189
86754
  name: "AST-Grep NAPI",
85190
86755
  required: false,
@@ -85209,8 +86774,8 @@ function findCommentCheckerPackageBinary() {
85209
86774
  try {
85210
86775
  const require2 = createRequire2(import.meta.url);
85211
86776
  const pkgPath = require2.resolve("@code-yeongyu/comment-checker/package.json");
85212
- const binaryPath = join49(dirname20(pkgPath), "bin", binaryName);
85213
- if (existsSync44(binaryPath))
86777
+ const binaryPath = join55(dirname22(pkgPath), "bin", binaryName);
86778
+ if (existsSync47(binaryPath))
85214
86779
  return binaryPath;
85215
86780
  } catch {}
85216
86781
  return null;
@@ -85343,11 +86908,11 @@ async function getGhCliInfo() {
85343
86908
 
85344
86909
  // src/cli/doctor/checks/tools-lsp.ts
85345
86910
  import { readFileSync as readFileSync35 } from "fs";
85346
- import { join as join51 } from "path";
86911
+ import { join as join56 } from "path";
85347
86912
 
85348
86913
  // src/mcp/lsp.ts
85349
- import { existsSync as existsSync45 } from "fs";
85350
- import { dirname as dirname21, resolve as resolve10 } from "path";
86914
+ import { existsSync as existsSync48 } from "fs";
86915
+ import { dirname as dirname23, resolve as resolve12 } from "path";
85351
86916
  import { fileURLToPath as fileURLToPath3 } from "url";
85352
86917
 
85353
86918
  // src/mcp/cli-suffix.ts
@@ -85360,66 +86925,6 @@ function hasCliSuffix(candidatePath, suffix) {
85360
86925
 
85361
86926
  // src/mcp/runtime-executable.ts
85362
86927
  import { basename as basename8 } from "path";
85363
-
85364
- // src/shared/bun-which-shim.ts
85365
- import { accessSync as accessSync4, constants as constants7 } from "fs";
85366
- import { delimiter as delimiter2, join as join50 } from "path";
85367
- var runtime3 = globalThis;
85368
- var IS_BUN2 = typeof runtime3.Bun !== "undefined";
85369
- function isUnsafeCommandName(commandName) {
85370
- if (commandName.includes("/") || commandName.includes("\\"))
85371
- return true;
85372
- if (commandName === "." || commandName === ".." || commandName.includes(".."))
85373
- return true;
85374
- if (/^[a-zA-Z]:/.test(commandName))
85375
- return true;
85376
- if (commandName.includes("\x00"))
85377
- return true;
85378
- return false;
85379
- }
85380
- function isExecutable2(filePath) {
85381
- try {
85382
- accessSync4(filePath, constants7.X_OK);
85383
- return true;
85384
- } catch {
85385
- return false;
85386
- }
85387
- }
85388
- function resolvePathValue() {
85389
- if (process.platform === "win32")
85390
- return process.env.Path ?? process.env.PATH;
85391
- return process.env.PATH;
85392
- }
85393
- function getWindowsCandidates(commandName) {
85394
- if (process.platform !== "win32")
85395
- return [commandName];
85396
- return [commandName, `${commandName}.exe`, `${commandName}.cmd`, `${commandName}.bat`, `${commandName}.com`];
85397
- }
85398
- function bunWhich(commandName) {
85399
- if (!commandName)
85400
- return null;
85401
- if (isUnsafeCommandName(commandName))
85402
- return null;
85403
- if (IS_BUN2)
85404
- return runtime3.Bun?.which(commandName) ?? null;
85405
- const pathValue = resolvePathValue();
85406
- if (!pathValue)
85407
- return null;
85408
- const pathEntries = pathValue.split(delimiter2).filter((pathEntry) => pathEntry.length > 0);
85409
- if (pathEntries.length === 0)
85410
- return null;
85411
- const candidateNames = getWindowsCandidates(commandName);
85412
- for (const pathEntry of pathEntries) {
85413
- for (const candidateName of candidateNames) {
85414
- const candidatePath = join50(pathEntry, candidateName);
85415
- if (isExecutable2(candidatePath))
85416
- return candidatePath;
85417
- }
85418
- }
85419
- return null;
85420
- }
85421
-
85422
- // src/mcp/runtime-executable.ts
85423
86928
  var NODE_EXECUTABLE_NAMES = new Set(["node", "node.exe"]);
85424
86929
  function isUnsafeCommandName2(commandName) {
85425
86930
  if (commandName.length === 0)
@@ -85481,9 +86986,9 @@ var LSP_BOOTSTRAP_SCRIPT = [
85481
86986
  "finish(run(process.execPath, [dist, 'mcp'], 'inherit'))"
85482
86987
  ].join(";");
85483
86988
  function addAncestorCommandCandidates(startDirectory, target, seenPaths, pathExists, resolveExecutable) {
85484
- let currentDirectory = resolve10(startDirectory);
86989
+ let currentDirectory = resolve12(startDirectory);
85485
86990
  while (true) {
85486
- const distCliPath = resolve10(currentDirectory, SUBMODULE_REL, DIST_CLI_REL);
86991
+ const distCliPath = resolve12(currentDirectory, SUBMODULE_REL, DIST_CLI_REL);
85487
86992
  if (!seenPaths.has(distCliPath)) {
85488
86993
  const runtime4 = resolveJavaScriptRuntime(resolveExecutable);
85489
86994
  seenPaths.add(distCliPath);
@@ -85494,7 +86999,7 @@ function addAncestorCommandCandidates(startDirectory, target, seenPaths, pathExi
85494
86999
  exists: runtime4.available && pathExists(distCliPath)
85495
87000
  });
85496
87001
  }
85497
- const sourceCliPath = resolve10(currentDirectory, SUBMODULE_REL, SOURCE_CLI_REL);
87002
+ const sourceCliPath = resolve12(currentDirectory, SUBMODULE_REL, SOURCE_CLI_REL);
85498
87003
  if (!seenPaths.has(sourceCliPath)) {
85499
87004
  const runtime4 = resolveExecutable("bun");
85500
87005
  seenPaths.add(sourceCliPath);
@@ -85505,7 +87010,7 @@ function addAncestorCommandCandidates(startDirectory, target, seenPaths, pathExi
85505
87010
  exists: runtime4.available && pathExists(sourceCliPath)
85506
87011
  });
85507
87012
  }
85508
- const parentDirectory = resolve10(currentDirectory, "..");
87013
+ const parentDirectory = resolve12(currentDirectory, "..");
85509
87014
  if (parentDirectory === currentDirectory) {
85510
87015
  return;
85511
87016
  }
@@ -85514,13 +87019,13 @@ function addAncestorCommandCandidates(startDirectory, target, seenPaths, pathExi
85514
87019
  }
85515
87020
  function getModuleDirectory(moduleUrl) {
85516
87021
  try {
85517
- return dirname21(fileURLToPath3(moduleUrl));
87022
+ return dirname23(fileURLToPath3(moduleUrl));
85518
87023
  } catch {
85519
87024
  return null;
85520
87025
  }
85521
87026
  }
85522
87027
  function findBootstrapRoot(candidates, pathExists) {
85523
- return candidates.find((candidate) => pathExists(resolve10(candidate.root, "package.json")))?.root ?? process.cwd();
87028
+ return candidates.find((candidate) => pathExists(resolve12(candidate.root, "package.json")))?.root ?? process.cwd();
85524
87029
  }
85525
87030
  function resolveJavaScriptRuntime(resolveExecutable) {
85526
87031
  const node = resolveExecutable("node");
@@ -85534,12 +87039,12 @@ function createBootstrapCandidate(root, resolveExecutable) {
85534
87039
  return {
85535
87040
  command: [runtime4.command, "-e", LSP_BOOTSTRAP_SCRIPT, root, git.command, npm.command, bun.command],
85536
87041
  root,
85537
- path: resolve10(root, SUBMODULE_REL, DIST_CLI_REL),
87042
+ path: resolve12(root, SUBMODULE_REL, DIST_CLI_REL),
85538
87043
  exists: runtime4.available && git.available && npm.available
85539
87044
  };
85540
87045
  }
85541
87046
  function resolveLspCommand(options = {}) {
85542
- const pathExists = options.exists ?? existsSync45;
87047
+ const pathExists = options.exists ?? existsSync48;
85543
87048
  const resolveExecutable = options.resolveExecutable ?? resolveRuntimeExecutable;
85544
87049
  const candidates = [];
85545
87050
  const seenPaths = new Set;
@@ -85589,7 +87094,7 @@ function readOmoConfig(configDirectory) {
85589
87094
  }
85590
87095
  function isLspMcpDisabled(options) {
85591
87096
  const userConfigDirectory = options.configDirectory ?? getOpenCodeConfigDir({ binary: "opencode" });
85592
- const projectConfigDirectory = join51(options.cwd ?? process.cwd(), ".opencode");
87097
+ const projectConfigDirectory = join56(options.cwd ?? process.cwd(), ".opencode");
85593
87098
  const userConfig = readOmoConfig(userConfigDirectory);
85594
87099
  const projectConfig = readOmoConfig(projectConfigDirectory);
85595
87100
  const disabledMcps = new Set([
@@ -85608,21 +87113,21 @@ function getInstalledLspServers(options = {}) {
85608
87113
 
85609
87114
  // src/cli/doctor/checks/tools-mcp.ts
85610
87115
  init_shared();
85611
- import { existsSync as existsSync46, readFileSync as readFileSync36 } from "fs";
85612
- import { homedir as homedir13 } from "os";
85613
- import { join as join52 } from "path";
87116
+ import { existsSync as existsSync49, readFileSync as readFileSync36 } from "fs";
87117
+ import { homedir as homedir15 } from "os";
87118
+ import { join as join57 } from "path";
85614
87119
  var BUILTIN_MCP_SERVERS = ["websearch", "context7", "grep_app", "lsp", "ast_grep"];
85615
87120
  function getMcpConfigPaths() {
85616
87121
  return [
85617
- join52(homedir13(), ".claude", ".mcp.json"),
85618
- join52(process.cwd(), ".mcp.json"),
85619
- join52(process.cwd(), ".claude", ".mcp.json")
87122
+ join57(homedir15(), ".claude", ".mcp.json"),
87123
+ join57(process.cwd(), ".mcp.json"),
87124
+ join57(process.cwd(), ".claude", ".mcp.json")
85620
87125
  ];
85621
87126
  }
85622
87127
  function loadUserMcpConfig() {
85623
87128
  const servers = {};
85624
87129
  for (const configPath of getMcpConfigPaths()) {
85625
- if (!existsSync46(configPath))
87130
+ if (!existsSync49(configPath))
85626
87131
  continue;
85627
87132
  try {
85628
87133
  const content = readFileSync36(configPath, "utf-8");
@@ -85779,10 +87284,10 @@ async function probeBinary(cmd, args) {
85779
87284
 
85780
87285
  // src/features/team-mode/team-registry/paths.ts
85781
87286
  init_logger();
85782
- import { homedir as homedir14 } from "os";
87287
+ import { homedir as homedir16 } from "os";
85783
87288
  import path14 from "path";
85784
87289
  function resolveBaseDir(config2) {
85785
- return config2.base_dir ?? path14.join(homedir14(), ".omo");
87290
+ return config2.base_dir ?? path14.join(homedir16(), ".omo");
85786
87291
  }
85787
87292
 
85788
87293
  // src/cli/doctor/checks/team-mode.ts
@@ -85849,22 +87354,57 @@ async function pathExists(dir) {
85849
87354
 
85850
87355
  // src/cli/doctor/checks/tui-plugin-config.ts
85851
87356
  init_shared();
85852
- import { existsSync as existsSync47, readFileSync as readFileSync38 } from "fs";
85853
- import { join as join53 } from "path";
87357
+ import { existsSync as existsSync50, readFileSync as readFileSync38 } from "fs";
87358
+ import { join as join58 } from "path";
85854
87359
  var TUI_SUBPATH = "tui";
85855
- function isOurFilePluginEntry(entry) {
85856
- if (!entry.startsWith("file:"))
85857
- return false;
87360
+ var TUI_EXPORT_SUBPATH = `./${TUI_SUBPATH}`;
87361
+ function fileEntryPackageJsonPath(entry) {
85858
87362
  let path16 = entry.slice("file:".length);
85859
87363
  if (path16.startsWith("//"))
85860
87364
  path16 = path16.slice(2);
87365
+ return join58(path16, "package.json");
87366
+ }
87367
+ function packageJsonExportsTui(pkgJsonPath) {
87368
+ if (!existsSync50(pkgJsonPath))
87369
+ return null;
85861
87370
  try {
85862
- const pkgJsonPath = join53(path16, "package.json");
85863
- if (!existsSync47(pkgJsonPath))
87371
+ const parsed = JSON.parse(readFileSync38(pkgJsonPath, "utf-8"));
87372
+ if (parsed.exports === undefined)
87373
+ return null;
87374
+ if (typeof parsed.exports === "string")
87375
+ return false;
87376
+ if (parsed.exports == null || typeof parsed.exports !== "object" || Array.isArray(parsed.exports))
87377
+ return null;
87378
+ return Object.hasOwn(parsed.exports, TUI_EXPORT_SUBPATH);
87379
+ } catch (error51) {
87380
+ return null;
87381
+ }
87382
+ }
87383
+ function packageNameFromServerEntry(entry) {
87384
+ if (entry === PLUGIN_NAME || entry.startsWith(`${PLUGIN_NAME}@`))
87385
+ return PLUGIN_NAME;
87386
+ if (entry === LEGACY_PLUGIN_NAME || entry.startsWith(`${LEGACY_PLUGIN_NAME}@`))
87387
+ return LEGACY_PLUGIN_NAME;
87388
+ return null;
87389
+ }
87390
+ function packageExportsTuiForServerEntry(entry) {
87391
+ if (entry.startsWith("file:"))
87392
+ return packageJsonExportsTui(fileEntryPackageJsonPath(entry));
87393
+ const packageName = packageNameFromServerEntry(entry);
87394
+ if (packageName === null)
87395
+ return null;
87396
+ return packageJsonExportsTui(join58(getOpenCodeConfigDir({ binary: "opencode" }), "node_modules", packageName, "package.json"));
87397
+ }
87398
+ function isOurFilePluginEntry(entry) {
87399
+ if (!entry.startsWith("file:"))
87400
+ return false;
87401
+ try {
87402
+ const pkgJsonPath = fileEntryPackageJsonPath(entry);
87403
+ if (!existsSync50(pkgJsonPath))
85864
87404
  return false;
85865
87405
  const parsed = JSON.parse(readFileSync38(pkgJsonPath, "utf-8"));
85866
87406
  return typeof parsed.name === "string" && ACCEPTED_PACKAGE_NAMES.includes(parsed.name);
85867
- } catch {
87407
+ } catch (error51) {
85868
87408
  return false;
85869
87409
  }
85870
87410
  }
@@ -85878,41 +87418,56 @@ function isServerPluginEntry(entry) {
85878
87418
  return false;
85879
87419
  }
85880
87420
  function isTuiPluginEntry(entry) {
87421
+ if (isNamedTuiPluginEntry(entry))
87422
+ return true;
87423
+ if (entry.startsWith("file:") && isOurFilePluginEntry(entry))
87424
+ return true;
87425
+ return false;
87426
+ }
87427
+ function isNamedTuiPluginEntry(entry) {
85881
87428
  const canonicalPrefix = `${PLUGIN_NAME}/${TUI_SUBPATH}`;
85882
87429
  const legacyPrefix = `${LEGACY_PLUGIN_NAME}/${TUI_SUBPATH}`;
85883
87430
  if (entry === canonicalPrefix || entry.startsWith(`${canonicalPrefix}@`))
85884
87431
  return true;
85885
87432
  if (entry === legacyPrefix || entry.startsWith(`${legacyPrefix}@`))
85886
87433
  return true;
85887
- if (entry.startsWith("file:") && isOurFilePluginEntry(entry))
85888
- return true;
85889
87434
  return false;
85890
87435
  }
85891
87436
  function detectServerPluginRegistration() {
85892
87437
  const paths = getOpenCodeConfigPaths({ binary: "opencode", version: null });
85893
- const configPath = existsSync47(paths.configJsonc) ? paths.configJsonc : existsSync47(paths.configJson) ? paths.configJson : null;
87438
+ const configPath = existsSync50(paths.configJsonc) ? paths.configJsonc : existsSync50(paths.configJson) ? paths.configJson : null;
85894
87439
  if (!configPath) {
85895
- return { registered: false, configPath: null };
87440
+ return { registered: false, configPath: null, packageExportsTui: null };
85896
87441
  }
85897
87442
  try {
85898
87443
  const parsed = parseJsonc(readFileSync38(configPath, "utf-8"));
85899
87444
  const plugins = parsed.plugin ?? [];
85900
- return { registered: plugins.some(isServerPluginEntry), configPath };
85901
- } catch {
85902
- return { registered: false, configPath };
87445
+ const serverEntry = plugins.find(isServerPluginEntry);
87446
+ return {
87447
+ registered: serverEntry !== undefined,
87448
+ configPath,
87449
+ packageExportsTui: serverEntry === undefined ? null : packageExportsTuiForServerEntry(serverEntry)
87450
+ };
87451
+ } catch (error51) {
87452
+ return { registered: false, configPath, packageExportsTui: null };
85903
87453
  }
85904
87454
  }
85905
87455
  function detectTuiPluginRegistration() {
85906
- const tuiJsonPath = join53(getOpenCodeConfigDir({ binary: "opencode" }), "tui.json");
85907
- if (!existsSync47(tuiJsonPath)) {
85908
- return { registered: false, configPath: tuiJsonPath, exists: false };
87456
+ const tuiJsonPath = join58(getOpenCodeConfigDir({ binary: "opencode" }), "tui.json");
87457
+ if (!existsSync50(tuiJsonPath)) {
87458
+ return { registered: false, configPath: tuiJsonPath, exists: false, hasNamedTuiEntry: false };
85909
87459
  }
85910
87460
  try {
85911
87461
  const parsed = parseJsonc(readFileSync38(tuiJsonPath, "utf-8"));
85912
87462
  const plugins = parsed.plugin ?? [];
85913
- return { registered: plugins.some(isTuiPluginEntry), configPath: tuiJsonPath, exists: true };
85914
- } catch {
85915
- return { registered: false, configPath: tuiJsonPath, exists: true };
87463
+ return {
87464
+ registered: plugins.some(isTuiPluginEntry),
87465
+ configPath: tuiJsonPath,
87466
+ exists: true,
87467
+ hasNamedTuiEntry: plugins.some(isNamedTuiPluginEntry)
87468
+ };
87469
+ } catch (error51) {
87470
+ return { registered: false, configPath: tuiJsonPath, exists: true, hasNamedTuiEntry: false };
85916
87471
  }
85917
87472
  }
85918
87473
  async function checkTuiPluginConfig() {
@@ -85934,7 +87489,32 @@ async function checkTuiPluginConfig() {
85934
87489
  issues
85935
87490
  };
85936
87491
  }
87492
+ if (server2.registered && server2.packageExportsTui === false && tui.hasNamedTuiEntry) {
87493
+ issues.push({
87494
+ title: "TUI plugin entry in tui.json is unresolvable",
87495
+ description: `The installed ${PLUGIN_NAME} package registered in opencode.json does not export ` + `"${TUI_EXPORT_SUBPATH}", but tui.json contains "${PLUGIN_NAME}/${TUI_SUBPATH}". ` + "OpenCode TUI may try to resolve that package subpath as a GitHub repository and fail.",
87496
+ fix: `Remove "${PLUGIN_NAME}/${TUI_SUBPATH}" or "${LEGACY_PLUGIN_NAME}/${TUI_SUBPATH}" from the "plugin" array in ${tui.configPath}.`,
87497
+ affects: ["TUI startup", "plugin loading"],
87498
+ severity: "warning"
87499
+ });
87500
+ return {
87501
+ name,
87502
+ status: "warn",
87503
+ message: "TUI plugin entry in tui.json is unresolvable",
87504
+ details: details.length > 0 ? details : undefined,
87505
+ issues
87506
+ };
87507
+ }
85937
87508
  if (server2.registered && !tui.registered) {
87509
+ if (server2.packageExportsTui === false) {
87510
+ return {
87511
+ name,
87512
+ status: "pass",
87513
+ message: "Server plugin registered; TUI subpath not shipped by this package version",
87514
+ details: details.length > 0 ? details : undefined,
87515
+ issues
87516
+ };
87517
+ }
85938
87518
  issues.push({
85939
87519
  title: "TUI plugin entry missing from tui.json",
85940
87520
  description: "The server plugin is registered in opencode.json, but the TUI plugin entry " + `("${PLUGIN_NAME}/${TUI_SUBPATH}") is missing from tui.json. The Roles \xB7 ` + "Models sidebar section and TUI-only commands will not appear.",
@@ -86361,11 +87941,11 @@ async function refreshModelCapabilities(options, deps = {}) {
86361
87941
 
86362
87942
  // src/features/mcp-oauth/storage.ts
86363
87943
  init_shared();
86364
- import { chmodSync as chmodSync2, existsSync as existsSync48, mkdirSync as mkdirSync13, readFileSync as readFileSync39, renameSync as renameSync6, unlinkSync as unlinkSync8, writeFileSync as writeFileSync10 } from "fs";
86365
- import { dirname as dirname22, join as join54 } from "path";
87944
+ import { chmodSync as chmodSync2, existsSync as existsSync51, mkdirSync as mkdirSync13, readFileSync as readFileSync39, renameSync as renameSync6, unlinkSync as unlinkSync8, writeFileSync as writeFileSync10 } from "fs";
87945
+ import { dirname as dirname24, join as join59 } from "path";
86366
87946
  var STORAGE_FILE_NAME = "mcp-oauth.json";
86367
87947
  function getMcpOauthStoragePath() {
86368
- return join54(getOpenCodeConfigDir({ binary: "opencode" }), STORAGE_FILE_NAME);
87948
+ return join59(getOpenCodeConfigDir({ binary: "opencode" }), STORAGE_FILE_NAME);
86369
87949
  }
86370
87950
  function normalizeHost2(serverHost) {
86371
87951
  let host = serverHost.trim();
@@ -86402,7 +87982,7 @@ function buildKey(serverHost, resource) {
86402
87982
  }
86403
87983
  function readStore() {
86404
87984
  const filePath = getMcpOauthStoragePath();
86405
- if (!existsSync48(filePath)) {
87985
+ if (!existsSync51(filePath)) {
86406
87986
  return null;
86407
87987
  }
86408
87988
  try {
@@ -86415,8 +87995,8 @@ function readStore() {
86415
87995
  function writeStore(store2) {
86416
87996
  const filePath = getMcpOauthStoragePath();
86417
87997
  try {
86418
- const dir = dirname22(filePath);
86419
- if (!existsSync48(dir)) {
87998
+ const dir = dirname24(filePath);
87999
+ if (!existsSync51(dir)) {
86420
88000
  mkdirSync13(dir, { recursive: true });
86421
88001
  }
86422
88002
  const tempPath = `${filePath}.tmp.${Date.now()}`;
@@ -86453,7 +88033,7 @@ function deleteToken(serverHost, resource) {
86453
88033
  if (Object.keys(store2).length === 0) {
86454
88034
  try {
86455
88035
  const filePath = getMcpOauthStoragePath();
86456
- if (existsSync48(filePath)) {
88036
+ if (existsSync51(filePath)) {
86457
88037
  unlinkSync8(filePath);
86458
88038
  }
86459
88039
  return true;
@@ -86622,7 +88202,7 @@ async function getOrRegisterClient(options) {
86622
88202
  }
86623
88203
  }
86624
88204
  function parseRegistrationResponse(data) {
86625
- if (!isRecord14(data))
88205
+ if (!isRecord17(data))
86626
88206
  return null;
86627
88207
  const clientId = data.client_id;
86628
88208
  if (typeof clientId !== "string" || clientId.length === 0)
@@ -86633,7 +88213,7 @@ function parseRegistrationResponse(data) {
86633
88213
  }
86634
88214
  return { clientId };
86635
88215
  }
86636
- function isRecord14(value) {
88216
+ function isRecord17(value) {
86637
88217
  return typeof value === "object" && value !== null;
86638
88218
  }
86639
88219
 
@@ -86647,13 +88227,13 @@ async function findAvailablePort2(startPort = DEFAULT_PORT) {
86647
88227
 
86648
88228
  // src/features/mcp-oauth/oauth-authorization-flow.ts
86649
88229
  import { spawn as spawn2 } from "child_process";
86650
- import { createHash as createHash4, randomBytes as randomBytes2 } from "crypto";
88230
+ import { createHash as createHash5, randomBytes as randomBytes2 } from "crypto";
86651
88231
  import { createServer as createServer2 } from "http";
86652
88232
  function generateCodeVerifier() {
86653
88233
  return randomBytes2(32).toString("base64url");
86654
88234
  }
86655
88235
  function generateCodeChallenge(verifier) {
86656
- return createHash4("sha256").update(verifier).digest("base64url");
88236
+ return createHash5("sha256").update(verifier).digest("base64url");
86657
88237
  }
86658
88238
  function buildAuthorizationUrl(authorizationEndpoint, options) {
86659
88239
  const url2 = new URL(authorizationEndpoint);
@@ -86673,7 +88253,7 @@ function buildAuthorizationUrl(authorizationEndpoint, options) {
86673
88253
  }
86674
88254
  var CALLBACK_TIMEOUT_MS = 5 * 60 * 1000;
86675
88255
  function startCallbackServer(port) {
86676
- return new Promise((resolve11, reject) => {
88256
+ return new Promise((resolve13, reject) => {
86677
88257
  let timeoutId;
86678
88258
  const server2 = createServer2((request, response) => {
86679
88259
  clearTimeout(timeoutId);
@@ -86699,7 +88279,7 @@ function startCallbackServer(port) {
86699
88279
  response.writeHead(200, { "content-type": "text/html" });
86700
88280
  response.end("<html><body><h1>Authorization successful. You can close this tab.</h1></body></html>");
86701
88281
  server2.close();
86702
- resolve11({ code, state: state2 });
88282
+ resolve13({ code, state: state2 });
86703
88283
  });
86704
88284
  timeoutId = setTimeout(() => {
86705
88285
  server2.close();
@@ -87033,7 +88613,7 @@ function createMcpOAuthCommand() {
87033
88613
  }
87034
88614
 
87035
88615
  // src/cli/boulder/boulder.ts
87036
- import { existsSync as existsSync49 } from "fs";
88616
+ import { existsSync as existsSync52 } from "fs";
87037
88617
 
87038
88618
  // src/cli/boulder/formatter.ts
87039
88619
  var import_picocolors20 = __toESM(require_picocolors(), 1);
@@ -87170,10 +88750,10 @@ async function boulder(options) {
87170
88750
  const boulderFilePath = getBoulderFilePath(directory);
87171
88751
  const state2 = readBoulderState(directory);
87172
88752
  if (!state2) {
87173
- const message = existsSync49(boulderFilePath) ? formatReadErrorMessage(options.json) : formatNoBoulderMessage(options.json);
88753
+ const message = existsSync52(boulderFilePath) ? formatReadErrorMessage(options.json) : formatNoBoulderMessage(options.json);
87174
88754
  process.stderr.write(`${message}
87175
88755
  `);
87176
- return existsSync49(boulderFilePath) ? 2 : 1;
88756
+ return existsSync52(boulderFilePath) ? 2 : 1;
87177
88757
  }
87178
88758
  const works = getBoulderWorks(state2);
87179
88759
  const filteredWorks = options.workId ? works.filter((work) => work.work_id === options.workId) : works;
@@ -87189,11 +88769,86 @@ async function boulder(options) {
87189
88769
  `);
87190
88770
  return 0;
87191
88771
  }
88772
+ // src/cli/codex-ulw-loop.ts
88773
+ import { spawn as spawn3 } from "child_process";
88774
+ import { existsSync as existsSync53, readdirSync as readdirSync7, realpathSync as realpathSync7 } from "fs";
88775
+ import { homedir as homedir17 } from "os";
88776
+ import { join as join60 } from "path";
88777
+ function resolveCodexUlwLoopCommand(input = {}) {
88778
+ const env = input.env ?? process.env;
88779
+ const homeDir = input.homeDir ?? homedir17();
88780
+ const localBin = resolveLocalOmoBin(env, homeDir, input.currentExecutablePaths ?? [process.argv[1]].filter((value) => typeof value === "string"));
88781
+ if (localBin !== null)
88782
+ return { executable: localBin, argsPrefix: ["ulw-loop"] };
88783
+ const componentCli = resolveNewestCachedUlwLoopCli(env.CODEX_HOME ?? join60(homeDir, ".codex"));
88784
+ if (componentCli !== null)
88785
+ return { executable: process.execPath, argsPrefix: [componentCli] };
88786
+ return null;
88787
+ }
88788
+ async function codexUlwLoop(args) {
88789
+ const command = resolveCodexUlwLoopCommand();
88790
+ if (command === null) {
88791
+ console.error("Codex ulw-loop is not installed. Run: npx lazycodex-ai@latest install --no-tui");
88792
+ return 1;
88793
+ }
88794
+ return new Promise((resolve13) => {
88795
+ const child = spawn3(command.executable, [...command.argsPrefix, ...args], { stdio: "inherit" });
88796
+ child.on("error", (error51) => {
88797
+ console.error(error51.message);
88798
+ resolve13(1);
88799
+ });
88800
+ child.on("close", (code) => resolve13(code ?? 1));
88801
+ });
88802
+ }
88803
+ function resolveLocalOmoBin(env, homeDir, currentExecutablePaths) {
88804
+ const candidates = [
88805
+ env.CODEX_LOCAL_BIN_DIR ? join60(env.CODEX_LOCAL_BIN_DIR, "omo") : undefined,
88806
+ join60(homeDir, ".local", "bin", "omo"),
88807
+ join60(homeDir, ".codex", "bin", "omo")
88808
+ ].filter((value) => typeof value === "string");
88809
+ return candidates.find((candidate) => existsSync53(candidate) && !isCurrentExecutable(candidate, currentExecutablePaths)) ?? null;
88810
+ }
88811
+ function isCurrentExecutable(candidate, currentExecutablePaths) {
88812
+ const candidateRealPath = realpathOrSelf(candidate);
88813
+ return currentExecutablePaths.some((currentPath) => realpathOrSelf(currentPath) === candidateRealPath);
88814
+ }
88815
+ function realpathOrSelf(path16) {
88816
+ try {
88817
+ return realpathSync7(path16);
88818
+ } catch {
88819
+ return path16;
88820
+ }
88821
+ }
88822
+ function resolveNewestCachedUlwLoopCli(codexHome) {
88823
+ const versionsRoot = join60(codexHome, "plugins", "cache", "sisyphuslabs", "omo");
88824
+ if (!existsSync53(versionsRoot))
88825
+ return null;
88826
+ const versions2 = readdirSync7(versionsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort(compareVersionNames).reverse();
88827
+ for (const version3 of versions2) {
88828
+ const candidate = join60(versionsRoot, version3, "components", "ulw-loop", "dist", "cli.js");
88829
+ if (existsSync53(candidate))
88830
+ return candidate;
88831
+ }
88832
+ return null;
88833
+ }
88834
+ function compareVersionNames(left, right) {
88835
+ const leftParts = left.split(".").map((part) => Number.parseInt(part, 10));
88836
+ const rightParts = right.split(".").map((part) => Number.parseInt(part, 10));
88837
+ const length = Math.max(leftParts.length, rightParts.length);
88838
+ for (let index = 0;index < length; index += 1) {
88839
+ const leftValue = Number.isFinite(leftParts[index] ?? Number.NaN) ? leftParts[index] ?? 0 : 0;
88840
+ const rightValue = Number.isFinite(rightParts[index] ?? Number.NaN) ? rightParts[index] ?? 0 : 0;
88841
+ if (leftValue !== rightValue)
88842
+ return leftValue - rightValue;
88843
+ }
88844
+ return left.localeCompare(right);
88845
+ }
88846
+
87192
88847
  // src/cli/cli-program.ts
87193
88848
  var VERSION2 = package_default.version;
87194
88849
  var program2 = new Command;
87195
88850
  function resolveInstallArgs(options, invocationName = process.env.OMO_INVOCATION_NAME) {
87196
- const defaultPlatform = invocationName === "lazycodex" ? "codex" : undefined;
88851
+ const defaultPlatform = invocationName === "lazycodex" || invocationName === "lazycodex-ai" ? "codex" : undefined;
87197
88852
  return {
87198
88853
  tui: options.tui !== false,
87199
88854
  claude: options.claude,
@@ -87214,7 +88869,7 @@ program2.name("oh-my-opencode").description("The ultimate OpenCode plugin - mult
87214
88869
  program2.command("install").alias("setup").description("Install and configure oh-my-opencode with interactive setup").option("--no-tui", "Run in non-interactive mode (requires all options)").option("--claude <value>", "Claude subscription: no, yes, max20").option("--openai <value>", "OpenAI/ChatGPT subscription: no, yes (default: no)").option("--gemini <value>", "Gemini integration: no, yes").option("--copilot <value>", "GitHub Copilot subscription: no, yes").addOption(new Option("--platform <platform>", "Install target platform: opencode, codex, both").choices(["opencode", "codex", "both"])).option("--opencode-zen <value>", "OpenCode Zen access: no, yes (default: no)").option("--zai-coding-plan <value>", "Z.ai Coding Plan subscription: no, yes (default: no)").option("--kimi-for-coding <value>", "Kimi For Coding subscription: no, yes (default: no)").option("--opencode-go <value>", "OpenCode Go subscription: no, yes (default: no)").option("--vercel-ai-gateway <value>", "Vercel AI Gateway: no, yes (default: no)").option("--codex-autonomous", "Configure Codex with approval never, full filesystem access, and network enabled").option("--no-codex-autonomous", "Leave existing Codex permission settings unchanged").option("--skip-auth", "Skip authentication setup hints").addHelpText("after", `
87215
88870
  Examples:
87216
88871
  $ bunx oh-my-opencode install
87217
- $ bunx lazycodex install --no-tui
88872
+ $ npx lazycodex-ai install --no-tui
87218
88873
  $ bunx oh-my-opencode install --no-tui --platform=both --claude=max20 --openai=yes --gemini=yes --copilot=no
87219
88874
  $ omo install --platform=codex --codex-autonomous
87220
88875
  $ bunx oh-my-opencode install --no-tui --claude=no --gemini=no --copilot=yes --opencode-zen=yes
@@ -87234,6 +88889,7 @@ Model Providers (Priority: Native > Copilot > OpenCode Zen > Z.ai > Kimi > Verce
87234
88889
  const exitCode = await install(args);
87235
88890
  process.exit(exitCode);
87236
88891
  });
88892
+ configureCleanupCommand(program2);
87237
88893
  program2.command("run <message>").allowUnknownOption().passThroughOptions().description("Run opencode with todo/background task completion enforcement").option("-a, --agent <name>", "Agent to use (default: from CLI/env/config, fallback: Sisyphus)").option("-m, --model <provider/model>", "Model override (e.g., anthropic/claude-sonnet-4)").option("-d, --directory <path>", "Working directory").option("-p, --port <port>", "Server port (attaches if port already in use)", parseInt).option("--attach <url>", "Attach to existing opencode server URL").option("--on-complete <command>", "Shell command to run after completion").option("--json", "Output structured JSON result to stdout").option("--no-timestamp", "Disable timestamp prefix in run output").option("--verbose", "Show full event stream (default: messages/tools only)").option("--session-id <id>", "Resume existing session instead of creating new one").addHelpText("after", `
87238
88894
  Examples:
87239
88895
  $ bunx oh-my-opencode run "Fix the bug in index.ts"
@@ -87332,6 +88988,10 @@ program2.command("boulder").description("Show boulder progress, elapsed time, an
87332
88988
  });
87333
88989
  process.exit(exitCode);
87334
88990
  });
88991
+ program2.command("ulw-loop [args...]").allowUnknownOption().passThroughOptions().description("Run the Codex LazyCodex ulw-loop CLI").action(async (args = []) => {
88992
+ const exitCode = await codexUlwLoop(args);
88993
+ process.exit(exitCode);
88994
+ });
87335
88995
  program2.addCommand(createMcpOAuthCommand());
87336
88996
  function runCli() {
87337
88997
  program2.parse();