oh-my-opencode 4.5.12 → 4.6.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 (147) 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/dist/cli/cleanup-command.d.ts +4 -0
  28. package/dist/cli/cleanup.d.ts +11 -0
  29. package/dist/cli/cli-program.d.ts +2 -1
  30. package/dist/cli/index.js +1837 -450
  31. package/dist/cli/install-codex/codex-cache.d.ts +1 -0
  32. package/dist/cli/install-codex/codex-cleanup-config.d.ts +6 -0
  33. package/dist/cli/install-codex/codex-cleanup.d.ts +21 -0
  34. package/dist/cli/install-codex/codex-config-mcp.d.ts +1 -0
  35. package/dist/cli/install-codex/codex-config-permissions.d.ts +1 -0
  36. package/dist/cli/install-codex/codex-config-reasoning.d.ts +1 -0
  37. package/dist/cli/install-codex/codex-config-toml.d.ts +2 -1
  38. package/dist/cli/install-codex/codex-installation-detection.d.ts +36 -0
  39. package/dist/cli/install-codex/codex-package-layout.d.ts +1 -0
  40. package/dist/cli/install-codex/codex-project-local-cleanup-best-effort.d.ts +7 -0
  41. package/dist/cli/install-codex/codex-project-local-cleanup.d.ts +35 -0
  42. package/dist/cli/install-codex/git-bash.d.ts +35 -0
  43. package/dist/cli/install-codex/index.d.ts +4 -0
  44. package/dist/cli/install-codex/toml-section-editor.d.ts +2 -0
  45. package/dist/cli/install-codex/types.d.ts +20 -0
  46. package/dist/cli/run/event-state.d.ts +1 -0
  47. package/dist/cli/run/poll-for-completion.d.ts +1 -0
  48. package/dist/cli/run/prompt-start.d.ts +7 -0
  49. package/dist/cli/star-request.d.ts +9 -0
  50. package/dist/config/schema/hooks.d.ts +0 -1
  51. package/dist/create-hooks.d.ts +0 -1
  52. package/dist/features/builtin-skills/skills/debugging.d.ts +2 -0
  53. package/dist/features/builtin-skills/skills/index.d.ts +1 -0
  54. package/dist/hooks/index.d.ts +0 -1
  55. package/dist/index.js +267 -114
  56. package/dist/plugin/hooks/create-core-hooks.d.ts +0 -1
  57. package/dist/plugin/hooks/create-session-hooks.d.ts +1 -2
  58. package/dist/plugin/messages-transform.d.ts +8 -1
  59. package/dist/plugin/user-abort-interrupted-recovery-guard.d.ts +6 -0
  60. package/dist/shared/prompt-async-gate/recent-dispatches.d.ts +14 -0
  61. package/dist/shared/prompt-async-gate/semantic-dedupe.d.ts +7 -0
  62. package/dist/shared/prompt-async-gate/session-idle-dispatch.d.ts +1 -0
  63. package/dist/shared/prompt-async-gate/timing.d.ts +1 -0
  64. package/dist/shared/prompt-async-gate/types.d.ts +2 -0
  65. package/dist/shared/prompt-async-gate.d.ts +1 -1
  66. package/package.json +22 -17
  67. package/packages/git-bash-mcp/dist/cli.js +367 -0
  68. package/packages/omo-codex/plugin/.mcp.json +11 -0
  69. package/packages/omo-codex/plugin/components/comment-checker/README.md +1 -1
  70. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +29 -0
  71. package/packages/omo-codex/plugin/components/git-bash/package.json +23 -0
  72. package/packages/omo-codex/plugin/components/git-bash/src/cli.ts +33 -0
  73. package/packages/omo-codex/plugin/components/git-bash/src/codex-hook.ts +180 -0
  74. package/packages/omo-codex/plugin/components/git-bash/src/index.ts +10 -0
  75. package/packages/omo-codex/plugin/components/git-bash/test/codex-hook.test.ts +195 -0
  76. package/packages/omo-codex/plugin/components/git-bash/tsconfig.build.json +13 -0
  77. package/packages/omo-codex/plugin/components/git-bash/tsconfig.json +25 -0
  78. package/packages/omo-codex/plugin/components/lsp/README.md +1 -1
  79. package/packages/omo-codex/plugin/components/lsp/src/cli.ts +5 -5
  80. package/packages/omo-codex/plugin/components/lsp/src/codex-hook-cli.ts +33 -0
  81. package/packages/omo-codex/plugin/components/lsp/src/codex-hook.ts +19 -27
  82. package/packages/omo-codex/plugin/components/lsp/test/codex-hook-cli.test.ts +28 -0
  83. package/packages/omo-codex/plugin/components/lsp/test/codex-hook-errors.test.ts +55 -0
  84. package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +7 -5
  85. package/packages/omo-codex/plugin/components/rules/README.md +1 -1
  86. package/packages/omo-codex/plugin/components/rules/bundled-rules/windows-git-bash.md +10 -0
  87. package/packages/omo-codex/plugin/components/rules/test/package-smoke.test.ts +3 -1
  88. package/packages/omo-codex/plugin/components/rules/test/windows-git-bash-bundled-rule.test.ts +97 -0
  89. package/packages/omo-codex/plugin/components/start-work-continuation/directive.md +5 -4
  90. package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +22 -0
  91. package/packages/omo-codex/plugin/components/ultrawork/README.md +2 -2
  92. package/packages/omo-codex/plugin/components/ultrawork/agents/codex-ultrawork-reviewer.toml +1 -0
  93. package/packages/omo-codex/plugin/components/ultrawork/agents/librarian.toml +8 -7
  94. package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +2 -1
  95. package/packages/omo-codex/plugin/components/ultrawork/directive.md +31 -5
  96. package/packages/omo-codex/plugin/components/ultrawork/test/codex-hook.test.ts +27 -4
  97. package/packages/omo-codex/plugin/components/ultrawork/test/package-smoke.test.ts +25 -0
  98. package/packages/omo-codex/plugin/components/ulw-loop/README.md +1 -1
  99. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +27 -205
  100. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +230 -0
  101. package/packages/omo-codex/plugin/components/ulw-loop/test/package-smoke.test.ts +102 -5
  102. package/packages/omo-codex/plugin/hooks/hooks.json +24 -2
  103. package/packages/omo-codex/plugin/package-lock.json +19 -0
  104. package/packages/omo-codex/plugin/package.json +3 -1
  105. package/packages/omo-codex/plugin/scripts/build-bundled-mcp-runtimes.mjs +16 -1
  106. package/packages/omo-codex/plugin/scripts/build-components.mjs +2 -1
  107. package/packages/omo-codex/plugin/scripts/sync-hook-status-messages.mjs +87 -0
  108. package/packages/omo-codex/plugin/skills/review-work/SKILL.md +27 -2
  109. package/packages/omo-codex/plugin/skills/start-work/SKILL.md +20 -0
  110. package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +27 -205
  111. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +230 -0
  112. package/packages/omo-codex/plugin/test/aggregate.test.mjs +23 -8
  113. package/packages/omo-codex/plugin/test/hook-status-message.test.mjs +56 -11
  114. package/packages/omo-codex/plugin/test/install-time-build-runtime.test.mjs +34 -0
  115. package/packages/omo-codex/plugin/test/mcp-research-servers.test.mjs +21 -0
  116. package/packages/omo-codex/plugin/test/node-install-surface.test.mjs +48 -0
  117. package/packages/omo-codex/plugin/test/subagent-guidance.test.mjs +76 -0
  118. package/packages/omo-codex/plugin/test/sync-hook-status-messages.test.mjs +66 -0
  119. package/packages/omo-codex/plugin/test/sync-skills.test.mjs +32 -2
  120. package/packages/omo-codex/scripts/install/cache.mjs +5 -3
  121. package/packages/omo-codex/scripts/install/cli-args.mjs +112 -0
  122. package/packages/omo-codex/scripts/install/config.mjs +36 -1
  123. package/packages/omo-codex/scripts/install/delegated-command.mjs +25 -0
  124. package/packages/omo-codex/scripts/install/git-bash.mjs +99 -0
  125. package/packages/omo-codex/scripts/install/git-bash.test.mjs +174 -0
  126. package/packages/omo-codex/scripts/install/mcp-runtime-cache.mjs +5 -1
  127. package/packages/omo-codex/scripts/install/multi-agent-v2-config.mjs +7 -1
  128. package/packages/omo-codex/scripts/install/permissions.d.mts +1 -0
  129. package/packages/omo-codex/scripts/install/permissions.mjs +26 -0
  130. package/packages/omo-codex/scripts/install/project-local-cleanup.mjs +229 -0
  131. package/packages/omo-codex/scripts/install/reasoning-config.mjs +14 -0
  132. package/packages/omo-codex/scripts/install/source-package-build.mjs +20 -0
  133. package/packages/omo-codex/scripts/install/toml-editor.mjs +19 -2
  134. package/packages/omo-codex/scripts/install-cli-args.test.mjs +146 -0
  135. package/packages/omo-codex/scripts/install-config-autonomous.test.mjs +48 -0
  136. package/packages/omo-codex/scripts/install-config-reasoning.test.mjs +62 -0
  137. package/packages/omo-codex/scripts/install-config.test.mjs +206 -0
  138. package/packages/omo-codex/scripts/install-local-entrypoint.test.mjs +129 -0
  139. package/packages/omo-codex/scripts/install-local-git-bash-preflight.test.mjs +145 -0
  140. package/packages/omo-codex/scripts/install-local.mjs +91 -8
  141. package/packages/omo-codex/scripts/install-local.test.mjs +15 -0
  142. package/packages/omo-codex/scripts/install-mcp-runtime.test.mjs +60 -0
  143. package/packages/omo-codex/scripts/install-packaged-local.test.mjs +67 -0
  144. package/packages/omo-codex/scripts/install-project-local-cleanup.test.mjs +277 -0
  145. package/packages/shared-skills/skills/review-work/SKILL.md +27 -2
  146. package/packages/shared-skills/skills/start-work/SKILL.md +20 -0
  147. package/dist/hooks/context-window-monitor.d.ts +0 -19
package/dist/cli/index.js CHANGED
@@ -51711,7 +51711,7 @@ async function withDispatchTimeout(operation, dispatchTimeoutMs, operationName)
51711
51711
  }
51712
51712
  }
51713
51713
  }
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;
51714
+ 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
51715
 
51716
51716
  // src/shared/prompt-async-gate/pending-tool-turn.ts
51717
51717
  function getPromptQuery(input) {
@@ -51808,6 +51808,42 @@ var init_pending_tool_turn = __esm(() => {
51808
51808
  init_message_inspection_error();
51809
51809
  });
51810
51810
 
51811
+ // src/shared/prompt-async-gate/recent-dispatches.ts
51812
+ function recentDispatchKey(sessionID, dedupeKey) {
51813
+ return `${sessionID}\x00${dedupeKey}`;
51814
+ }
51815
+ function pruneRecentPromptDispatches(now = Date.now()) {
51816
+ for (const [key, dispatch] of recentPromptDispatches) {
51817
+ if (dispatch.expiresAt <= now) {
51818
+ recentPromptDispatches.delete(key);
51819
+ }
51820
+ }
51821
+ }
51822
+ function getRecentPromptDispatch(sessionID, dedupeKey) {
51823
+ pruneRecentPromptDispatches();
51824
+ return recentPromptDispatches.get(recentDispatchKey(sessionID, dedupeKey));
51825
+ }
51826
+ function rememberRecentPromptDispatch(args) {
51827
+ pruneRecentPromptDispatches();
51828
+ if (args.holdMs <= 0) {
51829
+ return;
51830
+ }
51831
+ recentPromptDispatches.set(recentDispatchKey(args.sessionID, args.dedupeKey), {
51832
+ source: args.source,
51833
+ expiresAt: Date.now() + args.holdMs
51834
+ });
51835
+ log("[prompt-async-gate] remembered semantic prompt dispatch", {
51836
+ sessionID: args.sessionID,
51837
+ source: args.source,
51838
+ holdMs: args.holdMs
51839
+ });
51840
+ }
51841
+ var recentPromptDispatches;
51842
+ var init_recent_dispatches = __esm(() => {
51843
+ init_logger();
51844
+ recentPromptDispatches = new Map;
51845
+ });
51846
+
51811
51847
  // src/shared/prompt-async-gate/reservations.ts
51812
51848
  function setExpiredReservationHandler(handler) {
51813
51849
  expiredReservationHandler = handler;
@@ -51869,6 +51905,7 @@ async function dispatchAfterSessionIdle(args) {
51869
51905
  dedupeKey,
51870
51906
  settleMs,
51871
51907
  postDispatchHoldMs,
51908
+ semanticDedupeHoldMs,
51872
51909
  dispatchTimeoutMs,
51873
51910
  checkStatus,
51874
51911
  checkToolState,
@@ -51926,10 +51963,25 @@ async function dispatchAfterSessionIdle(args) {
51926
51963
  log(`[prompt-async-gate] ${sessionName} dispatching`, { sessionID, source });
51927
51964
  dispatchAttempted = true;
51928
51965
  const response = await withDispatchTimeout(dispatch(input), dispatchTimeoutMs, `[prompt-async-gate] ${sessionName} dispatch`);
51966
+ rememberRecentPromptDispatch({
51967
+ sessionID,
51968
+ dedupeKey,
51969
+ source,
51970
+ holdMs: semanticDedupeHoldMs
51971
+ });
51929
51972
  log(`[prompt-async-gate] ${sessionName} dispatched`, { sessionID, source });
51930
51973
  return { status: "dispatched", response };
51931
51974
  } catch (error) {
51932
- log(`[prompt-async-gate] ${sessionName} failed`, { sessionID, source, error: String(error) });
51975
+ if (dispatchAttempted) {
51976
+ rememberRecentPromptDispatch({
51977
+ sessionID,
51978
+ dedupeKey,
51979
+ source,
51980
+ holdMs: semanticDedupeHoldMs
51981
+ });
51982
+ }
51983
+ const errorText = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
51984
+ log(`[prompt-async-gate] ${sessionName} failed`, { sessionID, source, error: errorText });
51933
51985
  return { status: "failed", error, dispatchAttempted };
51934
51986
  } finally {
51935
51987
  finishPromptReservation(sessionID, reservation, dispatchAttempted, postDispatchHoldMs);
@@ -51939,6 +51991,7 @@ var init_session_idle_dispatch = __esm(() => {
51939
51991
  init_logger();
51940
51992
  init_session_idle_settle();
51941
51993
  init_pending_tool_turn();
51994
+ init_recent_dispatches();
51942
51995
  init_reservations();
51943
51996
  });
51944
51997
 
@@ -52038,6 +52091,7 @@ async function drainPromptQueue(sessionID, awaitedEntry) {
52038
52091
  dedupeKey: entry.dedupeKey,
52039
52092
  settleMs: entry.settleMs,
52040
52093
  postDispatchHoldMs: entry.postDispatchHoldMs,
52094
+ semanticDedupeHoldMs: entry.semanticDedupeHoldMs,
52041
52095
  dispatchTimeoutMs: entry.dispatchTimeoutMs,
52042
52096
  checkStatus: entry.checkStatus,
52043
52097
  checkToolState: entry.checkToolState,
@@ -52121,27 +52175,119 @@ var init_queue = __esm(() => {
52121
52175
  });
52122
52176
  });
52123
52177
 
52124
- // src/shared/prompt-async-gate.ts
52178
+ // src/shared/prompt-async-gate/semantic-dedupe.ts
52179
+ import { createHash } from "crypto";
52180
+ function isPlainRecord(value) {
52181
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
52182
+ return false;
52183
+ }
52184
+ const prototype = Object.getPrototypeOf(value);
52185
+ return prototype === Object.prototype || prototype === null;
52186
+ }
52187
+ function canonicalizePromptInputForDedupe(key, value, seen = new WeakSet, depth = 0) {
52188
+ if (key === "signal") {
52189
+ return "[AbortSignal]";
52190
+ }
52191
+ if (typeof value === "function") {
52192
+ return `[Function:${value.name}]`;
52193
+ }
52194
+ if (depth > MAX_PROMPT_DEDUPE_DEPTH) {
52195
+ return "[MaxDepth]";
52196
+ }
52197
+ if (Array.isArray(value)) {
52198
+ if (seen.has(value)) {
52199
+ return "[Circular]";
52200
+ }
52201
+ seen.add(value);
52202
+ try {
52203
+ return value.map((entry) => canonicalizePromptInputForDedupe("", entry, seen, depth + 1));
52204
+ } finally {
52205
+ seen.delete(value);
52206
+ }
52207
+ }
52208
+ if (!isPlainRecord(value)) {
52209
+ return value;
52210
+ }
52211
+ if (seen.has(value)) {
52212
+ return "[Circular]";
52213
+ }
52214
+ seen.add(value);
52215
+ try {
52216
+ const canonicalEntries = [];
52217
+ for (const entryKey of Object.keys(value).sort()) {
52218
+ canonicalEntries.push([
52219
+ entryKey,
52220
+ canonicalizePromptInputForDedupe(entryKey, value[entryKey], seen, depth + 1)
52221
+ ]);
52222
+ }
52223
+ return Object.fromEntries(canonicalEntries);
52224
+ } finally {
52225
+ seen.delete(value);
52226
+ }
52227
+ }
52228
+ function isContinuationTextPartLike(value) {
52229
+ if (!isPlainRecord(value)) {
52230
+ return false;
52231
+ }
52232
+ if (value.type !== "text" || typeof value.text !== "string") {
52233
+ return false;
52234
+ }
52235
+ if (!hasInternalInitiatorMarker(value.text)) {
52236
+ return false;
52237
+ }
52238
+ if (!isPlainRecord(value.metadata)) {
52239
+ return false;
52240
+ }
52241
+ return value.metadata.compaction_continue === true;
52242
+ }
52243
+ function hasContinuationPromptIntent(input) {
52244
+ if (!isPlainRecord(input) || !isPlainRecord(input.body) || !Array.isArray(input.body.parts)) {
52245
+ return false;
52246
+ }
52247
+ return input.body.parts.some((part) => isContinuationTextPartLike(part));
52248
+ }
52249
+ function normalizePromptInputForSemanticDedupe(input) {
52250
+ if (hasContinuationPromptIntent(input)) {
52251
+ return {
52252
+ __omo_internal_intent: "continuation"
52253
+ };
52254
+ }
52255
+ return canonicalizePromptInputForDedupe("", input);
52256
+ }
52125
52257
  function stringifyPromptInputForDedupe(input) {
52126
52258
  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
- });
52259
+ const serialized = JSON.stringify(normalizePromptInputForSemanticDedupe(input));
52136
52260
  return serialized ?? String(input);
52137
- } catch {
52138
- return String(input);
52261
+ } catch (error) {
52262
+ const errorTag = error instanceof Error ? error.name : String(error);
52263
+ return `${String(input)}:[unserializable:${errorTag}]`;
52139
52264
  }
52140
52265
  }
52141
- function createDefaultDedupeKey(source, input) {
52266
+ function createSemanticPromptDedupeKey(input) {
52142
52267
  const fingerprint = stringifyPromptInputForDedupe(input);
52143
- return `${source}:${fingerprint.length}:${fingerprint.slice(0, 8192)}`;
52268
+ const digest = createHash("sha256").update(fingerprint, "utf8").digest("hex");
52269
+ return `semantic:${digest}`;
52270
+ }
52271
+ function coalesceRecentSemanticPromptDispatch(args) {
52272
+ const recentDispatch = getRecentPromptDispatch(args.sessionID, args.dedupeKey);
52273
+ if (!recentDispatch) {
52274
+ return;
52275
+ }
52276
+ log("[prompt-async-gate] prompt coalesced with recent semantic dispatch", {
52277
+ sessionID: args.sessionID,
52278
+ source: args.source,
52279
+ queuedBy: recentDispatch.source
52280
+ });
52281
+ return { status: "queued", queuedBy: recentDispatch.source, position: 0 };
52144
52282
  }
52283
+ var MAX_PROMPT_DEDUPE_DEPTH = 64;
52284
+ var init_semantic_dedupe = __esm(() => {
52285
+ init_internal_initiator_marker();
52286
+ init_logger();
52287
+ init_recent_dispatches();
52288
+ });
52289
+
52290
+ // src/shared/prompt-async-gate.ts
52145
52291
  function hasObjectSessionPath(input) {
52146
52292
  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
52293
  }
@@ -52171,9 +52317,10 @@ async function dispatchInternalPrompt(args) {
52171
52317
  source,
52172
52318
  settleMs = DEFAULT_SESSION_IDLE_SETTLE_MS
52173
52319
  } = args;
52174
- const dedupeKey = args.dedupeKey ?? createDefaultDedupeKey(source, input);
52320
+ const dedupeKey = args.dedupeKey ?? createSemanticPromptDedupeKey(input);
52175
52321
  const queueRetryMs = args.queueRetryMs ?? DEFAULT_PROMPT_QUEUE_RETRY_MS;
52176
52322
  const postDispatchHoldMs = args.postDispatchHoldMs ?? DEFAULT_PROMPT_ASYNC_POST_DISPATCH_HOLD_MS;
52323
+ const semanticDedupeHoldMs = args.semanticDedupeHoldMs ?? (postDispatchHoldMs > 0 ? DEFAULT_PROMPT_SEMANTIC_DEDUPE_HOLD_MS : 0);
52177
52324
  const dispatchTimeoutMs = args.dispatchTimeoutMs ?? DEFAULT_PROMPT_DISPATCH_TIMEOUT_MS;
52178
52325
  const sessionName = args.mode === "async" ? "promptAsync" : "prompt";
52179
52326
  const dispatch = (() => {
@@ -52206,6 +52353,10 @@ async function dispatchInternalPrompt(args) {
52206
52353
  if (queuedBy !== undefined || isPromptQueueDraining(sessionID)) {
52207
52354
  return { status: "reserved", reservedBy: queuedBy ?? source };
52208
52355
  }
52356
+ const recentDispatchResult2 = coalesceRecentSemanticPromptDispatch({ sessionID, dedupeKey, source });
52357
+ if (recentDispatchResult2) {
52358
+ return recentDispatchResult2;
52359
+ }
52209
52360
  return dispatchAfterSessionIdle({
52210
52361
  sessionName,
52211
52362
  client,
@@ -52215,6 +52366,7 @@ async function dispatchInternalPrompt(args) {
52215
52366
  dedupeKey,
52216
52367
  settleMs,
52217
52368
  postDispatchHoldMs,
52369
+ semanticDedupeHoldMs,
52218
52370
  dispatchTimeoutMs,
52219
52371
  checkStatus: args.checkStatus !== false,
52220
52372
  checkToolState: args.checkToolState !== false,
@@ -52222,6 +52374,10 @@ async function dispatchInternalPrompt(args) {
52222
52374
  });
52223
52375
  }
52224
52376
  if (args.queue !== false) {
52377
+ const recentDispatchResult2 = coalesceRecentSemanticPromptDispatch({ sessionID, dedupeKey, source });
52378
+ if (recentDispatchResult2) {
52379
+ return recentDispatchResult2;
52380
+ }
52225
52381
  return enqueueInternalPrompt({
52226
52382
  id: nextPromptQueueID(),
52227
52383
  sessionID,
@@ -52232,6 +52388,7 @@ async function dispatchInternalPrompt(args) {
52232
52388
  dedupeKey,
52233
52389
  settleMs,
52234
52390
  postDispatchHoldMs,
52391
+ semanticDedupeHoldMs,
52235
52392
  dispatchTimeoutMs,
52236
52393
  queueRetryMs,
52237
52394
  checkStatus: args.checkStatus !== false,
@@ -52239,6 +52396,10 @@ async function dispatchInternalPrompt(args) {
52239
52396
  dispatch: async (_dispatchInput) => dispatchWithPathCompatibility(dispatch, input)
52240
52397
  });
52241
52398
  }
52399
+ const recentDispatchResult = coalesceRecentSemanticPromptDispatch({ sessionID, dedupeKey, source });
52400
+ if (recentDispatchResult) {
52401
+ return recentDispatchResult;
52402
+ }
52242
52403
  return dispatchAfterSessionIdle({
52243
52404
  sessionName,
52244
52405
  client,
@@ -52248,6 +52409,7 @@ async function dispatchInternalPrompt(args) {
52248
52409
  dedupeKey,
52249
52410
  settleMs,
52250
52411
  postDispatchHoldMs,
52412
+ semanticDedupeHoldMs,
52251
52413
  dispatchTimeoutMs,
52252
52414
  checkStatus: args.checkStatus !== false,
52253
52415
  checkToolState: args.checkToolState !== false,
@@ -52261,8 +52423,10 @@ var init_prompt_async_gate = __esm(() => {
52261
52423
  init_logger();
52262
52424
  init_session_idle_settle();
52263
52425
  init_queue();
52426
+ init_recent_dispatches();
52264
52427
  init_reservations();
52265
52428
  init_session_idle_dispatch();
52429
+ init_semantic_dedupe();
52266
52430
  });
52267
52431
 
52268
52432
  // src/shared/prompt-failure-classifier.ts
@@ -54105,7 +54269,7 @@ var init_config_manager = __esm(() => {
54105
54269
  });
54106
54270
 
54107
54271
  // 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";
54272
+ import { dirname as dirname14, posix, sep as sep3 } from "path";
54109
54273
  function createModulerModifier() {
54110
54274
  const getModuleFromFileName = createGetModuleFromFilename();
54111
54275
  return async (frames) => {
@@ -54114,7 +54278,7 @@ function createModulerModifier() {
54114
54278
  return frames;
54115
54279
  };
54116
54280
  }
54117
- function createGetModuleFromFilename(basePath = process.argv[1] ? dirname13(process.argv[1]) : process.cwd(), isWindows = sep3 === "\\") {
54281
+ function createGetModuleFromFilename(basePath = process.argv[1] ? dirname14(process.argv[1]) : process.cwd(), isWindows = sep3 === "\\") {
54118
54282
  const normalizedBase = isWindows ? normalizeWindowsPath(basePath) : basePath;
54119
54283
  return (filename) => {
54120
54284
  if (!filename)
@@ -56919,14 +57083,14 @@ async function addSourceContext(frames) {
56919
57083
  return frames;
56920
57084
  }
56921
57085
  function getContextLinesFromFile(path6, ranges, output) {
56922
- return new Promise((resolve8) => {
57086
+ return new Promise((resolve9) => {
56923
57087
  const stream = createReadStream(path6);
56924
57088
  const lineReaded = createInterface({
56925
57089
  input: stream
56926
57090
  });
56927
57091
  function destroyStreamAndResolve() {
56928
57092
  stream.destroy();
56929
- resolve8();
57093
+ resolve9();
56930
57094
  }
56931
57095
  let lineNumber = 0;
56932
57096
  let currentRangeIndex = 0;
@@ -58398,9 +58562,9 @@ var init_client = __esm(() => {
58398
58562
  if (this.disabled || this.optedOut)
58399
58563
  return;
58400
58564
  if (!this._waitUntilCycle) {
58401
- let resolve8;
58565
+ let resolve9;
58402
58566
  const promise = new Promise((r) => {
58403
- resolve8 = r;
58567
+ resolve9 = r;
58404
58568
  });
58405
58569
  try {
58406
58570
  waitUntil(promise);
@@ -58408,7 +58572,7 @@ var init_client = __esm(() => {
58408
58572
  return;
58409
58573
  }
58410
58574
  this._waitUntilCycle = {
58411
- resolve: resolve8,
58575
+ resolve: resolve9,
58412
58576
  startedAt: Date.now(),
58413
58577
  timer: undefined
58414
58578
  };
@@ -58434,11 +58598,11 @@ var init_client = __esm(() => {
58434
58598
  return cycle?.resolve;
58435
58599
  }
58436
58600
  async resolveWaitUntilFlush() {
58437
- const resolve8 = this._consumeWaitUntilCycle();
58601
+ const resolve9 = this._consumeWaitUntilCycle();
58438
58602
  try {
58439
58603
  await super.flush();
58440
58604
  } catch {} finally {
58441
- resolve8?.();
58605
+ resolve9?.();
58442
58606
  }
58443
58607
  }
58444
58608
  getPersistedProperty(key) {
@@ -58538,15 +58702,15 @@ var init_client = __esm(() => {
58538
58702
  return true;
58539
58703
  if (this.featureFlagsPoller === undefined)
58540
58704
  return false;
58541
- return new Promise((resolve8) => {
58705
+ return new Promise((resolve9) => {
58542
58706
  const timeout = setTimeout(() => {
58543
58707
  cleanup();
58544
- resolve8(false);
58708
+ resolve9(false);
58545
58709
  }, timeoutMs);
58546
58710
  const cleanup = this._events.on("localEvaluationFlagsLoaded", (count) => {
58547
58711
  clearTimeout(timeout);
58548
58712
  cleanup();
58549
- resolve8(count > 0);
58713
+ resolve9(count > 0);
58550
58714
  });
58551
58715
  });
58552
58716
  }
@@ -59033,13 +59197,13 @@ var init_client = __esm(() => {
59033
59197
  this.context?.enter(data, options);
59034
59198
  }
59035
59199
  async _shutdown(shutdownTimeoutMs) {
59036
- const resolve8 = this._consumeWaitUntilCycle();
59200
+ const resolve9 = this._consumeWaitUntilCycle();
59037
59201
  await this.featureFlagsPoller?.stopPoller(shutdownTimeoutMs);
59038
59202
  this.errorTracking.shutdown();
59039
59203
  try {
59040
59204
  return await super._shutdown(shutdownTimeoutMs);
59041
59205
  } finally {
59042
- resolve8?.();
59206
+ resolve9?.();
59043
59207
  }
59044
59208
  }
59045
59209
  async _requestRemoteConfigPayload(flagKey) {
@@ -59435,7 +59599,10 @@ var init_package = __esm(() => {
59435
59599
  types: "./index.d.ts",
59436
59600
  import: "./src/index.ts"
59437
59601
  },
59438
- "./telemetry": "./src/telemetry/index.ts",
59602
+ "./telemetry": {
59603
+ types: "./src/telemetry/index.ts",
59604
+ import: "./src/telemetry/index.ts"
59605
+ },
59439
59606
  "./marketplace.json": "./marketplace.json"
59440
59607
  },
59441
59608
  types: "./index.d.ts",
@@ -59543,10 +59710,10 @@ var init_data_path2 = __esm(() => {
59543
59710
  });
59544
59711
 
59545
59712
  // 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";
59713
+ import { existsSync as existsSync24, mkdirSync as mkdirSync8, readFileSync as readFileSync13 } from "fs";
59714
+ import { join as join29 } from "path";
59548
59715
  function getPostHogActivityStateFilePath() {
59549
- return join27(getActivityStateDir(), POSTHOG_ACTIVITY_STATE_FILE);
59716
+ return join29(getActivityStateDir(), POSTHOG_ACTIVITY_STATE_FILE);
59550
59717
  }
59551
59718
  function getUtcDayString(date) {
59552
59719
  return date.toISOString().slice(0, 10);
@@ -59556,7 +59723,7 @@ function isPostHogActivityState(value) {
59556
59723
  }
59557
59724
  function readPostHogActivityState() {
59558
59725
  const stateFilePath = getPostHogActivityStateFilePath();
59559
- if (!existsSync22(stateFilePath)) {
59726
+ if (!existsSync24(stateFilePath)) {
59560
59727
  return {};
59561
59728
  }
59562
59729
  try {
@@ -59603,7 +59770,7 @@ var init_posthog_activity_state = __esm(() => {
59603
59770
  });
59604
59771
 
59605
59772
  // packages/omo-codex/src/telemetry/posthog.ts
59606
- import { createHash as createHash2 } from "crypto";
59773
+ import { createHash as createHash3 } from "crypto";
59607
59774
  import os4 from "os";
59608
59775
  function resolveOsProvider() {
59609
59776
  return osProviderOverride2 ?? os4;
@@ -59686,7 +59853,7 @@ function createPostHogClient(source, options) {
59686
59853
  };
59687
59854
  }
59688
59855
  function getPostHogDistinctId() {
59689
- return createHash2("sha256").update(`omo-codex:${resolveOsProvider().hostname()}`).digest("hex");
59856
+ return createHash3("sha256").update(`omo-codex:${resolveOsProvider().hostname()}`).digest("hex");
59690
59857
  }
59691
59858
  function createCliPostHog() {
59692
59859
  return createPostHogClient("cli", {
@@ -59910,12 +60077,12 @@ var require_isexe = __commonJS((exports2, module) => {
59910
60077
  if (typeof Promise !== "function") {
59911
60078
  throw new TypeError("callback not provided");
59912
60079
  }
59913
- return new Promise(function(resolve9, reject) {
60080
+ return new Promise(function(resolve11, reject) {
59914
60081
  isexe(path8, options || {}, function(er, is) {
59915
60082
  if (er) {
59916
60083
  reject(er);
59917
60084
  } else {
59918
- resolve9(is);
60085
+ resolve11(is);
59919
60086
  }
59920
60087
  });
59921
60088
  });
@@ -59977,27 +60144,27 @@ var require_which = __commonJS((exports2, module) => {
59977
60144
  opt = {};
59978
60145
  const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
59979
60146
  const found = [];
59980
- const step = (i2) => new Promise((resolve9, reject) => {
60147
+ const step = (i2) => new Promise((resolve11, reject) => {
59981
60148
  if (i2 === pathEnv.length)
59982
- return opt.all && found.length ? resolve9(found) : reject(getNotFoundError(cmd));
60149
+ return opt.all && found.length ? resolve11(found) : reject(getNotFoundError(cmd));
59983
60150
  const ppRaw = pathEnv[i2];
59984
60151
  const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
59985
60152
  const pCmd = path8.join(pathPart, cmd);
59986
60153
  const p2 = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
59987
- resolve9(subStep(p2, i2, 0));
60154
+ resolve11(subStep(p2, i2, 0));
59988
60155
  });
59989
- const subStep = (p2, i2, ii) => new Promise((resolve9, reject) => {
60156
+ const subStep = (p2, i2, ii) => new Promise((resolve11, reject) => {
59990
60157
  if (ii === pathExt.length)
59991
- return resolve9(step(i2 + 1));
60158
+ return resolve11(step(i2 + 1));
59992
60159
  const ext = pathExt[ii];
59993
60160
  isexe(p2 + ext, { pathExt: pathExtExe }, (er, is) => {
59994
60161
  if (!er && is) {
59995
60162
  if (opt.all)
59996
60163
  found.push(p2 + ext);
59997
60164
  else
59998
- return resolve9(p2 + ext);
60165
+ return resolve11(p2 + ext);
59999
60166
  }
60000
- return resolve9(subStep(p2, i2, ii + 1));
60167
+ return resolve11(subStep(p2, i2, ii + 1));
60001
60168
  });
60002
60169
  });
60003
60170
  return cb ? step(0).then((res) => cb(null, res), cb) : step(0);
@@ -60894,8 +61061,8 @@ var init_update_toasts = __esm(() => {
60894
61061
  });
60895
61062
 
60896
61063
  // src/hooks/auto-update-checker/hook/background-update-check.ts
60897
- import { existsSync as existsSync38 } from "fs";
60898
- import { join as join41 } from "path";
61064
+ import { existsSync as existsSync41 } from "fs";
61065
+ import { join as join46 } from "path";
60899
61066
  function getCacheWorkspaceDir(deps) {
60900
61067
  return deps.join(deps.getOpenCodeCacheDir(), "packages");
60901
61068
  }
@@ -61013,8 +61180,8 @@ var init_background_update_check = __esm(() => {
61013
61180
  init_checker();
61014
61181
  init_update_toasts();
61015
61182
  defaultDeps3 = {
61016
- existsSync: existsSync38,
61017
- join: join41,
61183
+ existsSync: existsSync41,
61184
+ join: join46,
61018
61185
  runBunInstallWithDetails,
61019
61186
  log,
61020
61187
  getOpenCodeCacheDir,
@@ -61173,7 +61340,7 @@ async function showSpinnerToast(ctx, version3, message) {
61173
61340
  duration: frameInterval + 50
61174
61341
  }
61175
61342
  }).catch(() => {});
61176
- await new Promise((resolve10) => setTimeout(resolve10, frameInterval));
61343
+ await new Promise((resolve12) => setTimeout(resolve12, frameInterval));
61177
61344
  }
61178
61345
  }
61179
61346
  var SISYPHUS_SPINNER;
@@ -61256,13 +61423,13 @@ v${latestVersion} available. Restart OpenCode to apply.` : "OpenCode is now on S
61256
61423
  }
61257
61424
  };
61258
61425
  }
61259
- var defaultDeps4, isRecord13 = (value) => {
61426
+ var defaultDeps4, isRecord15 = (value) => {
61260
61427
  return typeof value === "object" && value !== null;
61261
61428
  }, getParentID = (properties) => {
61262
- if (!isRecord13(properties))
61429
+ if (!isRecord15(properties))
61263
61430
  return;
61264
61431
  const { info } = properties;
61265
- if (!isRecord13(info))
61432
+ if (!isRecord15(info))
61266
61433
  return;
61267
61434
  const { parentID } = info;
61268
61435
  return typeof parentID === "string" && parentID.length > 0 ? parentID : undefined;
@@ -61326,7 +61493,7 @@ var {
61326
61493
  // package.json
61327
61494
  var package_default = {
61328
61495
  name: "oh-my-opencode",
61329
- version: "4.5.12",
61496
+ version: "4.6.0",
61330
61497
  description: "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
61331
61498
  main: "./dist/index.js",
61332
61499
  types: "dist/index.d.ts",
@@ -61335,6 +61502,7 @@ var package_default = {
61335
61502
  "packages/rules-engine",
61336
61503
  "packages/ast-grep-core",
61337
61504
  "packages/ast-grep-mcp",
61505
+ "packages/git-bash-mcp",
61338
61506
  "packages/utils",
61339
61507
  "packages/model-core",
61340
61508
  "packages/prompts-core",
@@ -61349,7 +61517,8 @@ var package_default = {
61349
61517
  "oh-my-opencode": "bin/oh-my-opencode.js",
61350
61518
  "oh-my-openagent": "bin/oh-my-opencode.js",
61351
61519
  omo: "bin/oh-my-opencode.js",
61352
- lazycodex: "bin/oh-my-opencode.js"
61520
+ lazycodex: "bin/oh-my-opencode.js",
61521
+ "lazycodex-ai": "bin/oh-my-opencode.js"
61353
61522
  },
61354
61523
  files: [
61355
61524
  "dist",
@@ -61361,6 +61530,7 @@ var package_default = {
61361
61530
  ".agents/skills",
61362
61531
  "packages/lsp-tools-mcp/dist",
61363
61532
  "packages/ast-grep-mcp/dist",
61533
+ "packages/git-bash-mcp/dist",
61364
61534
  "packages/shared-skills/package.json",
61365
61535
  "packages/shared-skills/index.mjs",
61366
61536
  "packages/shared-skills/skills",
@@ -61378,7 +61548,7 @@ var package_default = {
61378
61548
  "./schema.json": "./dist/oh-my-opencode.schema.json"
61379
61549
  },
61380
61550
  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",
61551
+ 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
61552
  "build:lsp-tools-mcp": "npm --prefix packages/lsp-tools-mcp ci && npm --prefix packages/lsp-tools-mcp run build",
61383
61553
  "build:node-require-shim": "bun run script/patch-node-require-shim.ts",
61384
61554
  "build:all": "bun run build && bun run build:binaries",
@@ -61391,12 +61561,13 @@ var package_default = {
61391
61561
  prepublishOnly: "bun run clean && bun run build:lsp-tools-mcp && bun run build",
61392
61562
  "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
61563
  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",
61564
+ "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
61565
  "typecheck:script": "tsgo --noEmit -p script/tsconfig.json",
61396
61566
  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",
61567
+ "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
61568
  "test:windows-codex": "bun run test:codex",
61399
- "build:ast-grep-mcp": "bun run --cwd packages/ast-grep-mcp build"
61569
+ "build:ast-grep-mcp": "bun run --cwd packages/ast-grep-mcp build",
61570
+ "build:git-bash-mcp": "bun run --cwd packages/git-bash-mcp build"
61400
61571
  },
61401
61572
  keywords: [
61402
61573
  "opencode",
@@ -61439,6 +61610,7 @@ var package_default = {
61439
61610
  devDependencies: {
61440
61611
  "@oh-my-opencode/ast-grep-core": "workspace:*",
61441
61612
  "@oh-my-opencode/ast-grep-mcp": "workspace:*",
61613
+ "@oh-my-opencode/git-bash-mcp": "workspace:*",
61442
61614
  "@oh-my-opencode/agents-md-core": "workspace:*",
61443
61615
  "@oh-my-opencode/boulder-state": "workspace:*",
61444
61616
  "@oh-my-opencode/comment-checker-core": "workspace:*",
@@ -61457,17 +61629,17 @@ var package_default = {
61457
61629
  zod: "^4.4.3"
61458
61630
  },
61459
61631
  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"
61632
+ "oh-my-opencode-darwin-arm64": "4.6.0",
61633
+ "oh-my-opencode-darwin-x64": "4.6.0",
61634
+ "oh-my-opencode-darwin-x64-baseline": "4.6.0",
61635
+ "oh-my-opencode-linux-arm64": "4.6.0",
61636
+ "oh-my-opencode-linux-arm64-musl": "4.6.0",
61637
+ "oh-my-opencode-linux-x64": "4.6.0",
61638
+ "oh-my-opencode-linux-x64-baseline": "4.6.0",
61639
+ "oh-my-opencode-linux-x64-musl": "4.6.0",
61640
+ "oh-my-opencode-linux-x64-musl-baseline": "4.6.0",
61641
+ "oh-my-opencode-windows-x64": "4.6.0",
61642
+ "oh-my-opencode-windows-x64-baseline": "4.6.0"
61471
61643
  },
61472
61644
  overrides: {
61473
61645
  hono: "^4.12.18",
@@ -61490,6 +61662,7 @@ var package_default = {
61490
61662
  init_shared();
61491
61663
  init_config_manager();
61492
61664
  var import_picocolors3 = __toESM(require_picocolors(), 1);
61665
+ import { createInterface as createInterface2 } from "readline/promises";
61493
61666
 
61494
61667
  // src/cli/install-validators.ts
61495
61668
  var import_picocolors = __toESM(require_picocolors(), 1);
@@ -61517,6 +61690,9 @@ function formatConfigSummary(config) {
61517
61690
  if (config.hasCodex) {
61518
61691
  lines.push(` ${SYMBOLS.info} Codex autonomous mode: ${config.codexAutonomous ? "enabled" : "disabled"}`);
61519
61692
  }
61693
+ if (!config.hasOpenCode)
61694
+ return lines.join(`
61695
+ `);
61520
61696
  lines.push("");
61521
61697
  const claudeDetail = config.hasClaude ? config.isMax20 ? "max20" : "standard" : undefined;
61522
61698
  lines.push(formatProvider("Claude", config.hasClaude, claudeDetail));
@@ -61742,9 +61918,9 @@ function getUnsupportedOpenCodeVersionMessage(openCodeVersion) {
61742
61918
 
61743
61919
  // src/cli/install-codex/install-codex.ts
61744
61920
  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";
61921
+ import { join as join30, resolve as resolve9 } from "path";
61922
+ import { existsSync as existsSync25 } from "fs";
61923
+ import { mkdir as mkdir6, writeFile as writeFile7 } from "fs/promises";
61748
61924
 
61749
61925
  // src/cli/install-codex/codex-cache.ts
61750
61926
  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 +61937,13 @@ var BUNDLED_MCP_RUNTIMES = [
61761
61937
  destinationArg: "./components/ast-grep-mcp/dist/cli.js",
61762
61938
  destinationDistFromPlugin: "components/ast-grep-mcp/dist"
61763
61939
  },
61940
+ {
61941
+ label: "Git Bash MCP",
61942
+ sourceArg: "../../git-bash-mcp/dist/cli.js",
61943
+ sourceDistFromPlugin: "../../git-bash-mcp/dist",
61944
+ destinationArg: "./components/git-bash-mcp/dist/cli.js",
61945
+ destinationDistFromPlugin: "components/git-bash-mcp/dist"
61946
+ },
61764
61947
  {
61765
61948
  label: "LSP MCP",
61766
61949
  sourceArg: "../../lsp-tools-mcp/dist/cli.js",
@@ -61956,8 +62139,10 @@ function isRecord9(value) {
61956
62139
 
61957
62140
  // src/cli/install-codex/codex-cache.ts
61958
62141
  async function installCachedPlugin(input) {
61959
- await maybeRunNpmInstall(input.sourcePath, input.runCommand);
61960
- await maybeRunNpmBuild(input.sourcePath, input.runCommand);
62142
+ if (input.buildSource !== false) {
62143
+ await maybeRunNpmInstall(input.sourcePath, input.runCommand);
62144
+ await maybeRunNpmBuild(input.sourcePath, input.runCommand);
62145
+ }
61961
62146
  const targetPath = join22(input.codexHome, "plugins", "cache", input.marketplaceName, input.name, input.version);
61962
62147
  await replaceDirectory(input.sourcePath, targetPath);
61963
62148
  await rewriteCachedPackageLocalFileDependencies(targetPath, input.sourcePath);
@@ -62178,8 +62363,35 @@ function isRecord10(value) {
62178
62363
  return typeof value === "object" && value !== null && !Array.isArray(value);
62179
62364
  }
62180
62365
 
62366
+ // src/cli/install-codex/codex-package-layout.ts
62367
+ import { existsSync as existsSync22 } from "fs";
62368
+ import { readFile as readFile5 } from "fs/promises";
62369
+ import { join as join23 } from "path";
62370
+ var PACKAGED_CODEX_INSTALLER_NAMES = new Set([
62371
+ "@code-yeongyu/lazycodex",
62372
+ "@code-yeongyu/lazycodex-ai",
62373
+ "lazycodex",
62374
+ "lazycodex-ai",
62375
+ "oh-my-opencode",
62376
+ "oh-my-openagent"
62377
+ ]);
62378
+ async function shouldBuildSourcePackages(repoRoot) {
62379
+ if (existsSync22(join23(repoRoot, "src", "index.ts")))
62380
+ return true;
62381
+ const packageJsonPath = join23(repoRoot, "package.json");
62382
+ if (!existsSync22(packageJsonPath))
62383
+ return true;
62384
+ const packageJson = JSON.parse(await readFile5(packageJsonPath, "utf8"));
62385
+ if (!isRecord11(packageJson) || typeof packageJson.name !== "string")
62386
+ return true;
62387
+ return !PACKAGED_CODEX_INSTALLER_NAMES.has(packageJson.name);
62388
+ }
62389
+ function isRecord11(value) {
62390
+ return typeof value === "object" && value !== null && !Array.isArray(value);
62391
+ }
62392
+
62181
62393
  // src/cli/install-codex/codex-config-toml.ts
62182
- import { mkdir as mkdir3, readFile as readFile5, writeFile as writeFile3 } from "fs/promises";
62394
+ import { mkdir as mkdir3, readFile as readFile6, writeFile as writeFile3 } from "fs/promises";
62183
62395
  import { dirname as dirname12 } from "path";
62184
62396
 
62185
62397
  // src/cli/install-codex/toml-section-editor.ts
@@ -62210,10 +62422,24 @@ function replaceOrInsertSetting(config, section, key, value) {
62210
62422
  return config.slice(0, section.start) + replacement + config.slice(section.end);
62211
62423
  }
62212
62424
  function removeSetting(config, section, key) {
62213
- const linePattern = new RegExp(`^${escapeRegExp(key)}\\s*=.*(?:\\n|$)`, "m");
62425
+ const linePattern = new RegExp(`^\\s*${escapeRegExp(key)}\\s*=.*(?:\\n|$)`, "m");
62214
62426
  const replacement = section.text.replace(linePattern, "");
62215
62427
  return config.slice(0, section.start) + replacement + config.slice(section.end);
62216
62428
  }
62429
+ function replaceOrInsertRootSetting(config, key, value) {
62430
+ const sectionStart = findFirstTableStart(config);
62431
+ const root = config.slice(0, sectionStart);
62432
+ const suffix = config.slice(sectionStart);
62433
+ const linePattern = new RegExp(`^${escapeRegExp(key)}\\s*=.*$`, "m");
62434
+ const replacement = linePattern.test(root) ? root.replace(linePattern, `${key} = ${value}`) : `${root.trimEnd()}${root.trimEnd().length > 0 ? `
62435
+ ` : ""}${key} = ${value}
62436
+ `;
62437
+ if (suffix.length === 0)
62438
+ return replacement;
62439
+ return `${replacement.trimEnd()}
62440
+
62441
+ ${suffix.trimStart()}`;
62442
+ }
62217
62443
  function appendBlock(config, block) {
62218
62444
  const prefix = config.trimEnd();
62219
62445
  return `${prefix}${prefix.length > 0 ? `
@@ -62221,6 +62447,10 @@ function appendBlock(config, block) {
62221
62447
  ` : ""}${block.trimEnd()}
62222
62448
  `;
62223
62449
  }
62450
+ function findFirstTableStart(config) {
62451
+ const match = config.match(/^[[].*$/m);
62452
+ return match?.index ?? config.length;
62453
+ }
62224
62454
  function insertSetting(sectionText, key, value) {
62225
62455
  const lines = sectionText.split(`
62226
62456
  `);
@@ -62232,11 +62462,130 @@ function escapeRegExp(value) {
62232
62462
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
62233
62463
  }
62234
62464
 
62465
+ // src/cli/install-codex/codex-config-mcp.ts
62466
+ var CONTEXT7_MCP_SERVER_HEADER = "mcp_servers.context7";
62467
+ var CONTEXT7_MCP_SERVER_BLOCK = [
62468
+ `[${CONTEXT7_MCP_SERVER_HEADER}]`,
62469
+ 'command = "npx"',
62470
+ 'args = ["-y", "@upstash/context7-mcp", "--api-key", "YOUR_API_KEY"]',
62471
+ "startup_timeout_sec = 20",
62472
+ ""
62473
+ ].join(`
62474
+ `);
62475
+ function ensureContext7McpServer(config) {
62476
+ if (findTomlSection(config, CONTEXT7_MCP_SERVER_HEADER))
62477
+ return config;
62478
+ return appendBlock(config, CONTEXT7_MCP_SERVER_BLOCK);
62479
+ }
62480
+
62481
+ // packages/omo-codex/scripts/install/toml-editor.mjs
62482
+ function findTomlSection2(config, header) {
62483
+ const headerLine = `[${header}]`;
62484
+ const lines = config.match(/[^\n]*\n?|$/g) ?? [];
62485
+ let offset = 0;
62486
+ let start = -1;
62487
+ for (const line of lines) {
62488
+ if (line.length === 0)
62489
+ break;
62490
+ const trimmed = line.trim();
62491
+ if (start === -1) {
62492
+ if (trimmed === headerLine)
62493
+ start = offset;
62494
+ } else if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
62495
+ return { start, end: offset, text: config.slice(start, offset) };
62496
+ }
62497
+ offset += line.length;
62498
+ }
62499
+ if (start === -1)
62500
+ return null;
62501
+ return { start, end: config.length, text: config.slice(start) };
62502
+ }
62503
+ function replaceOrInsertSetting2(config, section, key, value) {
62504
+ const linePattern = new RegExp(`^${escapeRegExp2(key)}\\s*=.*$`, "m");
62505
+ const replacement = linePattern.test(section.text) ? section.text.replace(linePattern, `${key} = ${value}`) : insertSetting2(section.text, key, value);
62506
+ return config.slice(0, section.start) + replacement + config.slice(section.end);
62507
+ }
62508
+ function removeSetting2(config, section, key) {
62509
+ const linePattern = new RegExp(`^\\s*${escapeRegExp2(key)}\\s*=.*(?:\\n|$)`, "m");
62510
+ const replacement = section.text.replace(linePattern, "");
62511
+ return config.slice(0, section.start) + replacement + config.slice(section.end);
62512
+ }
62513
+ function replaceOrInsertRootSetting2(config, key, value) {
62514
+ const sectionStart = findFirstTableStart2(config);
62515
+ const root = config.slice(0, sectionStart);
62516
+ const suffix = config.slice(sectionStart);
62517
+ const linePattern = new RegExp(`^${escapeRegExp2(key)}\\s*=.*$`, "m");
62518
+ const replacement = linePattern.test(root) ? root.replace(linePattern, `${key} = ${value}`) : `${root.trimEnd()}${root.trimEnd().length > 0 ? `
62519
+ ` : ""}${key} = ${value}
62520
+ `;
62521
+ if (suffix.length === 0)
62522
+ return replacement;
62523
+ return `${replacement.trimEnd()}
62524
+
62525
+ ${suffix.trimStart()}`;
62526
+ }
62527
+ function appendBlock2(config, block) {
62528
+ const prefix = config.trimEnd();
62529
+ return `${prefix}${prefix.length > 0 ? `
62530
+
62531
+ ` : ""}${block.trimEnd()}
62532
+ `;
62533
+ }
62534
+ function findFirstTableStart2(config) {
62535
+ const match = config.match(/^[[].*$/m);
62536
+ return match?.index ?? config.length;
62537
+ }
62538
+ function insertSetting2(sectionText, key, value) {
62539
+ const lines = sectionText.split(`
62540
+ `);
62541
+ lines.splice(1, 0, `${key} = ${value}`);
62542
+ return lines.join(`
62543
+ `);
62544
+ }
62545
+ function escapeRegExp2(value) {
62546
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
62547
+ }
62548
+
62549
+ // packages/omo-codex/scripts/install/permissions.mjs
62550
+ function ensureAutonomousPermissions(config) {
62551
+ let next = replaceOrInsertRootSetting2(config, "approval_policy", JSON.stringify("never"));
62552
+ next = replaceOrInsertRootSetting2(next, "sandbox_mode", JSON.stringify("danger-full-access"));
62553
+ next = replaceOrInsertRootSetting2(next, "network_access", JSON.stringify("enabled"));
62554
+ next = removeWindowsSandboxSetting(next);
62555
+ next = ensureNoticeEnabled(next, "hide_full_access_warning");
62556
+ return ensureNoticeEnabled(next, "hide_world_writable_warning");
62557
+ }
62558
+ function removeWindowsSandboxSetting(config) {
62559
+ const section = findTomlSection2(config, "windows");
62560
+ if (!section)
62561
+ return config;
62562
+ return removeSetting2(config, section, "sandbox");
62563
+ }
62564
+ function ensureNoticeEnabled(config, key) {
62565
+ const section = findTomlSection2(config, "notice");
62566
+ if (!section)
62567
+ return appendNoticeBlock(config, key);
62568
+ return replaceOrInsertSetting2(config, section, key, "true");
62569
+ }
62570
+ function appendNoticeBlock(config, key) {
62571
+ return appendBlock2(config, `[notice]
62572
+ ${key} = true
62573
+ `);
62574
+ }
62575
+ // src/cli/install-codex/codex-config-reasoning.ts
62576
+ var DEFAULT_MODE_REASONING_EFFORT = "high";
62577
+ var PLAN_MODE_REASONING_EFFORT = "xhigh";
62578
+ function ensureCodexReasoningConfig(config) {
62579
+ let next = replaceOrInsertRootSetting(config, "model_reasoning_effort", JSON.stringify(DEFAULT_MODE_REASONING_EFFORT));
62580
+ next = replaceOrInsertRootSetting(next, "plan_mode_reasoning_effort", JSON.stringify(PLAN_MODE_REASONING_EFFORT));
62581
+ return next;
62582
+ }
62583
+
62235
62584
  // src/cli/install-codex/codex-multi-agent-v2-config.ts
62236
62585
  var CODEX_MULTI_AGENT_V2_HEADER = "features.multi_agent_v2";
62237
62586
  var CODEX_MULTI_AGENT_V2_MAX_CONCURRENT_THREADS_PER_SESSION = 1e4;
62238
62587
  function ensureCodexMultiAgentV2Config(config) {
62239
- const normalizedConfig = removeFeatureFlagSetting(config, "multi_agent_v2");
62588
+ const normalizedConfig = removeLegacyAgentsMaxThreadsSetting(removeFeatureFlagSetting(config, "multi_agent_v2"));
62240
62589
  const section = findTomlSection(normalizedConfig, CODEX_MULTI_AGENT_V2_HEADER);
62241
62590
  const maxThreadsValue = CODEX_MULTI_AGENT_V2_MAX_CONCURRENT_THREADS_PER_SESSION.toString();
62242
62591
  if (!section) {
@@ -62261,6 +62610,12 @@ function removeFeatureFlagSetting(config, featureName) {
62261
62610
  return config;
62262
62611
  return removeSetting(config, section, featureName);
62263
62612
  }
62613
+ function removeLegacyAgentsMaxThreadsSetting(config) {
62614
+ const section = findTomlSection(config, "agents");
62615
+ if (!section)
62616
+ return config;
62617
+ return removeSetting(config, section, "max_threads");
62618
+ }
62264
62619
 
62265
62620
  // src/cli/install-codex/codex-config-toml.ts
62266
62621
  var SISYPHUS_LEGACY_MARKETPLACES = ["lazycodex", "code-yeongyu-codex-plugins"];
@@ -62276,7 +62631,7 @@ async function updateCodexConfig(input) {
62276
62631
  await mkdir3(dirname12(input.configPath), { recursive: true });
62277
62632
  let config = "";
62278
62633
  if (await exists2(input.configPath))
62279
- config = await readFile5(input.configPath, "utf8");
62634
+ config = await readFile6(input.configPath, "utf8");
62280
62635
  const pluginSet = new Set(input.pluginNames);
62281
62636
  for (const legacyMarketplaceName of legacyMarketplaceNames(input.marketplaceName)) {
62282
62637
  config = removeMarketplaceBlock(config, legacyMarketplaceName);
@@ -62288,13 +62643,16 @@ async function updateCodexConfig(input) {
62288
62643
  config = removeStaleManagedAgentBlocks(config, new Set((input.agentConfigs ?? []).map((agentConfig) => agentConfig.name)));
62289
62644
  config = ensureFeatureEnabled(config, "plugins");
62290
62645
  config = ensureFeatureEnabled(config, "plugin_hooks");
62646
+ config = ensureCodexReasoningConfig(config);
62291
62647
  config = ensureCodexMultiAgentV2Config(config);
62648
+ config = ensureContext7McpServer(config);
62292
62649
  if (input.autonomousPermissions === true)
62293
62650
  config = ensureAutonomousPermissions(config);
62294
62651
  config = ensureMarketplaceBlock(config, input.marketplaceName, input.marketplaceSource);
62295
62652
  for (const pluginName of input.pluginNames) {
62296
62653
  config = ensurePluginEnabled(config, `${pluginName}@${input.marketplaceName}`);
62297
62654
  }
62655
+ config = ensureOmoGitBashMcpPolicy(config, input);
62298
62656
  for (const state of input.trustedHookStates ?? []) {
62299
62657
  config = ensureHookTrusted(config, state.key, state.trustedHash);
62300
62658
  }
@@ -62360,42 +62718,6 @@ ${featureName} = true
62360
62718
  `);
62361
62719
  return replaceOrInsertSetting(config, section, featureName, "true");
62362
62720
  }
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, "\\$&");
62398
- }
62399
62721
  function ensureMarketplaceBlock(config, marketplaceName, source) {
62400
62722
  const header = `marketplaces.${marketplaceName}`;
62401
62723
  const lines = [
@@ -62424,6 +62746,22 @@ enabled = true
62424
62746
  `);
62425
62747
  return replaceOrInsertSetting(config, section, "enabled", "true");
62426
62748
  }
62749
+ function ensurePluginMcpEnabled(config, pluginKey, serverName, enabled) {
62750
+ const header = `plugins.${JSON.stringify(pluginKey)}.mcp_servers.${serverName}`;
62751
+ const section = findTomlSection(config, header);
62752
+ const enabledValue = enabled ? "true" : "false";
62753
+ if (!section)
62754
+ return appendBlock(config, `[${header}]
62755
+ enabled = ${enabledValue}
62756
+ `);
62757
+ return replaceOrInsertSetting(config, section, "enabled", enabledValue);
62758
+ }
62759
+ function ensureOmoGitBashMcpPolicy(config, input) {
62760
+ if (input.marketplaceName !== "sisyphuslabs" || !input.pluginNames.includes("omo"))
62761
+ return config;
62762
+ const enabled = (input.platform ?? process.platform) === "win32";
62763
+ return ensurePluginMcpEnabled(config, "omo@sisyphuslabs", "git_bash", enabled);
62764
+ }
62427
62765
  function ensureHookTrusted(config, key, trustedHash) {
62428
62766
  const header = `hooks.state.${JSON.stringify(key)}`;
62429
62767
  const section = findTomlSection(config, header);
@@ -62521,7 +62859,7 @@ function parseJsonString(value) {
62521
62859
  }
62522
62860
  async function exists2(path6) {
62523
62861
  try {
62524
- await readFile5(path6, "utf8");
62862
+ await readFile6(path6, "utf8");
62525
62863
  return true;
62526
62864
  } catch (error) {
62527
62865
  if (error instanceof Error)
@@ -62531,9 +62869,9 @@ async function exists2(path6) {
62531
62869
  }
62532
62870
 
62533
62871
  // 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";
62872
+ import { createHash as createHash2 } from "crypto";
62873
+ import { readFile as readFile7 } from "fs/promises";
62874
+ import { join as join24 } from "path";
62537
62875
  var EVENT_LABELS = new Map([
62538
62876
  ["PreToolUse", "pre_tool_use"],
62539
62877
  ["PermissionRequest", "permission_request"],
@@ -62547,17 +62885,17 @@ var EVENT_LABELS = new Map([
62547
62885
  ["Stop", "stop"]
62548
62886
  ]);
62549
62887
  async function trustedHookStatesForPlugin(input) {
62550
- const manifestPath = join23(input.pluginRoot, ".codex-plugin", "plugin.json");
62888
+ const manifestPath = join24(input.pluginRoot, ".codex-plugin", "plugin.json");
62551
62889
  if (!await exists3(manifestPath))
62552
62890
  return [];
62553
- const manifest = JSON.parse(await readFile6(manifestPath, "utf8"));
62554
- if (!isRecord11(manifest) || typeof manifest.hooks !== "string")
62891
+ const manifest = JSON.parse(await readFile7(manifestPath, "utf8"));
62892
+ if (!isRecord12(manifest) || typeof manifest.hooks !== "string")
62555
62893
  return [];
62556
- const hooksPath = join23(input.pluginRoot, manifest.hooks);
62894
+ const hooksPath = join24(input.pluginRoot, manifest.hooks);
62557
62895
  if (!await exists3(hooksPath))
62558
62896
  return [];
62559
- const parsed = JSON.parse(await readFile6(hooksPath, "utf8"));
62560
- if (!isRecord11(parsed) || !isRecord11(parsed.hooks))
62897
+ const parsed = JSON.parse(await readFile7(hooksPath, "utf8"));
62898
+ if (!isRecord12(parsed) || !isRecord12(parsed.hooks))
62561
62899
  return [];
62562
62900
  const keySource = `${input.pluginName}@${input.marketplaceName}:${stripDotSlash(manifest.hooks)}`;
62563
62901
  const states = [];
@@ -62568,10 +62906,10 @@ async function trustedHookStatesForPlugin(input) {
62568
62906
  if (eventLabel === undefined)
62569
62907
  continue;
62570
62908
  for (const [groupIndex, group] of groups.entries()) {
62571
- if (!isRecord11(group) || !Array.isArray(group.hooks))
62909
+ if (!isRecord12(group) || !Array.isArray(group.hooks))
62572
62910
  continue;
62573
62911
  for (const [handlerIndex, handler] of group.hooks.entries()) {
62574
- if (!isRecord11(handler) || handler.type !== "command")
62912
+ if (!isRecord12(handler) || handler.type !== "command")
62575
62913
  continue;
62576
62914
  if (handler.async === true)
62577
62915
  continue;
@@ -62598,12 +62936,12 @@ function commandHookHash(eventName, matcher, handler) {
62598
62936
  if (typeof matcher === "string")
62599
62937
  identity.matcher = matcher;
62600
62938
  const canonical = JSON.stringify(canonicalJson(identity));
62601
- return `sha256:${createHash("sha256").update(canonical).digest("hex")}`;
62939
+ return `sha256:${createHash2("sha256").update(canonical).digest("hex")}`;
62602
62940
  }
62603
62941
  function canonicalJson(value) {
62604
62942
  if (Array.isArray(value))
62605
62943
  return value.map(canonicalJson);
62606
- if (!isRecord11(value))
62944
+ if (!isRecord12(value))
62607
62945
  return value;
62608
62946
  const result = {};
62609
62947
  for (const key of Object.keys(value).sort()) {
@@ -62616,19 +62954,113 @@ function stripDotSlash(value) {
62616
62954
  }
62617
62955
  async function exists3(path6) {
62618
62956
  try {
62619
- await readFile6(path6, "utf8");
62957
+ await readFile7(path6, "utf8");
62620
62958
  return true;
62621
62959
  } catch {
62622
62960
  return false;
62623
62961
  }
62624
62962
  }
62625
- function isRecord11(value) {
62963
+ function isRecord12(value) {
62626
62964
  return typeof value === "object" && value !== null && !Array.isArray(value);
62627
62965
  }
62628
62966
 
62967
+ // src/cli/install-codex/git-bash.ts
62968
+ import { execFileSync } from "child_process";
62969
+ import { existsSync as existsSync23 } from "fs";
62970
+ var GIT_BASH_ENV_KEY = "OMO_CODEX_GIT_BASH_PATH";
62971
+ var SKIP_GIT_BASH_AUTO_INSTALL_ENV_KEY = "OMO_CODEX_SKIP_GIT_BASH_AUTO_INSTALL";
62972
+ var PROGRAM_FILES_GIT_BASH = "C:\\Program Files\\Git\\bin\\bash.exe";
62973
+ var PROGRAM_FILES_X86_GIT_BASH = "C:\\Program Files (x86)\\Git\\bin\\bash.exe";
62974
+ var WINGET_INSTALL_ARGS = ["install", "--id", "Git.Git", "-e", "--source", "winget"];
62975
+ function resolveGitBash(input) {
62976
+ if (input.platform !== "win32")
62977
+ return { found: true, path: null, source: "not-required" };
62978
+ const checkedPaths = [];
62979
+ const envPath = nonEmptyEnvValue(input.env, GIT_BASH_ENV_KEY);
62980
+ if (envPath !== undefined) {
62981
+ checkedPaths.push(envPath);
62982
+ if (isBashExePath(envPath) && input.exists(envPath))
62983
+ return { found: true, path: envPath, source: "env" };
62984
+ return missingGitBash(checkedPaths);
62985
+ }
62986
+ for (const candidate of [
62987
+ { path: PROGRAM_FILES_GIT_BASH, source: "program-files" },
62988
+ { path: PROGRAM_FILES_X86_GIT_BASH, source: "program-files-x86" }
62989
+ ]) {
62990
+ checkedPaths.push(candidate.path);
62991
+ if (input.exists(candidate.path))
62992
+ return { found: true, path: candidate.path, source: candidate.source };
62993
+ }
62994
+ for (const pathCandidate of input.where("bash")) {
62995
+ const candidate = pathCandidate.trim();
62996
+ if (candidate.length === 0)
62997
+ continue;
62998
+ checkedPaths.push(candidate);
62999
+ if (isBashExePath(candidate) && input.exists(candidate))
63000
+ return { found: true, path: candidate, source: "path" };
63001
+ }
63002
+ return missingGitBash(checkedPaths);
63003
+ }
63004
+ function resolveGitBashForCurrentProcess(input = {}) {
63005
+ return resolveGitBash({
63006
+ platform: input.platform ?? process.platform,
63007
+ env: input.env ?? process.env,
63008
+ exists: existsSync23,
63009
+ where: whereCommand
63010
+ });
63011
+ }
63012
+ async function prepareGitBashForInstall(input) {
63013
+ const resolve8 = input.resolveGitBash ?? (() => resolveGitBashForCurrentProcess({ platform: input.platform, env: input.env }));
63014
+ const initialResolution = resolve8();
63015
+ if (input.platform !== "win32" || initialResolution.found)
63016
+ return initialResolution;
63017
+ if (input.env[SKIP_GIT_BASH_AUTO_INSTALL_ENV_KEY] === "1")
63018
+ return initialResolution;
63019
+ try {
63020
+ await input.runCommand("winget", WINGET_INSTALL_ARGS, { cwd: input.cwd });
63021
+ } catch (error) {
63022
+ if (!(error instanceof Error))
63023
+ throw error;
63024
+ return initialResolution;
63025
+ }
63026
+ return resolve8();
63027
+ }
63028
+ function missingGitBash(checkedPaths) {
63029
+ return {
63030
+ found: false,
63031
+ checkedPaths,
63032
+ installHint: [
63033
+ "Git Bash is required for native Windows Codex profile installs.",
63034
+ "Install it with: winget install --id Git.Git -e --source winget",
63035
+ `For a custom install, set ${GIT_BASH_ENV_KEY}=C:\\path\\to\\bash.exe`,
63036
+ "Then rerun `npx lazycodex-ai install`."
63037
+ ].join(`
63038
+ `)
63039
+ };
63040
+ }
63041
+ function nonEmptyEnvValue(env, key) {
63042
+ const value = env[key];
63043
+ if (value === undefined)
63044
+ return;
63045
+ const trimmed = value.trim();
63046
+ return trimmed.length === 0 ? undefined : trimmed;
63047
+ }
63048
+ function isBashExePath(path6) {
63049
+ return path6.toLowerCase().endsWith("bash.exe");
63050
+ }
63051
+ function whereCommand(command) {
63052
+ try {
63053
+ return execFileSync("where", [command], { encoding: "utf8" }).split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
63054
+ } catch (error) {
63055
+ if (error instanceof Error)
63056
+ return [];
63057
+ throw error;
63058
+ }
63059
+ }
63060
+
62629
63061
  // src/cli/install-codex/link-cached-plugin-agents.ts
62630
63062
  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";
63063
+ import { basename as basename6, join as join25 } from "path";
62632
63064
  var MANIFEST_FILE = ".installed-agents.json";
62633
63065
  async function linkCachedPluginAgents(input) {
62634
63066
  const platform = input.platform ?? process.platform;
@@ -62637,11 +63069,11 @@ async function linkCachedPluginAgents(input) {
62637
63069
  await writeManifest(input.pluginRoot, []);
62638
63070
  return [];
62639
63071
  }
62640
- const agentsDir = join24(input.codexHome, "agents");
63072
+ const agentsDir = join25(input.codexHome, "agents");
62641
63073
  await mkdir4(agentsDir, { recursive: true });
62642
63074
  const linked = [];
62643
63075
  for (const agentPath of bundledAgents) {
62644
- const linkPath = join24(agentsDir, basename6(agentPath));
63076
+ const linkPath = join25(agentsDir, basename6(agentPath));
62645
63077
  if (platform === "win32") {
62646
63078
  await replaceWithCopy(linkPath, agentPath);
62647
63079
  } else {
@@ -62653,7 +63085,7 @@ async function linkCachedPluginAgents(input) {
62653
63085
  return linked;
62654
63086
  }
62655
63087
  async function discoverBundledAgents(pluginRoot) {
62656
- const componentsRoot = join24(pluginRoot, "components");
63088
+ const componentsRoot = join25(pluginRoot, "components");
62657
63089
  if (!await exists4(componentsRoot))
62658
63090
  return [];
62659
63091
  const componentEntries = await readdir3(componentsRoot, { withFileTypes: true });
@@ -62661,14 +63093,14 @@ async function discoverBundledAgents(pluginRoot) {
62661
63093
  for (const entry of componentEntries) {
62662
63094
  if (!entry.isDirectory())
62663
63095
  continue;
62664
- const agentsRoot = join24(componentsRoot, entry.name, "agents");
63096
+ const agentsRoot = join25(componentsRoot, entry.name, "agents");
62665
63097
  if (!await exists4(agentsRoot))
62666
63098
  continue;
62667
63099
  const agentEntries = await readdir3(agentsRoot, { withFileTypes: true });
62668
63100
  for (const file of agentEntries) {
62669
63101
  if (!file.isFile() || !file.name.endsWith(".toml"))
62670
63102
  continue;
62671
- agents.push(join24(agentsRoot, file.name));
63103
+ agents.push(join25(agentsRoot, file.name));
62672
63104
  }
62673
63105
  }
62674
63106
  agents.sort();
@@ -62692,7 +63124,7 @@ async function prepareReplacement(linkPath) {
62692
63124
  await rm3(linkPath, { force: true });
62693
63125
  }
62694
63126
  async function writeManifest(pluginRoot, agentPaths) {
62695
- const manifestPath = join24(pluginRoot, MANIFEST_FILE);
63127
+ const manifestPath = join25(pluginRoot, MANIFEST_FILE);
62696
63128
  const payload = { agents: [...agentPaths].sort() };
62697
63129
  await writeFile4(manifestPath, `${JSON.stringify(payload, null, "\t")}
62698
63130
  `);
@@ -62707,14 +63139,14 @@ async function exists4(path6) {
62707
63139
  }
62708
63140
 
62709
63141
  // src/cli/install-codex/codex-marketplace.ts
62710
- import { readFile as readFile7 } from "fs/promises";
62711
- import { join as join25 } from "path";
63142
+ import { readFile as readFile8 } from "fs/promises";
63143
+ import { join as join26 } from "path";
62712
63144
  var DEFAULT_MARKETPLACE_PATH = "packages/omo-codex/marketplace.json";
62713
63145
  async function readMarketplace(repoRoot, options) {
62714
- const marketplacePath = options?.marketplacePath ?? join25(repoRoot, DEFAULT_MARKETPLACE_PATH);
62715
- const raw = await readFile7(marketplacePath, "utf8");
63146
+ const marketplacePath = options?.marketplacePath ?? join26(repoRoot, DEFAULT_MARKETPLACE_PATH);
63147
+ const raw = await readFile8(marketplacePath, "utf8");
62716
63148
  const parsed = JSON.parse(raw);
62717
- if (!isRecord12(parsed))
63149
+ if (!isRecord13(parsed))
62718
63150
  throw new Error("marketplace.json must be an object");
62719
63151
  if (typeof parsed.name !== "string" || parsed.name.trim() === "") {
62720
63152
  throw new Error("marketplace.json name must be a non-empty string");
@@ -62730,12 +63162,12 @@ async function readMarketplace(repoRoot, options) {
62730
63162
  function resolvePluginSource(repoRoot, plugin, options) {
62731
63163
  const sourcePath = localSourcePath(options?.pathOverride ?? plugin.source);
62732
63164
  const relativePath = sourcePath.slice(2);
62733
- return join25(repoRoot, ...relativePath.split(/[\\/]/));
63165
+ return join26(repoRoot, ...relativePath.split(/[\\/]/));
62734
63166
  }
62735
63167
  async function readPluginManifest(pluginRoot) {
62736
- const raw = await readFile7(join25(pluginRoot, ".codex-plugin", "plugin.json"), "utf8");
63168
+ const raw = await readFile8(join26(pluginRoot, ".codex-plugin", "plugin.json"), "utf8");
62737
63169
  const parsed = JSON.parse(raw);
62738
- if (!isRecord12(parsed))
63170
+ if (!isRecord13(parsed))
62739
63171
  throw new Error(`${pluginRoot} plugin.json must be an object`);
62740
63172
  if (typeof parsed.name !== "string" || parsed.name.trim() === "") {
62741
63173
  throw new Error(`${pluginRoot} plugin.json name must be a non-empty string`);
@@ -62761,7 +63193,7 @@ function validatePathSegment(value, label) {
62761
63193
  }
62762
63194
  }
62763
63195
  function normalizeMarketplacePlugin(plugin, index) {
62764
- if (!isRecord12(plugin))
63196
+ if (!isRecord13(plugin))
62765
63197
  throw new Error(`marketplace plugin ${index} must be an object`);
62766
63198
  if (typeof plugin.name !== "string" || plugin.name.trim() === "") {
62767
63199
  throw new Error(`marketplace plugin ${index} name must be a non-empty string`);
@@ -62773,7 +63205,7 @@ function normalizeMarketplacePlugin(plugin, index) {
62773
63205
  }
62774
63206
  return { name: plugin.name, source: plugin.source };
62775
63207
  }
62776
- if (isRecord12(plugin.source) && plugin.source.source === "local" && typeof plugin.source.path === "string") {
63208
+ if (isRecord13(plugin.source) && plugin.source.source === "local" && typeof plugin.source.path === "string") {
62777
63209
  validateLocalSourcePath(plugin.source.path);
62778
63210
  const local = { source: "local", path: plugin.source.path };
62779
63211
  return { name: plugin.name, source: local };
@@ -62800,13 +63232,13 @@ function validateLocalSourcePath(path6) {
62800
63232
  }
62801
63233
  return path6;
62802
63234
  }
62803
- function isRecord12(value) {
63235
+ function isRecord13(value) {
62804
63236
  return typeof value === "object" && value !== null && !Array.isArray(value);
62805
63237
  }
62806
63238
 
62807
63239
  // src/cli/install-codex/codex-marketplace-snapshot.ts
62808
63240
  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";
63241
+ import { join as join27, sep as sep2 } from "path";
62810
63242
  var INSTALLED_MARKETPLACES_DIR = ".tmp/marketplaces";
62811
63243
  async function writeInstalledMarketplaceSnapshot(input) {
62812
63244
  const marketplaceRoot = installedMarketplaceRoot(input.codexHome, input.marketplace.name);
@@ -62819,21 +63251,21 @@ async function writeInstalledMarketplaceSnapshot(input) {
62819
63251
  return snapshotPlugins;
62820
63252
  }
62821
63253
  function installedMarketplaceRoot(codexHome, marketplaceName) {
62822
- return join26(codexHome, INSTALLED_MARKETPLACES_DIR, marketplaceName);
63254
+ return join27(codexHome, INSTALLED_MARKETPLACES_DIR, marketplaceName);
62823
63255
  }
62824
63256
  async function writeMarketplaceManifest(marketplaceRoot, marketplace) {
62825
- const manifestDir = join26(marketplaceRoot, ".agents", "plugins");
63257
+ const manifestDir = join27(marketplaceRoot, ".agents", "plugins");
62826
63258
  await mkdir5(manifestDir, { recursive: true });
62827
- const tempPath = join26(manifestDir, `.marketplace-${process.pid}-${Date.now()}.json.tmp`);
63259
+ const tempPath = join27(manifestDir, `.marketplace-${process.pid}-${Date.now()}.json.tmp`);
62828
63260
  await writeFile5(tempPath, `${JSON.stringify(marketplace, null, "\t")}
62829
63261
  `);
62830
- await rename2(tempPath, join26(manifestDir, "marketplace.json"));
63262
+ await rename2(tempPath, join27(manifestDir, "marketplace.json"));
62831
63263
  }
62832
63264
  async function writeSnapshotPlugin(marketplaceRoot, plugin) {
62833
- const pluginsDir = join26(marketplaceRoot, "plugins");
63265
+ const pluginsDir = join27(marketplaceRoot, "plugins");
62834
63266
  await mkdir5(pluginsDir, { recursive: true });
62835
- const targetPath = join26(pluginsDir, plugin.name);
62836
- const tempPath = join26(pluginsDir, `.tmp-${plugin.name}-${process.pid}-${Date.now()}`);
63267
+ const targetPath = join27(pluginsDir, plugin.name);
63268
+ const tempPath = join27(pluginsDir, `.tmp-${plugin.name}-${process.pid}-${Date.now()}`);
62837
63269
  await rm4(tempPath, { recursive: true, force: true });
62838
63270
  await cp3(plugin.sourcePath, tempPath, {
62839
63271
  recursive: true,
@@ -62871,19 +63303,260 @@ var defaultRunCommand = async (command, args, options) => {
62871
63303
  }
62872
63304
  };
62873
63305
 
63306
+ // src/cli/install-codex/codex-project-local-cleanup.ts
63307
+ import { copyFile as copyFile2, lstat as lstat4, readFile as readFile9, writeFile as writeFile6 } from "fs/promises";
63308
+ import { dirname as dirname13, join as join28, resolve as resolve8 } from "path";
63309
+ var LEGACY_AGENT_CONFLICT_KEYS = ["max_threads"];
63310
+ var PROJECT_LOCAL_ARTIFACT_PATHS = [
63311
+ ".omx",
63312
+ ".codex/hooks.json",
63313
+ ".codex/agents",
63314
+ ".codex/prompts",
63315
+ ".codex/skills"
63316
+ ];
63317
+ async function repairNearestProjectLocalCodexArtifacts(input) {
63318
+ const project = await findProjectLocalCodexConfigs(input.startDirectory, input.codexHome);
63319
+ if (project === null) {
63320
+ return emptyProjectLocalCodexCleanupResult();
63321
+ }
63322
+ const artifacts = await collectProjectLocalArtifacts(project.artifactRoots);
63323
+ const configs = [];
63324
+ for (const configPath of project.configPaths) {
63325
+ const original = await readFile9(configPath, "utf8");
63326
+ const repair = repairProjectLocalCodexConfigText(original);
63327
+ if (!repair.changed) {
63328
+ configs.push({
63329
+ projectRoot: project.projectRoot,
63330
+ configPath,
63331
+ changed: false,
63332
+ removedKeys: repair.removedKeys
63333
+ });
63334
+ continue;
63335
+ }
63336
+ const backupPath = `${configPath}.backup-${formatBackupTimestamp(input.now?.() ?? new Date)}`;
63337
+ await copyFile2(configPath, backupPath);
63338
+ await writeFile6(configPath, `${repair.config.trimEnd()}
63339
+ `);
63340
+ configs.push({
63341
+ projectRoot: project.projectRoot,
63342
+ configPath,
63343
+ changed: true,
63344
+ removedKeys: repair.removedKeys,
63345
+ backupPath
63346
+ });
63347
+ }
63348
+ const changedConfigs = configs.filter((config) => config.changed);
63349
+ const nearestChangedConfig = lastValue(changedConfigs);
63350
+ const nearestConfig = lastValue(configs);
63351
+ return {
63352
+ projectRoot: project.projectRoot,
63353
+ configPath: nearestChangedConfig?.configPath ?? nearestConfig?.configPath ?? null,
63354
+ changed: changedConfigs.length > 0,
63355
+ removedKeys: uniqueRemovedKeys(changedConfigs),
63356
+ backupPath: nearestChangedConfig?.backupPath,
63357
+ configs,
63358
+ artifacts
63359
+ };
63360
+ }
63361
+ function emptyProjectLocalCodexCleanupResult() {
63362
+ return {
63363
+ projectRoot: null,
63364
+ configPath: null,
63365
+ changed: false,
63366
+ removedKeys: [],
63367
+ configs: [],
63368
+ artifacts: []
63369
+ };
63370
+ }
63371
+ function uniqueRemovedKeys(configs) {
63372
+ const keys = [];
63373
+ for (const config of configs) {
63374
+ for (const key of config.removedKeys) {
63375
+ if (!keys.includes(key))
63376
+ keys.push(key);
63377
+ }
63378
+ }
63379
+ return keys;
63380
+ }
63381
+ function lastValue(values) {
63382
+ return values.length > 0 ? values[values.length - 1] ?? null : null;
63383
+ }
63384
+ function repairProjectLocalCodexConfigText(config) {
63385
+ if (!isMultiAgentV2Enabled(config))
63386
+ return { config, changed: false, removedKeys: [] };
63387
+ let nextConfig = config;
63388
+ const removedKeys = [];
63389
+ for (const key of LEGACY_AGENT_CONFLICT_KEYS) {
63390
+ const section = findTomlSection(nextConfig, "agents");
63391
+ if (section === null || !hasSetting(section.text, key))
63392
+ continue;
63393
+ nextConfig = removeSetting(nextConfig, section, key);
63394
+ removedKeys.push(key);
63395
+ }
63396
+ return {
63397
+ config: nextConfig,
63398
+ changed: removedKeys.length > 0,
63399
+ removedKeys
63400
+ };
63401
+ }
63402
+ async function findProjectLocalCodexConfigs(startDirectory, codexHome) {
63403
+ if (startDirectory.includes("\x00"))
63404
+ return null;
63405
+ const startDirectoryStat = await maybeLstat(startDirectory);
63406
+ if (startDirectoryStat !== null && !startDirectoryStat.isDirectory()) {
63407
+ throw new ProjectLocalCleanupStartDirectoryError(startDirectory);
63408
+ }
63409
+ const codexHomeConfigPath = codexHome === undefined ? null : join28(resolve8(codexHome), "config.toml");
63410
+ let current = resolve8(startDirectory);
63411
+ const configPathsFromCwd = [];
63412
+ while (true) {
63413
+ const configPath = join28(current, ".codex", "config.toml");
63414
+ if (await isRegularProjectLocalConfig(current, configPath)) {
63415
+ if (codexHomeConfigPath === null || resolve8(configPath) !== codexHomeConfigPath) {
63416
+ configPathsFromCwd.push(configPath);
63417
+ }
63418
+ }
63419
+ if (await exists5(join28(current, ".git"))) {
63420
+ return configPathsFromCwd.length === 0 ? null : {
63421
+ projectRoot: current,
63422
+ configPaths: [...configPathsFromCwd].reverse(),
63423
+ artifactRoots: artifactRootsForConfigPaths(configPathsFromCwd)
63424
+ };
63425
+ }
63426
+ const parent = dirname13(current);
63427
+ if (parent === current) {
63428
+ const nearestConfigPath = configPathsFromCwd[0];
63429
+ return nearestConfigPath === undefined ? null : {
63430
+ projectRoot: dirname13(dirname13(nearestConfigPath)),
63431
+ configPaths: [nearestConfigPath],
63432
+ artifactRoots: [dirname13(dirname13(nearestConfigPath))]
63433
+ };
63434
+ }
63435
+ current = parent;
63436
+ }
63437
+ }
63438
+ async function isRegularProjectLocalConfig(directory, configPath) {
63439
+ const codexDirStat = await maybeLstat(join28(directory, ".codex"));
63440
+ if (codexDirStat === null || !codexDirStat.isDirectory() || codexDirStat.isSymbolicLink())
63441
+ return false;
63442
+ const configStat = await maybeLstat(configPath);
63443
+ return configStat !== null && configStat.isFile() && !configStat.isSymbolicLink();
63444
+ }
63445
+ function artifactRootsForConfigPaths(configPaths) {
63446
+ const roots = [];
63447
+ for (const configPath of configPaths) {
63448
+ const root = dirname13(dirname13(configPath));
63449
+ if (!roots.includes(root))
63450
+ roots.push(root);
63451
+ }
63452
+ return roots.reverse();
63453
+ }
63454
+ async function collectProjectLocalArtifacts(projectRoots) {
63455
+ const artifacts = [];
63456
+ const seenPaths = new Set;
63457
+ for (const projectRoot of projectRoots) {
63458
+ for (const relativePath of PROJECT_LOCAL_ARTIFACT_PATHS) {
63459
+ const artifactPath = join28(projectRoot, relativePath);
63460
+ if (seenPaths.has(artifactPath))
63461
+ continue;
63462
+ const entryStat = await maybeLstat(artifactPath);
63463
+ if (entryStat === null)
63464
+ continue;
63465
+ seenPaths.add(artifactPath);
63466
+ artifacts.push({
63467
+ relativePath,
63468
+ path: artifactPath,
63469
+ kind: entryStat.isDirectory() ? "directory" : entryStat.isFile() ? "file" : "other"
63470
+ });
63471
+ }
63472
+ }
63473
+ return artifacts;
63474
+ }
63475
+ function isMultiAgentV2Enabled(config) {
63476
+ const featuresSection = findTomlSection(config, "features");
63477
+ if (featuresSection !== null && settingIsBooleanTrue(featuresSection.text, "multi_agent_v2"))
63478
+ return true;
63479
+ const multiAgentSection = findTomlSection(config, "features.multi_agent_v2");
63480
+ return multiAgentSection !== null && settingIsBooleanTrue(multiAgentSection.text, "enabled");
63481
+ }
63482
+ function settingIsBooleanTrue(sectionText, key) {
63483
+ return new RegExp(`^\\s*${escapeRegExp(key)}\\s*=\\s*true\\s*(?:#.*)?$`, "m").test(sectionText);
63484
+ }
63485
+ function hasSetting(sectionText, key) {
63486
+ return new RegExp(`^\\s*${escapeRegExp(key)}\\s*=`, "m").test(sectionText);
63487
+ }
63488
+ function formatBackupTimestamp(date) {
63489
+ return date.toISOString().replace(/[:.]/g, "-");
63490
+ }
63491
+ async function maybeLstat(path6) {
63492
+ try {
63493
+ return await lstat4(path6);
63494
+ } catch (error) {
63495
+ if (nodeErrorCode(error) === "ENOENT")
63496
+ return null;
63497
+ throw error;
63498
+ }
63499
+ }
63500
+ async function exists5(path6) {
63501
+ return await maybeLstat(path6) !== null;
63502
+ }
63503
+ function nodeErrorCode(error) {
63504
+ if (!(error instanceof Error) || !("code" in error))
63505
+ return null;
63506
+ return typeof error.code === "string" ? error.code : null;
63507
+ }
63508
+
63509
+ class ProjectLocalCleanupStartDirectoryError extends Error {
63510
+ constructor(startDirectory) {
63511
+ super(`Project-local Codex cleanup start path is not a directory: ${startDirectory}`);
63512
+ this.name = "ProjectLocalCleanupStartDirectoryError";
63513
+ }
63514
+ }
63515
+
63516
+ // src/cli/install-codex/codex-project-local-cleanup-best-effort.ts
63517
+ async function repairProjectLocalCodexArtifactsBestEffort(input) {
63518
+ try {
63519
+ return await repairNearestProjectLocalCodexArtifacts({
63520
+ startDirectory: input.startDirectory,
63521
+ codexHome: input.codexHome,
63522
+ now: input.now
63523
+ });
63524
+ } catch (error) {
63525
+ input.log(`Skipped project-local Codex cleanup: ${formatUnknownError(error)}`);
63526
+ return emptyProjectLocalCodexCleanupResult();
63527
+ }
63528
+ }
63529
+ function formatUnknownError(error) {
63530
+ return error instanceof Error ? error.message : String(error);
63531
+ }
63532
+
62874
63533
  // src/cli/install-codex/install-codex.ts
62875
63534
  var SISYPHUS_LEGACY_CACHE_MARKETPLACES = ["lazycodex", "code-yeongyu-codex-plugins"];
62876
63535
  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 });
63536
+ const env = options.env ?? process.env;
63537
+ const platform = options.platform ?? process.platform;
63538
+ const repoRoot = resolve9(options.repoRoot ?? findRepoRoot({ importerDir: import.meta.dir, env }));
63539
+ const codexHome = resolve9(options.codexHome ?? env.CODEX_HOME ?? join30(homedir5(), ".codex"));
63540
+ const projectDirectory = resolve9(options.projectDirectory ?? env.OMO_CODEX_PROJECT ?? process.cwd());
63541
+ const binDir = resolveCodexInstallerBinDir({ binDir: options.binDir, codexHome, env });
62880
63542
  const runCommand = options.runCommand ?? defaultRunCommand;
62881
63543
  const log2 = options.log ?? (() => {
62882
63544
  return;
62883
63545
  });
62884
- const codexPackageRoot = join28(repoRoot, "packages", "omo-codex");
63546
+ const buildSource = await shouldBuildSourcePackages(repoRoot);
63547
+ const gitBashResolution = await prepareGitBashForInstall({
63548
+ platform,
63549
+ env,
63550
+ cwd: repoRoot,
63551
+ runCommand,
63552
+ resolveGitBash: platform === "win32" ? options.gitBashResolver ?? (() => resolveGitBashForCurrentProcess({ platform, env })) : undefined
63553
+ });
63554
+ if (!gitBashResolution.found) {
63555
+ throw new Error(gitBashResolution.installHint);
63556
+ }
63557
+ const codexPackageRoot = join30(repoRoot, "packages", "omo-codex");
62885
63558
  const marketplace = await readMarketplace(repoRoot, {
62886
- marketplacePath: join28(codexPackageRoot, "marketplace.json")
63559
+ marketplacePath: join30(codexPackageRoot, "marketplace.json")
62887
63560
  });
62888
63561
  const installed = [];
62889
63562
  const pluginSources = [];
@@ -62898,6 +63571,7 @@ async function runCodexInstaller(options = {}) {
62898
63571
  validatePathSegment(version2, "plugin version");
62899
63572
  log2(`Building ${entry.name}@${version2}`);
62900
63573
  const plugin = await installCachedPlugin({
63574
+ buildSource,
62901
63575
  codexHome,
62902
63576
  marketplaceName: marketplace.name,
62903
63577
  name: entry.name,
@@ -62905,7 +63579,7 @@ async function runCodexInstaller(options = {}) {
62905
63579
  sourcePath,
62906
63580
  version: version2
62907
63581
  });
62908
- const links = await linkCachedPluginBins({ binDir, pluginRoot: plugin.path });
63582
+ const links = await linkCachedPluginBins({ binDir, pluginRoot: plugin.path, platform });
62909
63583
  for (const link of links) {
62910
63584
  log2(`Linked ${link.name} -> ${link.target}`);
62911
63585
  }
@@ -62920,7 +63594,7 @@ async function runCodexInstaller(options = {}) {
62920
63594
  });
62921
63595
  for (const plugin of installed) {
62922
63596
  const pluginRoot = agentSourceRoots.get(plugin.name) ?? plugin.path;
62923
- const agentLinks = await linkCachedPluginAgents({ codexHome, pluginRoot });
63597
+ const agentLinks = await linkCachedPluginAgents({ codexHome, pluginRoot, platform });
62924
63598
  for (const link of agentLinks) {
62925
63599
  log2(`Linked agent ${link.name} -> ${link.target}`);
62926
63600
  const agentName = agentNameFromToml(link.name);
@@ -62944,41 +63618,57 @@ async function runCodexInstaller(options = {}) {
62944
63618
  pluginNames: marketplace.plugins.map((plugin) => plugin.name)
62945
63619
  });
62946
63620
  }
62947
- const marketplaceRoot = join28(codexHome, "plugins", "cache", marketplace.name);
63621
+ const marketplaceRoot = join30(codexHome, "plugins", "cache", marketplace.name);
62948
63622
  await writeCachedMarketplaceManifest({
62949
63623
  marketplaceName: marketplace.name,
62950
63624
  marketplaceRoot,
62951
63625
  plugins: installed
62952
63626
  });
62953
- const configPath = join28(codexHome, "config.toml");
63627
+ const configPath = join30(codexHome, "config.toml");
62954
63628
  await updateCodexConfig({
62955
63629
  configPath,
62956
63630
  repoRoot: codexPackageRoot,
62957
63631
  marketplaceName: marketplace.name,
62958
63632
  marketplaceSource: codexMarketplaceSource(marketplaceRoot),
62959
63633
  pluginNames: marketplace.plugins.map((plugin) => plugin.name),
63634
+ platform,
62960
63635
  trustedHookStates,
62961
63636
  agentConfigs: [...agentConfigs.values()].sort((left, right) => left.name.localeCompare(right.name)),
62962
63637
  autonomousPermissions: options.autonomousPermissions === true
62963
63638
  });
63639
+ const projectCleanup = await repairProjectLocalCodexArtifactsBestEffort({
63640
+ startDirectory: projectDirectory,
63641
+ codexHome,
63642
+ log: log2
63643
+ });
63644
+ for (const configCleanup of projectCleanup.configs) {
63645
+ if (!configCleanup.changed)
63646
+ continue;
63647
+ log2(`Repaired project Codex config ${configCleanup.configPath} (backup: ${configCleanup.backupPath})`);
63648
+ }
63649
+ for (const artifact of projectCleanup.artifacts) {
63650
+ log2(`Found project-local legacy artifact ${artifact.path}; left in place`);
63651
+ }
62964
63652
  await trackCodexInstallTelemetry();
62965
63653
  return {
62966
63654
  marketplaceName: marketplace.name,
62967
63655
  installed,
62968
63656
  configPath,
62969
- codexHome
63657
+ codexHome,
63658
+ gitBashPath: gitBashResolution.path,
63659
+ projectCleanup
62970
63660
  };
62971
63661
  }
62972
63662
  function resolveCodexInstallerBinDir(input) {
62973
63663
  const explicitBinDir = input.binDir ?? input.env?.CODEX_LOCAL_BIN_DIR;
62974
63664
  if (explicitBinDir !== undefined && explicitBinDir.trim().length > 0)
62975
- return resolve8(explicitBinDir);
63665
+ return resolve9(explicitBinDir);
62976
63666
  const homeDir = input.homeDir ?? homedir5();
62977
- const defaultCodexHome = resolve8(homeDir, ".codex");
62978
- const resolvedCodexHome = resolve8(input.codexHome);
63667
+ const defaultCodexHome = resolve9(homeDir, ".codex");
63668
+ const resolvedCodexHome = resolve9(input.codexHome);
62979
63669
  if (resolvedCodexHome !== defaultCodexHome)
62980
- return join28(resolvedCodexHome, "bin");
62981
- return resolve8(homeDir, ".local", "bin");
63670
+ return join30(resolvedCodexHome, "bin");
63671
+ return resolve9(homeDir, ".local", "bin");
62982
63672
  }
62983
63673
  function agentNameFromToml(fileName) {
62984
63674
  return fileName.endsWith(".toml") ? fileName.slice(0, -".toml".length) : fileName;
@@ -62995,9 +63685,9 @@ async function agentSourceRootsForInstall(input) {
62995
63685
  return new Map(snapshotPlugins.map((plugin) => [plugin.name, plugin.path]));
62996
63686
  }
62997
63687
  async function writeCachedMarketplaceManifest(input) {
62998
- const marketplaceDir = join28(input.marketplaceRoot, ".agents", "plugins");
63688
+ const marketplaceDir = join30(input.marketplaceRoot, ".agents", "plugins");
62999
63689
  await mkdir6(marketplaceDir, { recursive: true });
63000
- await writeFile6(join28(marketplaceDir, "marketplace.json"), `${JSON.stringify({
63690
+ await writeFile7(join30(marketplaceDir, "marketplace.json"), `${JSON.stringify({
63001
63691
  name: input.marketplaceName,
63002
63692
  plugins: input.plugins.map((plugin) => ({
63003
63693
  name: plugin.name,
@@ -63009,36 +63699,33 @@ async function writeCachedMarketplaceManifest(input) {
63009
63699
  function legacyCacheMarketplaces(marketplaceName) {
63010
63700
  return marketplaceName === "sisyphuslabs" ? SISYPHUS_LEGACY_CACHE_MARKETPLACES : [];
63011
63701
  }
63012
- function codexMarketplaceSource(marketplaceRoot) {
63013
- return { sourceType: "local", source: marketplaceRoot };
63014
- }
63015
63702
  function findRepoRootFromImporter(importerDir) {
63016
63703
  let current = importerDir;
63017
63704
  for (let depth = 0;depth <= 5; depth += 1) {
63018
63705
  if (isRepoRootWithCodexPlugin(current))
63019
63706
  return current;
63020
- for (const wrapperPackageRoot of [join28(current, "node_modules", "oh-my-openagent"), join28(current, "oh-my-openagent")]) {
63707
+ for (const wrapperPackageRoot of [join30(current, "node_modules", "oh-my-openagent"), join30(current, "oh-my-openagent")]) {
63021
63708
  if (isRepoRootWithCodexPlugin(wrapperPackageRoot))
63022
63709
  return wrapperPackageRoot;
63023
63710
  }
63024
- current = resolve8(current, "..");
63711
+ current = resolve9(current, "..");
63025
63712
  }
63026
63713
  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
63714
  }
63028
63715
  function findRepoRoot(input) {
63029
63716
  const wrapperPackageRoot = input.env?.OMO_WRAPPER_PACKAGE_ROOT;
63030
63717
  if (wrapperPackageRoot !== undefined && wrapperPackageRoot.trim().length > 0) {
63031
- const resolvedWrapperPackageRoot = resolve8(wrapperPackageRoot);
63718
+ const resolvedWrapperPackageRoot = resolve9(wrapperPackageRoot);
63032
63719
  if (isRepoRootWithCodexPlugin(resolvedWrapperPackageRoot))
63033
63720
  return resolvedWrapperPackageRoot;
63034
63721
  }
63035
63722
  return findRepoRootFromImporter(input.importerDir);
63036
63723
  }
63037
63724
  function isRepoRootWithCodexPlugin(repoRoot) {
63038
- return existsSyncLike(join28(repoRoot, "packages", "omo-codex", "plugin", ".codex-plugin", "plugin.json"));
63725
+ return existsSync25(join30(repoRoot, "packages", "omo-codex", "plugin", ".codex-plugin", "plugin.json"));
63039
63726
  }
63040
- function existsSyncLike(path7) {
63041
- return existsSync23(path7);
63727
+ function codexMarketplaceSource(marketplaceRoot) {
63728
+ return { sourceType: "local", source: marketplaceRoot };
63042
63729
  }
63043
63730
  async function trackCodexInstallTelemetry() {
63044
63731
  try {
@@ -63052,13 +63739,507 @@ async function trackCodexInstallTelemetry() {
63052
63739
  return;
63053
63740
  }
63054
63741
  }
63742
+ // src/cli/install-codex/codex-installation-detection.ts
63743
+ import { execFile } from "child_process";
63744
+ import { existsSync as existsSync26 } from "fs";
63745
+ import { homedir as homedir6 } from "os";
63746
+ import { join as join32, win32 as win322 } from "path";
63747
+
63748
+ // src/shared/bun-which-shim.ts
63749
+ import { accessSync as accessSync3, constants as constants4 } from "fs";
63750
+ import { delimiter, join as join31 } from "path";
63751
+ var runtime3 = globalThis;
63752
+ var IS_BUN2 = typeof runtime3.Bun !== "undefined";
63753
+ function isUnsafeCommandName(commandName) {
63754
+ if (commandName.includes("/") || commandName.includes("\\"))
63755
+ return true;
63756
+ if (commandName === "." || commandName === ".." || commandName.includes(".."))
63757
+ return true;
63758
+ if (/^[a-zA-Z]:/.test(commandName))
63759
+ return true;
63760
+ if (commandName.includes("\x00"))
63761
+ return true;
63762
+ return false;
63763
+ }
63764
+ function isExecutable(filePath) {
63765
+ try {
63766
+ accessSync3(filePath, constants4.X_OK);
63767
+ return true;
63768
+ } catch {
63769
+ return false;
63770
+ }
63771
+ }
63772
+ function resolvePathValue() {
63773
+ if (process.platform === "win32")
63774
+ return process.env.Path ?? process.env.PATH;
63775
+ return process.env.PATH;
63776
+ }
63777
+ function getWindowsCandidates(commandName) {
63778
+ if (process.platform !== "win32")
63779
+ return [commandName];
63780
+ return [commandName, `${commandName}.exe`, `${commandName}.cmd`, `${commandName}.bat`, `${commandName}.com`];
63781
+ }
63782
+ function bunWhich(commandName) {
63783
+ if (!commandName)
63784
+ return null;
63785
+ if (isUnsafeCommandName(commandName))
63786
+ return null;
63787
+ if (IS_BUN2)
63788
+ return runtime3.Bun?.which(commandName) ?? null;
63789
+ const pathValue = resolvePathValue();
63790
+ if (!pathValue)
63791
+ return null;
63792
+ const pathEntries = pathValue.split(delimiter).filter((pathEntry) => pathEntry.length > 0);
63793
+ if (pathEntries.length === 0)
63794
+ return null;
63795
+ const candidateNames = getWindowsCandidates(commandName);
63796
+ for (const pathEntry of pathEntries) {
63797
+ for (const candidateName of candidateNames) {
63798
+ const candidatePath = join31(pathEntry, candidateName);
63799
+ if (isExecutable(candidatePath))
63800
+ return candidatePath;
63801
+ }
63802
+ }
63803
+ return null;
63804
+ }
63805
+
63806
+ // src/cli/install-codex/codex-installation-detection.ts
63807
+ var CODEX_PATH_CHECK_LABEL = "codex (PATH)";
63808
+ var WINDOWS_START_APPS_ARGS = [
63809
+ "-NoProfile",
63810
+ "-Command",
63811
+ "Get-StartApps -Name 'Codex' | Select-Object -First 1 -ExpandProperty AppID"
63812
+ ];
63813
+ async function detectCodexInstallation(input = {}) {
63814
+ const platform = input.platform ?? process.platform;
63815
+ const env = input.env ?? process.env;
63816
+ const homeDir = input.homeDir ?? homedir6();
63817
+ const exists6 = input.exists ?? existsSync26;
63818
+ const which = input.which ?? bunWhich;
63819
+ const checkedPaths = [CODEX_PATH_CHECK_LABEL];
63820
+ const cliPath = nonEmptyValue(which("codex"));
63821
+ if (cliPath !== undefined)
63822
+ return { found: true, source: "cli", path: cliPath };
63823
+ if (platform === "darwin") {
63824
+ return detectMacCodexInstallation({ homeDir, exists: exists6, checkedPaths });
63825
+ }
63826
+ if (platform === "win32") {
63827
+ return detectWindowsCodexInstallation({
63828
+ env,
63829
+ exists: exists6,
63830
+ checkedPaths,
63831
+ runCommand: input.runCommand ?? defaultRunCommand2
63832
+ });
63833
+ }
63834
+ return missingCodexInstallation(checkedPaths, "Install OpenAI Codex CLI and make sure `codex` is on PATH.");
63835
+ }
63836
+ function formatCodexInstallationWarning(detection) {
63837
+ return [
63838
+ "Codex CLI or desktop app was not detected. LazyCodex will still install the Codex plugin, but Codex itself must be installed to use it.",
63839
+ detection.hint,
63840
+ `Checked: ${detection.checkedPaths.join(", ")}`
63841
+ ].join(`
63842
+ `);
63843
+ }
63844
+ function detectMacCodexInstallation(input) {
63845
+ for (const appPath of macCodexAppPaths(input.homeDir)) {
63846
+ input.checkedPaths.push(appPath);
63847
+ if (input.exists(appPath))
63848
+ return { found: true, source: "mac-app", path: appPath };
63849
+ }
63850
+ for (const dmgPath of macCodexDmgPaths(input.homeDir)) {
63851
+ input.checkedPaths.push(dmgPath);
63852
+ if (input.exists(dmgPath)) {
63853
+ return missingCodexInstallation(input.checkedPaths, `Open ${dmgPath} to install Codex Desktop, or install OpenAI Codex CLI and make sure \`codex\` is on PATH.`, dmgPath);
63854
+ }
63855
+ }
63856
+ return missingCodexInstallation(input.checkedPaths, "Install OpenAI Codex CLI, or install Codex Desktop for macOS.");
63857
+ }
63858
+ async function detectWindowsCodexInstallation(input) {
63859
+ for (const candidate of windowsCodexCliPaths(input.env)) {
63860
+ input.checkedPaths.push(candidate);
63861
+ if (input.exists(candidate))
63862
+ return { found: true, source: "windows-standard-cli", path: candidate };
63863
+ }
63864
+ input.checkedPaths.push("Windows Start Apps: Codex");
63865
+ const appId = await findWindowsCodexStartApp(input.runCommand);
63866
+ if (appId !== null)
63867
+ return { found: true, source: "windows-start-app", appId };
63868
+ return missingCodexInstallation(input.checkedPaths, "Install OpenAI Codex CLI, or install Codex Desktop from Microsoft Store.");
63869
+ }
63870
+ async function findWindowsCodexStartApp(runCommand) {
63871
+ try {
63872
+ const result = await runCommand("powershell.exe", WINDOWS_START_APPS_ARGS);
63873
+ if (!result.success)
63874
+ return null;
63875
+ return nonEmptyValue(result.stdout.trim()) ?? null;
63876
+ } catch (error) {
63877
+ if (error instanceof Error)
63878
+ return null;
63879
+ throw error;
63880
+ }
63881
+ }
63882
+ function macCodexAppPaths(homeDir) {
63883
+ return ["/Applications/Codex.app", join32(homeDir, "Applications", "Codex.app")];
63884
+ }
63885
+ function macCodexDmgPaths(homeDir) {
63886
+ const downloads = join32(homeDir, "Downloads");
63887
+ return [join32(downloads, "codex.dmg"), join32(downloads, "Codex.dmg")];
63888
+ }
63889
+ function windowsCodexCliPaths(env) {
63890
+ const candidates = [];
63891
+ const explicitInstallDir = nonEmptyValue(env.CODEX_INSTALL_DIR);
63892
+ if (explicitInstallDir !== undefined)
63893
+ candidates.push(win322.join(explicitInstallDir, "codex.exe"));
63894
+ const localAppData = nonEmptyValue(env.LOCALAPPDATA) ?? nonEmptyValue(env.LocalAppData);
63895
+ if (localAppData !== undefined) {
63896
+ candidates.push(win322.join(localAppData, "Programs", "OpenAI", "Codex", "bin", "codex.exe"));
63897
+ }
63898
+ return dedupe(candidates);
63899
+ }
63900
+ function missingCodexInstallation(checkedPaths, hint, downloadedInstallerPath) {
63901
+ if (downloadedInstallerPath !== undefined) {
63902
+ return { found: false, checkedPaths, hint, downloadedInstallerPath };
63903
+ }
63904
+ return { found: false, checkedPaths, hint };
63905
+ }
63906
+ function nonEmptyValue(value) {
63907
+ if (value === null || value === undefined)
63908
+ return;
63909
+ const trimmed = value.trim();
63910
+ return trimmed.length > 0 ? trimmed : undefined;
63911
+ }
63912
+ function dedupe(values) {
63913
+ const seen = new Set;
63914
+ const deduped = [];
63915
+ for (const value of values) {
63916
+ if (seen.has(value))
63917
+ continue;
63918
+ seen.add(value);
63919
+ deduped.push(value);
63920
+ }
63921
+ return deduped;
63922
+ }
63923
+ function defaultRunCommand2(command, args) {
63924
+ return new Promise((resolve10) => {
63925
+ execFile(command, [...args], { encoding: "utf8", windowsHide: true }, (error, stdout) => {
63926
+ resolve10({ success: error === null, stdout });
63927
+ });
63928
+ });
63929
+ }
63930
+ // src/cli/install-codex/codex-cleanup.ts
63931
+ import { lstat as lstat6, readFile as readFile11, readdir as readdir4, rm as rm5 } from "fs/promises";
63932
+ import { homedir as homedir7 } from "os";
63933
+ import { isAbsolute as isAbsolute6, join as join33, relative as relative5, resolve as resolve10 } from "path";
63934
+
63935
+ // src/cli/install-codex/codex-cleanup-config.ts
63936
+ import { lstat as lstat5, mkdir as mkdir7, readFile as readFile10, writeFile as writeFile8 } from "fs/promises";
63937
+ import { dirname as dirname15 } from "path";
63938
+ var MANAGED_MARKETPLACES = ["sisyphuslabs", "lazycodex", "code-yeongyu-codex-plugins"];
63939
+ var MANAGED_CODEX_AGENT_NAMES2 = [
63940
+ "codex-ultrawork-reviewer",
63941
+ "explorer",
63942
+ "librarian",
63943
+ "metis",
63944
+ "momus",
63945
+ "plan"
63946
+ ];
63947
+ function cleanupCodexLightConfigText(config) {
63948
+ let nextConfig = config;
63949
+ for (const marketplace of MANAGED_MARKETPLACES) {
63950
+ nextConfig = removeTomlSections2(nextConfig, (header) => header === `marketplaces.${marketplace}`);
63951
+ nextConfig = removeTomlSections2(nextConfig, (header) => isManagedPluginHeader(header, marketplace));
63952
+ nextConfig = removeTomlSections2(nextConfig, (header) => isManagedHookStateHeader(header, marketplace));
63953
+ }
63954
+ nextConfig = removeManagedAgentBlocks(nextConfig);
63955
+ return nextConfig.replace(/\n{3,}/g, `
63956
+
63957
+ `);
63958
+ }
63959
+ async function cleanupCodexConfig(configPath, now) {
63960
+ if (!await configExists(configPath))
63961
+ return { changed: false };
63962
+ const original = await readFile10(configPath, "utf8");
63963
+ const next = cleanupCodexLightConfigText(original);
63964
+ if (next === original)
63965
+ return { changed: false };
63966
+ const backupPath = `${configPath}.backup-${formatBackupTimestamp2(now?.() ?? new Date)}`;
63967
+ await mkdir7(dirname15(configPath), { recursive: true });
63968
+ await writeFile8(backupPath, original);
63969
+ await writeFile8(configPath, `${next.trimEnd()}
63970
+ `);
63971
+ return { changed: true, backupPath };
63972
+ }
63973
+ function removeManagedAgentBlocks(config) {
63974
+ const managedAgentNames = new Set(MANAGED_CODEX_AGENT_NAMES2);
63975
+ return splitTomlSections2(config).filter((section) => {
63976
+ if (section.header === null)
63977
+ return true;
63978
+ const agentName = parseAgentHeaderName2(section.header);
63979
+ if (agentName === null || !managedAgentNames.has(agentName))
63980
+ return true;
63981
+ return !section.text.includes(`config_file = ${JSON.stringify(`./agents/${agentName}.toml`)}`);
63982
+ }).map((section) => section.text).join("");
63983
+ }
63984
+ function isManagedPluginHeader(header, marketplace) {
63985
+ const pluginKey = parsePluginHeaderKey2(header);
63986
+ return pluginKey !== null && pluginKey.endsWith(`@${marketplace}`);
63987
+ }
63988
+ function isManagedHookStateHeader(header, marketplace) {
63989
+ const prefix = "hooks.state.";
63990
+ if (!header.startsWith(prefix))
63991
+ return false;
63992
+ const hookKey = parseJsonString2(header.slice(prefix.length));
63993
+ if (hookKey === null)
63994
+ return false;
63995
+ const separator = hookKey.indexOf(":");
63996
+ if (separator === -1)
63997
+ return false;
63998
+ return hookKey.slice(0, separator).endsWith(`@${marketplace}`);
63999
+ }
64000
+ function removeTomlSections2(config, shouldRemove) {
64001
+ return splitTomlSections2(config).filter((section) => section.header === null || !shouldRemove(section.header)).map((section) => section.text).join("");
64002
+ }
64003
+ function splitTomlSections2(config) {
64004
+ const lines = config.match(/[^\n]*\n?|$/g) ?? [];
64005
+ const sections = [];
64006
+ let current = { header: null, text: "" };
64007
+ for (const line of lines) {
64008
+ if (line.length === 0)
64009
+ break;
64010
+ const header = parseTomlHeader2(line);
64011
+ if (header !== null) {
64012
+ if (current.text.length > 0)
64013
+ sections.push(current);
64014
+ current = { header, text: line };
64015
+ } else {
64016
+ current.text += line;
64017
+ }
64018
+ }
64019
+ if (current.text.length > 0)
64020
+ sections.push(current);
64021
+ return sections;
64022
+ }
64023
+ function parseTomlHeader2(line) {
64024
+ const trimmed = line.trim();
64025
+ if (!trimmed.startsWith("[") || !trimmed.endsWith("]") || trimmed.startsWith("[["))
64026
+ return null;
64027
+ return trimmed.slice(1, -1);
64028
+ }
64029
+ function parsePluginHeaderKey2(header) {
64030
+ const prefix = "plugins.";
64031
+ if (!header.startsWith(prefix))
64032
+ return null;
64033
+ return parseLeadingJsonString2(header.slice(prefix.length));
64034
+ }
64035
+ function parseAgentHeaderName2(header) {
64036
+ const prefix = "agents.";
64037
+ if (!header.startsWith(prefix))
64038
+ return null;
64039
+ const key = header.slice(prefix.length);
64040
+ return key.startsWith('"') ? parseLeadingJsonString2(key) : key;
64041
+ }
64042
+ function parseLeadingJsonString2(value) {
64043
+ if (!value.startsWith('"'))
64044
+ return parseJsonString2(value);
64045
+ let escaped = false;
64046
+ for (let index = 1;index < value.length; index += 1) {
64047
+ const char = value[index];
64048
+ if (escaped) {
64049
+ escaped = false;
64050
+ continue;
64051
+ }
64052
+ if (char === "\\") {
64053
+ escaped = true;
64054
+ continue;
64055
+ }
64056
+ if (char === '"')
64057
+ return parseJsonString2(value.slice(0, index + 1));
64058
+ }
64059
+ return null;
64060
+ }
64061
+ function parseJsonString2(value) {
64062
+ try {
64063
+ const parsed = JSON.parse(value);
64064
+ return typeof parsed === "string" ? parsed : null;
64065
+ } catch (error) {
64066
+ if (error instanceof Error)
64067
+ return null;
64068
+ return null;
64069
+ }
64070
+ }
64071
+ function formatBackupTimestamp2(date) {
64072
+ return date.toISOString().replace(/[:.]/g, "-");
64073
+ }
64074
+ async function configExists(path7) {
64075
+ try {
64076
+ await lstat5(path7);
64077
+ return true;
64078
+ } catch (error) {
64079
+ if (nodeErrorCode2(error) === "ENOENT")
64080
+ return false;
64081
+ throw error;
64082
+ }
64083
+ }
64084
+ function nodeErrorCode2(error) {
64085
+ if (!(error instanceof Error) || !("code" in error))
64086
+ return null;
64087
+ return typeof error.code === "string" ? error.code : null;
64088
+ }
64089
+
64090
+ // src/cli/install-codex/codex-cleanup.ts
64091
+ var INSTALLED_AGENTS_MANIFEST = ".installed-agents.json";
64092
+ async function cleanupCodexLight(input = {}) {
64093
+ const env = input.env ?? process.env;
64094
+ const codexHome = resolve10(input.codexHome ?? env.CODEX_HOME ?? join33(homedir7(), ".codex"));
64095
+ const configPath = join33(codexHome, "config.toml");
64096
+ const agentPaths = await collectInstalledAgentPaths(codexHome);
64097
+ const configCleanup = await cleanupCodexConfig(configPath, input.now);
64098
+ const agentCleanup = await removeManifestListedAgentLinks(codexHome, agentPaths);
64099
+ const removedPaths = [];
64100
+ for (const path7 of managedGlobalStatePaths(codexHome)) {
64101
+ if (await removePathIfExists(path7))
64102
+ removedPaths.push(path7);
64103
+ }
64104
+ const projectDirectory = input.projectDirectory ?? env.OMO_CODEX_PROJECT ?? process.cwd();
64105
+ const projectCleanup = await repairProjectLocalCodexArtifactsBestEffort({
64106
+ startDirectory: projectDirectory,
64107
+ codexHome,
64108
+ now: input.now,
64109
+ log: () => {
64110
+ return;
64111
+ }
64112
+ });
64113
+ return {
64114
+ codexHome,
64115
+ configPath,
64116
+ configChanged: configCleanup.changed,
64117
+ configBackupPath: configCleanup.backupPath,
64118
+ removedPaths,
64119
+ removedAgentLinks: agentCleanup.removed,
64120
+ skippedAgentLinks: agentCleanup.skipped,
64121
+ projectCleanup
64122
+ };
64123
+ }
64124
+ function managedGlobalStatePaths(codexHome) {
64125
+ return [
64126
+ join33(codexHome, "plugins", "cache", "sisyphuslabs"),
64127
+ join33(codexHome, ".tmp", "marketplaces", "sisyphuslabs")
64128
+ ];
64129
+ }
64130
+ async function collectInstalledAgentPaths(codexHome) {
64131
+ const manifestPaths = [
64132
+ join33(codexHome, ".tmp", "marketplaces", "sisyphuslabs", "plugins", "omo", INSTALLED_AGENTS_MANIFEST)
64133
+ ];
64134
+ const versionRoot = join33(codexHome, "plugins", "cache", "sisyphuslabs", "omo");
64135
+ if (await exists6(versionRoot)) {
64136
+ const entries = await readdir4(versionRoot, { withFileTypes: true });
64137
+ for (const entry of entries) {
64138
+ if (entry.isDirectory())
64139
+ manifestPaths.push(join33(versionRoot, entry.name, INSTALLED_AGENTS_MANIFEST));
64140
+ }
64141
+ }
64142
+ const paths = new Set;
64143
+ for (const manifestPath of manifestPaths) {
64144
+ for (const path7 of await readInstalledAgentManifest(manifestPath)) {
64145
+ paths.add(path7);
64146
+ }
64147
+ }
64148
+ return [...paths].sort();
64149
+ }
64150
+ async function readInstalledAgentManifest(manifestPath) {
64151
+ if (!await exists6(manifestPath))
64152
+ return [];
64153
+ const parsed = JSON.parse(await readFile11(manifestPath, "utf8"));
64154
+ if (!isRecord14(parsed) || !Array.isArray(parsed.agents))
64155
+ return [];
64156
+ return parsed.agents.filter((path7) => typeof path7 === "string");
64157
+ }
64158
+ async function removeManifestListedAgentLinks(codexHome, paths) {
64159
+ const agentsDir = join33(codexHome, "agents");
64160
+ const removed = [];
64161
+ const skipped = [];
64162
+ for (const path7 of paths) {
64163
+ if (!isSafeManagedAgentPath(agentsDir, path7)) {
64164
+ skipped.push(path7);
64165
+ continue;
64166
+ }
64167
+ const entryStat = await maybeLstat2(path7);
64168
+ if (entryStat === null)
64169
+ continue;
64170
+ if (entryStat.isDirectory() && !entryStat.isSymbolicLink()) {
64171
+ skipped.push(path7);
64172
+ continue;
64173
+ }
64174
+ await rm5(path7, { force: true });
64175
+ removed.push(path7);
64176
+ }
64177
+ return { removed, skipped };
64178
+ }
64179
+ function isSafeManagedAgentPath(agentsDir, path7) {
64180
+ if (!isAbsolute6(path7))
64181
+ return false;
64182
+ const relativePath = relative5(agentsDir, path7);
64183
+ if (relativePath === "" || relativePath.startsWith("..") || isAbsolute6(relativePath))
64184
+ return false;
64185
+ const fileName = relativePath.split(/[\\/]/).pop();
64186
+ if (fileName === undefined)
64187
+ return false;
64188
+ return MANAGED_CODEX_AGENT_NAMES2.some((agentName) => fileName === `${agentName}.toml`);
64189
+ }
64190
+ async function removePathIfExists(path7) {
64191
+ if (!await exists6(path7))
64192
+ return false;
64193
+ await rm5(path7, { recursive: true, force: true });
64194
+ return true;
64195
+ }
64196
+ async function exists6(path7) {
64197
+ return await maybeLstat2(path7) !== null;
64198
+ }
64199
+ async function maybeLstat2(path7) {
64200
+ try {
64201
+ return await lstat6(path7);
64202
+ } catch (error) {
64203
+ if (nodeErrorCode3(error) === "ENOENT")
64204
+ return null;
64205
+ throw error;
64206
+ }
64207
+ }
64208
+ function nodeErrorCode3(error) {
64209
+ if (!(error instanceof Error) || !("code" in error))
64210
+ return null;
64211
+ return typeof error.code === "string" ? error.code : null;
64212
+ }
64213
+ function isRecord14(value) {
64214
+ return typeof value === "object" && value !== null && !Array.isArray(value);
64215
+ }
63055
64216
  // src/cli/star-request.ts
64217
+ import { execFile as execFile2 } from "child_process";
64218
+ import { promisify as promisify2 } from "util";
63056
64219
  var STAR_REPOSITORIES = [
63057
64220
  "code-yeongyu/oh-my-openagent",
63058
64221
  "code-yeongyu/lazycodex"
63059
64222
  ];
63060
- function formatGitHubStarCommand(repository) {
63061
- return `gh api --silent --method PUT /user/starred/${repository} >/dev/null 2>&1 || true`;
64223
+ var PLATFORM_REPOSITORIES = {
64224
+ opencode: ["code-yeongyu/oh-my-openagent"],
64225
+ codex: STAR_REPOSITORIES,
64226
+ both: STAR_REPOSITORIES
64227
+ };
64228
+ var execFileAsync = promisify2(execFile2);
64229
+ async function runGitHubStarCommand(repository) {
64230
+ await execFileAsync("gh", ["api", "--silent", "--method", "PUT", `/user/starred/${repository}`]);
64231
+ }
64232
+ async function starGitHubRepositories(platform = "both", runCommand = runGitHubStarCommand) {
64233
+ const results = [];
64234
+ for (const repository of PLATFORM_REPOSITORIES[platform]) {
64235
+ try {
64236
+ await runCommand(repository);
64237
+ results.push({ repository, ok: true });
64238
+ } catch (error) {
64239
+ results.push({ repository, ok: false, error: error instanceof Error ? error.message : String(error) });
64240
+ }
64241
+ }
64242
+ return results;
63062
64243
  }
63063
64244
 
63064
64245
  // src/cli/cli-installer.ts
@@ -63133,10 +64314,10 @@ async function runCliInstaller(args, version2) {
63133
64314
  printSuccess(`Config written ${SYMBOLS.arrow} ${import_picocolors3.default.dim(omoResult.configPath)}`);
63134
64315
  }
63135
64316
  printBox(formatConfigSummary(config), isUpdate ? "Updated Configuration" : "Installation Complete");
63136
- if (!config.hasClaude) {
64317
+ if (config.hasOpenCode && !config.hasClaude) {
63137
64318
  printInfo("Note: Sisyphus agent performs best with Claude Opus 4.5+. " + "Other models work but may have reduced orchestration quality.");
63138
64319
  }
63139
- if (!config.hasClaude && !config.hasOpenAI && !config.hasGemini && !config.hasCopilot && !config.hasOpencodeZen && !config.hasVercelAiGateway) {
64320
+ if (config.hasOpenCode && !config.hasClaude && !config.hasOpenAI && !config.hasGemini && !config.hasCopilot && !config.hasOpencodeZen && !config.hasVercelAiGateway) {
63140
64321
  printWarning("No model providers configured. Using opencode/big-pickle as fallback.");
63141
64322
  }
63142
64323
  console.log(`${SYMBOLS.star} ${import_picocolors3.default.bold(import_picocolors3.default.green(isUpdate ? "Configuration updated!" : "Installation complete!"))}`);
@@ -63165,11 +64346,7 @@ async function runCliInstaller(args, version2) {
63165
64346
  printBox(`${import_picocolors3.default.bold("Pro Tip:")} Include ${import_picocolors3.default.cyan("ultrawork")} (or ${import_picocolors3.default.cyan("ulw")}) in your prompt.
63166
64347
  ` + `All features work like magic-parallel agents, background tasks,
63167
64348
  ` + `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))}`);
63171
- }
63172
- console.log();
64349
+ await maybePromptForGitHubStars(config.platform);
63173
64350
  console.log(import_picocolors3.default.dim("oMoMoMoMo... Enjoy!"));
63174
64351
  console.log();
63175
64352
  if (hasOpenCode && (config.hasClaude || config.hasGemini || config.hasCopilot) && !args.skipAuth) {
@@ -63180,6 +64357,34 @@ async function runCliInstaller(args, version2) {
63180
64357
  }
63181
64358
  return 0;
63182
64359
  }
64360
+ async function maybePromptForGitHubStars(platform) {
64361
+ if (!process.stdin.isTTY || !process.stdout.isTTY)
64362
+ return;
64363
+ const readline = createInterface2({ input: process.stdin, output: process.stdout });
64364
+ try {
64365
+ const answer = await readline.question(`${SYMBOLS.star} ${import_picocolors3.default.yellow("Star the repos on GitHub?")} ${import_picocolors3.default.dim("[y/N]")} `);
64366
+ if (!isYes(answer))
64367
+ return;
64368
+ } finally {
64369
+ readline.close();
64370
+ }
64371
+ const results = await starGitHubRepositories(platform);
64372
+ const failed = results.filter((result) => !result.ok);
64373
+ if (failed.length === 0) {
64374
+ printSuccess("Starred GitHub repositories");
64375
+ console.log();
64376
+ return;
64377
+ }
64378
+ printWarning("Could not star every repository. Make sure GitHub CLI is installed and authenticated.");
64379
+ for (const result of failed) {
64380
+ console.log(` ${SYMBOLS.bullet} ${result.repository}`);
64381
+ }
64382
+ console.log();
64383
+ }
64384
+ function isYes(value) {
64385
+ const normalized = value.trim().toLowerCase();
64386
+ return normalized === "y" || normalized === "yes";
64387
+ }
63183
64388
 
63184
64389
  // node_modules/.bun/@clack+core@1.3.1/node_modules/@clack/core/dist/index.mjs
63185
64390
  import { styleText as v } from "util";
@@ -63828,6 +65033,24 @@ var Q = class extends m {
63828
65033
  }
63829
65034
  }
63830
65035
  };
65036
+
65037
+ class X extends m {
65038
+ get cursor() {
65039
+ return this.value ? 0 : 1;
65040
+ }
65041
+ get _value() {
65042
+ return this.cursor === 0;
65043
+ }
65044
+ constructor(t) {
65045
+ super(t, false), this.value = !!t.initialValue, this.on("userInput", () => {
65046
+ this.value = this._value;
65047
+ }), this.on("confirm", (s) => {
65048
+ this.output.write(import_sisteransi.cursor.move(0, -1)), this.value = s, this.state = "submit", this.close();
65049
+ }), this.on("cursor", () => {
65050
+ this.value = !this.value;
65051
+ });
65052
+ }
65053
+ }
63831
65054
  var Z = { Y: { type: "year", len: 4 }, M: { type: "month", len: 2 }, D: { type: "day", len: 2 } };
63832
65055
  function P(r) {
63833
65056
  return [...r].map((t) => Z[t]);
@@ -64265,6 +65488,33 @@ var F2 = ({ cursor: t, options: i2, style: s, output: r = process.stdout, maxIte
64265
65488
  S2.push(G2);
64266
65489
  return h2 && S2.push(l), S2;
64267
65490
  };
65491
+ var ue = (t) => {
65492
+ const i2 = t.active ?? "Yes", s = t.inactive ?? "No";
65493
+ return new X({ active: i2, inactive: s, signal: t.signal, input: t.input, output: t.output, initialValue: t.initialValue ?? true, render() {
65494
+ 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)}
65495
+ ` : ""}${a}
65496
+ `, o = this.value ? i2 : s;
65497
+ switch (this.state) {
65498
+ case "submit": {
65499
+ const l = r ? `${e("gray", $2)} ` : "";
65500
+ return `${c}${l}${e("dim", o)}`;
65501
+ }
65502
+ case "cancel": {
65503
+ const l = r ? `${e("gray", $2)} ` : "";
65504
+ return `${c}${l}${e(["strikethrough", "dim"], o)}${r ? `
65505
+ ${e("gray", $2)}` : ""}`;
65506
+ }
65507
+ default: {
65508
+ const l = r ? `${e("cyan", $2)} ` : "", d = r ? e("cyan", x2) : "";
65509
+ return `${c}${l}${this.value ? `${e("green", z2)} ${i2}` : `${e("dim", U2)} ${e("dim", i2)}`}${t.vertical ? r ? `
65510
+ ${e("cyan", $2)} ` : `
65511
+ ` : ` ${e("dim", "/")} `}${this.value ? `${e("dim", U2)} ${e("dim", s)}` : `${e("green", z2)} ${s}`}
65512
+ ${d}
65513
+ `;
65514
+ }
65515
+ }
65516
+ } }).prompt();
65517
+ };
64268
65518
  var R2 = { message: (t = [], { symbol: i2 = e("gray", $2), secondarySymbol: s = e("gray", $2), output: r = process.stdout, spacing: u = 1, withGuide: n } = {}) => {
64269
65519
  const a = [], c = n ?? h.withGuide, o = c ? s : "", l = c ? `${i2} ` : "", d = c ? `${s} ` : "";
64270
65520
  for (let p2 = 0;p2 < u; p2++)
@@ -64687,15 +65937,19 @@ async function runTuiInstaller(args, version2) {
64687
65937
  }
64688
65938
  spinner.stop(`Config written to ${import_picocolors4.default.cyan(omoResult.configPath)}`);
64689
65939
  }
64690
- if (!config.hasClaude) {
65940
+ if (config.hasOpenCode && !config.hasClaude) {
64691
65941
  R2.info(`${import_picocolors4.default.bold("Note:")} Sisyphus agent performs best with Claude Opus 4.5+.
64692
65942
  ` + `Other models work but may have reduced orchestration quality.`);
64693
65943
  }
64694
- if (!config.hasClaude && !config.hasOpenAI && !config.hasGemini && !config.hasCopilot && !config.hasOpencodeZen && !config.hasVercelAiGateway) {
65944
+ if (config.hasOpenCode && !config.hasClaude && !config.hasOpenAI && !config.hasGemini && !config.hasCopilot && !config.hasOpencodeZen && !config.hasVercelAiGateway) {
64695
65945
  R2.warn("No model providers configured. Using opencode/big-pickle as fallback.");
64696
65946
  }
64697
65947
  Se(formatConfigSummary(config), isUpdate ? "Updated Configuration" : "Installation Complete");
64698
65948
  if (config.hasCodex) {
65949
+ const codexInstallation = await detectCodexInstallation();
65950
+ if (!codexInstallation.found) {
65951
+ R2.warn(formatCodexInstallationWarning(codexInstallation));
65952
+ }
64699
65953
  spinner.start("Installing Codex harness adapter");
64700
65954
  try {
64701
65955
  const codexResult = await runCodexInstaller({ autonomousPermissions: config.codexAutonomous });
@@ -64720,9 +65974,20 @@ async function runTuiInstaller(args, version2) {
64720
65974
  Se(`Include ${import_picocolors4.default.cyan("ultrawork")} (or ${import_picocolors4.default.cyan("ulw")}) in your prompt.
64721
65975
  ` + `All features work like magic-parallel agents, background tasks,
64722
65976
  ` + `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))}`);
65977
+ const shouldStar = await ue({
65978
+ message: "Star the repos on GitHub?",
65979
+ initialValue: false
65980
+ });
65981
+ if (!q(shouldStar) && shouldStar) {
65982
+ spinner.start("Starring GitHub repositories");
65983
+ const results = await starGitHubRepositories(selectedPlatform);
65984
+ const failed = results.filter((result) => !result.ok);
65985
+ if (failed.length === 0) {
65986
+ spinner.stop("GitHub repositories starred");
65987
+ } else {
65988
+ spinner.stop("Could not star every repository");
65989
+ R2.warn("Make sure GitHub CLI is installed and authenticated.");
65990
+ }
64726
65991
  }
64727
65992
  ye(import_picocolors4.default.green("oMoMoMoMo... Enjoy!"));
64728
65993
  if (config.hasOpenCode && (config.hasClaude || config.hasGemini || config.hasCopilot) && !args.skipAuth) {
@@ -64751,6 +66016,71 @@ async function install(args) {
64751
66016
  return args.tui ? runTuiInstaller(args, VERSION) : runCliInstaller(args, VERSION);
64752
66017
  }
64753
66018
 
66019
+ // src/cli/cleanup.ts
66020
+ function resolveCleanupPlatform(options, invocationName = process.env.OMO_INVOCATION_NAME) {
66021
+ if (options.platform !== undefined)
66022
+ return options.platform;
66023
+ return invocationName === "lazycodex" || invocationName === "lazycodex-ai" ? "codex" : undefined;
66024
+ }
66025
+ async function cleanup(options) {
66026
+ if (options.platform !== "codex") {
66027
+ console.error("Error: cleanup currently supports only --platform=codex");
66028
+ return 1;
66029
+ }
66030
+ const result = await cleanupCodexLight({
66031
+ codexHome: options.codexHome,
66032
+ projectDirectory: options.project
66033
+ });
66034
+ if (options.json === true) {
66035
+ console.log(JSON.stringify(result, null, 2));
66036
+ return 0;
66037
+ }
66038
+ console.log(`Codex Light cleanup complete: ${result.codexHome}`);
66039
+ if (result.configChanged) {
66040
+ console.log(`- Updated ${result.configPath}`);
66041
+ if (result.configBackupPath !== undefined)
66042
+ console.log(`- Backup ${result.configBackupPath}`);
66043
+ } else {
66044
+ console.log(`- No managed Codex config blocks found in ${result.configPath}`);
66045
+ }
66046
+ for (const path7 of result.removedPaths) {
66047
+ console.log(`- Removed ${path7}`);
66048
+ }
66049
+ for (const path7 of result.removedAgentLinks) {
66050
+ console.log(`- Removed managed agent link ${path7}`);
66051
+ }
66052
+ for (const path7 of result.skippedAgentLinks) {
66053
+ console.log(`- Skipped agent path outside managed scope ${path7}`);
66054
+ }
66055
+ if (result.projectCleanup.changed) {
66056
+ console.log(`- Repaired project-local Codex config ${result.projectCleanup.configPath}`);
66057
+ }
66058
+ for (const artifact of result.projectCleanup.artifacts) {
66059
+ console.log(`- Left project-local artifact in place ${artifact.path}`);
66060
+ }
66061
+ return 0;
66062
+ }
66063
+
66064
+ // src/cli/cleanup-command.ts
66065
+ function configureCleanupCommand(program2) {
66066
+ 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", `
66067
+ Examples:
66068
+ $ npx lazycodex-ai cleanup
66069
+ $ omo cleanup --platform=codex
66070
+ $ omo cleanup --platform=codex --project /path/to/project
66071
+ `).action(async (options) => {
66072
+ const rootOptions = program2.opts();
66073
+ const platform = resolveCleanupPlatform({ platform: options.platform ?? rootOptions.platform });
66074
+ const exitCode = await cleanup({
66075
+ platform,
66076
+ codexHome: options.codexHome,
66077
+ project: options.project,
66078
+ json: options.json ?? false
66079
+ });
66080
+ process.exit(exitCode);
66081
+ });
66082
+ }
66083
+
64754
66084
  // src/cli/run/runner.ts
64755
66085
  var import_picocolors14 = __toESM(require_picocolors(), 1);
64756
66086
 
@@ -64758,6 +66088,7 @@ var import_picocolors14 = __toESM(require_picocolors(), 1);
64758
66088
  function createEventState() {
64759
66089
  return {
64760
66090
  mainSessionIdle: false,
66091
+ mainSessionStarted: false,
64761
66092
  mainSessionError: false,
64762
66093
  lastError: null,
64763
66094
  lastOutput: "",
@@ -65173,10 +66504,12 @@ function handleSessionStatus(ctx, payload, state) {
65173
66504
  return;
65174
66505
  if (props?.status?.type === "busy") {
65175
66506
  state.mainSessionIdle = false;
66507
+ state.mainSessionStarted = true;
65176
66508
  } else if (props?.status?.type === "idle") {
65177
66509
  state.mainSessionIdle = true;
65178
66510
  } else if (props?.status?.type === "retry") {
65179
66511
  state.mainSessionIdle = false;
66512
+ state.mainSessionStarted = true;
65180
66513
  }
65181
66514
  }
65182
66515
  function handleSessionError(ctx, payload, state) {
@@ -65216,6 +66549,7 @@ function handleMessagePartUpdated(ctx, payload, state) {
65216
66549
  const padded = writePaddedText(newText, state.thinkingAtLineStart);
65217
66550
  process.stdout.write(import_picocolors7.default.dim(padded.output));
65218
66551
  state.thinkingAtLineStart = padded.atLineStart;
66552
+ state.mainSessionStarted = true;
65219
66553
  state.hasReceivedMeaningfulWork = true;
65220
66554
  }
65221
66555
  state.lastReasoningText = reasoningText;
@@ -65228,6 +66562,7 @@ function handleMessagePartUpdated(ctx, payload, state) {
65228
66562
  const padded = writePaddedText(newText, state.textAtLineStart);
65229
66563
  process.stdout.write(padded.output);
65230
66564
  state.textAtLineStart = padded.atLineStart;
66565
+ state.mainSessionStarted = true;
65231
66566
  state.hasReceivedMeaningfulWork = true;
65232
66567
  }
65233
66568
  state.lastPartText = part.text;
@@ -65239,6 +66574,7 @@ function handleMessagePartUpdated(ctx, payload, state) {
65239
66574
  }
65240
66575
  }
65241
66576
  if (part.type === "tool") {
66577
+ state.mainSessionStarted = true;
65242
66578
  handleToolPart(ctx, part, state);
65243
66579
  }
65244
66580
  }
@@ -65264,6 +66600,7 @@ function handleMessagePartDelta(ctx, payload, state) {
65264
66600
  process.stdout.write(import_picocolors7.default.dim(padded2.output));
65265
66601
  state.thinkingAtLineStart = padded2.atLineStart;
65266
66602
  state.lastReasoningText += delta;
66603
+ state.mainSessionStarted = true;
65267
66604
  state.hasReceivedMeaningfulWork = true;
65268
66605
  return;
65269
66606
  }
@@ -65272,6 +66609,7 @@ function handleMessagePartDelta(ctx, payload, state) {
65272
66609
  process.stdout.write(padded.output);
65273
66610
  state.textAtLineStart = padded.atLineStart;
65274
66611
  state.lastPartText += delta;
66612
+ state.mainSessionStarted = true;
65275
66613
  state.hasReceivedMeaningfulWork = true;
65276
66614
  }
65277
66615
  function handleToolPart(_ctx, part, state) {
@@ -65317,11 +66655,15 @@ function handleMessageUpdated(ctx, payload, state) {
65317
66655
  if (messageID && role) {
65318
66656
  state.messageRoleById[messageID] = role;
65319
66657
  }
66658
+ if (messageID) {
66659
+ state.mainSessionStarted = true;
66660
+ }
65320
66661
  if (props?.info?.role !== "assistant")
65321
66662
  return;
65322
66663
  const isNewMessage = !messageID || messageID !== state.currentMessageId;
65323
66664
  if (isNewMessage) {
65324
66665
  state.currentMessageId = messageID;
66666
+ state.mainSessionStarted = true;
65325
66667
  state.hasReceivedMeaningfulWork = true;
65326
66668
  state.messageCount++;
65327
66669
  state.lastPartText = "";
@@ -65357,6 +66699,7 @@ function handleToolExecute(ctx, payload, state) {
65357
66699
  return;
65358
66700
  const toolName = props?.name || "unknown";
65359
66701
  state.currentTool = toolName;
66702
+ state.mainSessionStarted = true;
65360
66703
  const header = formatToolHeader(toolName, props?.input ?? {});
65361
66704
  const suffix = header.description ? ` ${import_picocolors7.default.dim(header.description)}` : "";
65362
66705
  state.hasReceivedMeaningfulWork = true;
@@ -65451,7 +66794,7 @@ async function processEvents(ctx, stream, state) {
65451
66794
  }
65452
66795
  // src/plugin-config.ts
65453
66796
  import * as fs4 from "fs";
65454
- import { homedir as homedir6 } from "os";
66797
+ import { homedir as homedir8 } from "os";
65455
66798
  import * as path7 from "path";
65456
66799
 
65457
66800
  // node_modules/.bun/zod@4.4.3/node_modules/zod/v4/classic/external.js
@@ -67065,8 +68408,8 @@ function emoji() {
67065
68408
  }
67066
68409
  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
68410
  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 ?? ":");
68411
+ var mac = (delimiter2) => {
68412
+ const escapedDelim = escapeRegex(delimiter2 ?? ":");
67070
68413
  return new RegExp(`^(?:[0-9A-F]{2}${escapedDelim}){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}${escapedDelim}){5}[0-9a-f]{2}$`);
67071
68414
  };
67072
68415
  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 +73156,9 @@ var error17 = () => {
71813
73156
  if (issue2.values.length === 2) {
71814
73157
  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
73158
  }
71816
- const lastValue = stringified[stringified.length - 1];
73159
+ const lastValue2 = stringified[stringified.length - 1];
71817
73160
  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}`;
73161
+ 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
73162
  }
71820
73163
  case "too_big": {
71821
73164
  const sizing = getSizing(issue2.origin);
@@ -80025,7 +81368,6 @@ var GitMasterConfigSchema = exports_external.object({
80025
81368
  // src/config/schema/hooks.ts
80026
81369
  var HookNameSchema = exports_external.enum([
80027
81370
  "todo-continuation-enforcer",
80028
- "context-window-monitor",
80029
81371
  "session-recovery",
80030
81372
  "session-notification",
80031
81373
  "comment-checker",
@@ -80480,7 +81822,7 @@ function addAgentOrderWarnings(configPath, agentOrder) {
80480
81822
  });
80481
81823
  }
80482
81824
  function resolveHomeDirectory() {
80483
- return process.env.HOME ?? process.env.USERPROFILE ?? homedir6();
81825
+ return process.env.HOME ?? process.env.USERPROFILE ?? homedir8();
80484
81826
  }
80485
81827
  function resolveConfigPathAfterLegacyMigration(detectedPath) {
80486
81828
  if (!path7.basename(detectedPath).startsWith(LEGACY_CONFIG_BASENAME)) {
@@ -80776,7 +82118,7 @@ function loadPluginConfig(directory, ctx) {
80776
82118
  // node_modules/.bun/@opencode-ai+sdk@1.15.10/node_modules/@opencode-ai/sdk/dist/gen/core/serverSentEvents.gen.js
80777
82119
  var createSseClient = ({ onSseError, onSseEvent, responseTransformer, responseValidator, sseDefaultRetryDelay, sseMaxRetryAttempts, sseMaxRetryDelay, sseSleepFn, url: url2, ...options }) => {
80778
82120
  let lastEventId;
80779
- const sleep = sseSleepFn ?? ((ms) => new Promise((resolve9) => setTimeout(resolve9, ms)));
82121
+ const sleep = sseSleepFn ?? ((ms) => new Promise((resolve11) => setTimeout(resolve11, ms)));
80780
82122
  const createStream = async function* () {
80781
82123
  let retryDelay = sseDefaultRetryDelay ?? 3000;
80782
82124
  let attempt = 0;
@@ -82239,7 +83581,7 @@ async function createOpencodeServer(options) {
82239
83581
  }
82240
83582
  });
82241
83583
  let clear = () => {};
82242
- const url2 = await new Promise((resolve9, reject) => {
83584
+ const url2 = await new Promise((resolve11, reject) => {
82243
83585
  const id = setTimeout(() => {
82244
83586
  clear();
82245
83587
  stop(proc);
@@ -82265,7 +83607,7 @@ async function createOpencodeServer(options) {
82265
83607
  }
82266
83608
  clearTimeout(id);
82267
83609
  resolved = true;
82268
- resolve9(match[1]);
83610
+ resolve11(match[1]);
82269
83611
  return;
82270
83612
  }
82271
83613
  }
@@ -82320,7 +83662,7 @@ var import_picocolors9 = __toESM(require_picocolors(), 1);
82320
83662
 
82321
83663
  // src/cli/run/opencode-binary-resolver.ts
82322
83664
  init_spawn_with_windows_hide();
82323
- import { delimiter, dirname as dirname15, join as join30 } from "path";
83665
+ import { delimiter as delimiter2, dirname as dirname17, join as join35 } from "path";
82324
83666
  var OPENCODE_COMMANDS = ["opencode", "opencode-desktop"];
82325
83667
  var WINDOWS_SUFFIXES = ["", ".exe", ".cmd", ".bat", ".ps1"];
82326
83668
  function getCommandCandidates(platform) {
@@ -82341,9 +83683,9 @@ function collectCandidateBinaryPaths(pathEnv, which = Bun.which, platform = proc
82341
83683
  for (const command of commandCandidates) {
82342
83684
  addCandidate(which(command));
82343
83685
  }
82344
- for (const entry of (pathEnv ?? "").split(delimiter).filter(Boolean)) {
83686
+ for (const entry of (pathEnv ?? "").split(delimiter2).filter(Boolean)) {
82345
83687
  for (const command of commandCandidates) {
82346
- addCandidate(join30(entry, command));
83688
+ addCandidate(join35(entry, command));
82347
83689
  }
82348
83690
  }
82349
83691
  return candidates;
@@ -82370,9 +83712,9 @@ async function findWorkingOpencodeBinary(pathEnv = process.env.PATH, probe = can
82370
83712
  return null;
82371
83713
  }
82372
83714
  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);
83715
+ const preferredDir = dirname17(binaryPath);
83716
+ const existing = (pathEnv ?? "").split(delimiter2).filter((entry) => entry.length > 0 && entry !== preferredDir);
83717
+ return [preferredDir, ...existing].join(delimiter2);
82376
83718
  }
82377
83719
  async function withWorkingOpencodePath(startServer, finder = findWorkingOpencodeBinary) {
82378
83720
  const originalPath = process.env.PATH;
@@ -82534,7 +83876,7 @@ async function resolveSession(options) {
82534
83876
  if (attempt < SESSION_CREATE_MAX_RETRIES) {
82535
83877
  const delay = SESSION_CREATE_RETRY_DELAY_MS * attempt;
82536
83878
  console.log(import_picocolors10.default.dim(` Retrying in ${delay}ms...`));
82537
- await new Promise((resolve9) => setTimeout(resolve9, delay));
83879
+ await new Promise((resolve11) => setTimeout(resolve11, delay));
82538
83880
  }
82539
83881
  continue;
82540
83882
  }
@@ -82545,7 +83887,7 @@ async function resolveSession(options) {
82545
83887
  if (attempt < SESSION_CREATE_MAX_RETRIES) {
82546
83888
  const delay = SESSION_CREATE_RETRY_DELAY_MS * attempt;
82547
83889
  console.log(import_picocolors10.default.dim(` Retrying in ${delay}ms...`));
82548
- await new Promise((resolve9) => setTimeout(resolve9, delay));
83890
+ await new Promise((resolve11) => setTimeout(resolve11, delay));
82549
83891
  }
82550
83892
  }
82551
83893
  throw new Error("Failed to create session after all retries");
@@ -82756,7 +84098,7 @@ var BOULDER_STATE_PATH = `${BOULDER_DIR}/${BOULDER_FILE}`;
82756
84098
  var NOTEPAD_DIR = "notepads";
82757
84099
  var NOTEPAD_BASE_PATH = `${BOULDER_DIR}/${NOTEPAD_DIR}`;
82758
84100
  // packages/boulder-state/src/top-level-task.ts
82759
- import { existsSync as existsSync25, readFileSync as readFileSync15 } from "fs";
84101
+ import { existsSync as existsSync28, readFileSync as readFileSync15 } from "fs";
82760
84102
  var TODO_HEADING_PATTERN = /^##\s+TODOs\b/i;
82761
84103
  var FINAL_VERIFICATION_HEADING_PATTERN = /^##\s+Final Verification Wave\b/i;
82762
84104
  var SECOND_LEVEL_HEADING_PATTERN = /^##\s+/;
@@ -82779,7 +84121,7 @@ function buildTaskRef(section, taskLabel) {
82779
84121
  };
82780
84122
  }
82781
84123
  function readCurrentTopLevelTask(planPath) {
82782
- if (!existsSync25(planPath)) {
84124
+ if (!existsSync28(planPath)) {
82783
84125
  return null;
82784
84126
  }
82785
84127
  try {
@@ -82808,13 +84150,13 @@ function readCurrentTopLevelTask(planPath) {
82808
84150
  }
82809
84151
  }
82810
84152
  // 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";
84153
+ import { existsSync as existsSync29 } from "fs";
84154
+ import { isAbsolute as isAbsolute7, join as join36, relative as relative6, resolve as resolve11 } from "path";
82813
84155
  function getBoulderFilePath(directory) {
82814
- return join31(directory, BOULDER_DIR, BOULDER_FILE);
84156
+ return join36(directory, BOULDER_DIR, BOULDER_FILE);
82815
84157
  }
82816
84158
  function resolveTrackedPath(baseDirectory, trackedPath) {
82817
- return isAbsolute6(trackedPath) ? resolve9(trackedPath) : resolve9(baseDirectory, trackedPath);
84159
+ return isAbsolute7(trackedPath) ? resolve11(trackedPath) : resolve11(baseDirectory, trackedPath);
82818
84160
  }
82819
84161
  function resolveBoulderPlanPath(directory, state) {
82820
84162
  const absolutePlanPath = resolveTrackedPath(directory, state.active_plan);
@@ -82822,20 +84164,20 @@ function resolveBoulderPlanPath(directory, state) {
82822
84164
  if (!worktreePath) {
82823
84165
  return absolutePlanPath;
82824
84166
  }
82825
- const absoluteDirectory = resolve9(directory);
82826
- const relativePlanPath = relative5(absoluteDirectory, absolutePlanPath);
82827
- if (relativePlanPath.length === 0 || relativePlanPath.startsWith("..") || isAbsolute6(relativePlanPath)) {
84167
+ const absoluteDirectory = resolve11(directory);
84168
+ const relativePlanPath = relative6(absoluteDirectory, absolutePlanPath);
84169
+ if (relativePlanPath.length === 0 || relativePlanPath.startsWith("..") || isAbsolute7(relativePlanPath)) {
82828
84170
  return absolutePlanPath;
82829
84171
  }
82830
84172
  const absoluteWorktreePath = resolveTrackedPath(directory, worktreePath);
82831
- const worktreePlanPath = resolve9(absoluteWorktreePath, relativePlanPath);
82832
- return existsSync26(worktreePlanPath) ? worktreePlanPath : absolutePlanPath;
84173
+ const worktreePlanPath = resolve11(absoluteWorktreePath, relativePlanPath);
84174
+ return existsSync29(worktreePlanPath) ? worktreePlanPath : absolutePlanPath;
82833
84175
  }
82834
84176
  function resolveBoulderPlanPathForWork(directory, work) {
82835
84177
  return resolveBoulderPlanPath(directory, work);
82836
84178
  }
82837
84179
  // packages/boulder-state/src/storage/plan-progress.ts
82838
- import { existsSync as existsSync27, readFileSync as readFileSync16, readdirSync as readdirSync4, statSync as statSync4 } from "fs";
84180
+ import { existsSync as existsSync30, readFileSync as readFileSync16, readdirSync as readdirSync4, statSync as statSync4 } from "fs";
82839
84181
  var TODO_HEADING_PATTERN2 = /^##\s+TODOs\b/i;
82840
84182
  var FINAL_VERIFICATION_HEADING_PATTERN2 = /^##\s+Final Verification Wave\b/i;
82841
84183
  var SECOND_LEVEL_HEADING_PATTERN2 = /^##\s+/;
@@ -82844,7 +84186,7 @@ var CHECKED_CHECKBOX_PATTERN = /^(\s*)[-*]\s*\[[xX]\]\s*(.+)$/;
82844
84186
  var TODO_TASK_PATTERN2 = /^\d+\.\s+/;
82845
84187
  var FINAL_WAVE_TASK_PATTERN2 = /^F\d+\.\s+/i;
82846
84188
  function getPlanProgress(planPath) {
82847
- if (!existsSync27(planPath)) {
84189
+ if (!existsSync30(planPath)) {
82848
84190
  return { total: 0, completed: 0, isComplete: false };
82849
84191
  }
82850
84192
  try {
@@ -82964,10 +84306,10 @@ function selectMirrorWork(state) {
82964
84306
  return sorted[0] ?? null;
82965
84307
  }
82966
84308
  // packages/boulder-state/src/storage/read-state.ts
82967
- import { existsSync as existsSync28, readFileSync as readFileSync17 } from "fs";
84309
+ import { existsSync as existsSync31, readFileSync as readFileSync17 } from "fs";
82968
84310
  function readBoulderState(directory) {
82969
84311
  const filePath = getBoulderFilePath(directory);
82970
- if (!existsSync28(filePath)) {
84312
+ if (!existsSync31(filePath)) {
82971
84313
  return null;
82972
84314
  }
82973
84315
  try {
@@ -83047,14 +84389,14 @@ function getSessionAgent(sessionID) {
83047
84389
  // src/features/run-continuation-state/constants.ts
83048
84390
  var CONTINUATION_MARKER_DIR = ".omo/run-continuation";
83049
84391
  // 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";
84392
+ import { existsSync as existsSync32, mkdirSync as mkdirSync9, readFileSync as readFileSync18, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "fs";
84393
+ import { join as join37 } from "path";
83052
84394
  function getMarkerPath(directory, sessionID) {
83053
- return join32(directory, CONTINUATION_MARKER_DIR, `${sessionID}.json`);
84395
+ return join37(directory, CONTINUATION_MARKER_DIR, `${sessionID}.json`);
83054
84396
  }
83055
84397
  function readContinuationMarker(directory, sessionID) {
83056
84398
  const markerPath = getMarkerPath(directory, sessionID);
83057
- if (!existsSync29(markerPath))
84399
+ if (!existsSync32(markerPath))
83058
84400
  return null;
83059
84401
  try {
83060
84402
  const raw = readFileSync18(markerPath, "utf-8");
@@ -83120,7 +84462,7 @@ async function isSessionInBoulderLineage(input) {
83120
84462
  init_shared();
83121
84463
  init_compaction_marker();
83122
84464
  import { readFileSync as readFileSync19, readdirSync as readdirSync5 } from "fs";
83123
- import { join as join33 } from "path";
84465
+ import { join as join38 } from "path";
83124
84466
  var defaultSessionLastAgentDeps = {
83125
84467
  getMessageDir,
83126
84468
  isSqliteBackend,
@@ -83174,7 +84516,7 @@ async function getLastAgentFromSession(sessionID, client3, deps = {}) {
83174
84516
  try {
83175
84517
  const messages = readdirSync5(messageDir).filter((fileName) => fileName.endsWith(".json")).map((fileName) => {
83176
84518
  try {
83177
- const content = readFileSync19(join33(messageDir, fileName), "utf-8");
84519
+ const content = readFileSync19(join38(messageDir, fileName), "utf-8");
83178
84520
  const parsed = JSON.parse(content);
83179
84521
  return {
83180
84522
  fileName,
@@ -83210,8 +84552,8 @@ init_agent_display_names();
83210
84552
 
83211
84553
  // src/hooks/ralph-loop/storage.ts
83212
84554
  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";
84555
+ import { existsSync as existsSync33, readFileSync as readFileSync20, writeFileSync as writeFileSync7, unlinkSync as unlinkSync5, mkdirSync as mkdirSync10 } from "fs";
84556
+ import { dirname as dirname18, join as join39 } from "path";
83215
84557
 
83216
84558
  // src/hooks/ralph-loop/constants.ts
83217
84559
  var DEFAULT_STATE_FILE = ".omo/ralph-loop.local.md";
@@ -83220,11 +84562,11 @@ var DEFAULT_COMPLETION_PROMISE = "DONE";
83220
84562
 
83221
84563
  // src/hooks/ralph-loop/storage.ts
83222
84564
  function getStateFilePath(directory, customPath) {
83223
- return customPath ? join34(directory, customPath) : join34(directory, DEFAULT_STATE_FILE);
84565
+ return customPath ? join39(directory, customPath) : join39(directory, DEFAULT_STATE_FILE);
83224
84566
  }
83225
84567
  function readState(directory, customPath) {
83226
84568
  const filePath = getStateFilePath(directory, customPath);
83227
- if (!existsSync30(filePath)) {
84569
+ if (!existsSync33(filePath)) {
83228
84570
  return null;
83229
84571
  }
83230
84572
  try {
@@ -83449,13 +84791,14 @@ async function pollForCompletion(ctx, eventState, abortController, options = {})
83449
84791
  const minStabilizationMs = rawMinStabilizationMs > 0 ? rawMinStabilizationMs : MIN_STABILIZATION_MS;
83450
84792
  const eventWatchdogMs = options.eventWatchdogMs ?? DEFAULT_EVENT_WATCHDOG_MS;
83451
84793
  const secondaryMeaningfulWorkTimeoutMs = options.secondaryMeaningfulWorkTimeoutMs ?? DEFAULT_SECONDARY_MEANINGFUL_WORK_TIMEOUT_MS;
84794
+ const requireMeaningfulWork = options.requireMeaningfulWork ?? false;
83452
84795
  let consecutiveCompleteChecks = 0;
83453
84796
  let errorCycleCount = 0;
83454
84797
  let firstWorkTimestamp = null;
83455
84798
  let secondaryTimeoutChecked = false;
83456
84799
  const pollStartTimestamp = Date.now();
83457
84800
  while (!abortController.signal.aborted) {
83458
- await new Promise((resolve10) => setTimeout(resolve10, pollIntervalMs));
84801
+ await new Promise((resolve12) => setTimeout(resolve12, pollIntervalMs));
83459
84802
  if (abortController.signal.aborted) {
83460
84803
  return 130;
83461
84804
  }
@@ -83508,21 +84851,24 @@ Session ended with error: ${eventState.lastError}`));
83508
84851
  consecutiveCompleteChecks = 0;
83509
84852
  continue;
83510
84853
  }
84854
+ if (requireMeaningfulWork) {
84855
+ if (Date.now() - pollStartTimestamp <= secondaryMeaningfulWorkTimeoutMs) {
84856
+ consecutiveCompleteChecks = 0;
84857
+ continue;
84858
+ }
84859
+ const hasActiveWork = await hasActiveSessionWork(ctx);
84860
+ if (hasActiveWork) {
84861
+ consecutiveCompleteChecks = 0;
84862
+ continue;
84863
+ }
84864
+ console.error(import_picocolors13.default.red(`
84865
+
84866
+ Session never produced assistant output, tool activity, or reasoning after the prompt started.`));
84867
+ return 1;
84868
+ }
83511
84869
  if (Date.now() - pollStartTimestamp > secondaryMeaningfulWorkTimeoutMs && !secondaryTimeoutChecked) {
83512
84870
  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;
84871
+ const hasActiveWork = await hasActiveSessionWork(ctx);
83526
84872
  if (hasActiveWork) {
83527
84873
  eventState.hasReceivedMeaningfulWork = true;
83528
84874
  console.log(import_picocolors13.default.yellow(`
@@ -83556,6 +84902,21 @@ All tasks completed.`));
83556
84902
  }
83557
84903
  return 130;
83558
84904
  }
84905
+ async function hasActiveSessionWork(ctx) {
84906
+ const childrenRes = await ctx.client.session.children({
84907
+ path: { id: ctx.sessionID },
84908
+ query: { directory: ctx.directory }
84909
+ });
84910
+ const children = normalizeSDKResponse(childrenRes, []);
84911
+ const todosRes = await ctx.client.session.todo({
84912
+ path: { id: ctx.sessionID },
84913
+ query: { directory: ctx.directory }
84914
+ });
84915
+ const todos = normalizeSDKResponse(todosRes, []);
84916
+ const hasActiveChildren = Array.isArray(children) && children.length > 0;
84917
+ const hasActiveTodos = Array.isArray(todos) && todos.some(isIncompleteTodo);
84918
+ return hasActiveChildren || hasActiveTodos;
84919
+ }
83559
84920
  async function getMainSessionStatus(ctx) {
83560
84921
  try {
83561
84922
  const statusesRes = await ctx.client.session.status({
@@ -83575,6 +84936,88 @@ async function getMainSessionStatus(ctx) {
83575
84936
  }
83576
84937
  }
83577
84938
 
84939
+ // src/cli/run/prompt-start.ts
84940
+ init_shared();
84941
+ var DEFAULT_PROMPT_START_TIMEOUT_MS = 30000;
84942
+ var DEFAULT_PROMPT_START_POLL_INTERVAL_MS = 100;
84943
+ function createAbortError() {
84944
+ const error51 = new Error("Prompt start wait aborted");
84945
+ error51.name = "AbortError";
84946
+ return error51;
84947
+ }
84948
+ function sleep(delayMs) {
84949
+ return new Promise((resolve12) => setTimeout(resolve12, delayMs));
84950
+ }
84951
+ function hasPromptStartEvidence(eventState) {
84952
+ return eventState.mainSessionStarted || eventState.hasReceivedMeaningfulWork || eventState.messageCount > 0 || eventState.currentTool !== null;
84953
+ }
84954
+ async function readMainSessionStatus(ctx) {
84955
+ if (typeof ctx.client.session.status !== "function") {
84956
+ return null;
84957
+ }
84958
+ try {
84959
+ const statusRes = await ctx.client.session.status({
84960
+ query: { directory: ctx.directory }
84961
+ });
84962
+ const statuses = normalizeSDKResponse(statusRes, {});
84963
+ return statuses[ctx.sessionID]?.type ?? "idle";
84964
+ } catch (error51) {
84965
+ if (ctx.verbose) {
84966
+ console.error(`[run] failed to read session status while waiting for prompt start: ${String(error51)}`);
84967
+ }
84968
+ return null;
84969
+ }
84970
+ }
84971
+ async function hasPersistedMessages(ctx) {
84972
+ if (typeof ctx.client.session.messages !== "function") {
84973
+ return false;
84974
+ }
84975
+ try {
84976
+ const messagesRes = await ctx.client.session.messages({
84977
+ path: { id: ctx.sessionID },
84978
+ query: { directory: ctx.directory }
84979
+ });
84980
+ const messages = normalizeSDKResponse(messagesRes, []);
84981
+ return messages.length > 0;
84982
+ } catch (error51) {
84983
+ if (ctx.verbose) {
84984
+ console.error(`[run] failed to read session messages while waiting for prompt start: ${String(error51)}`);
84985
+ }
84986
+ return false;
84987
+ }
84988
+ }
84989
+ async function waitForPromptStart(ctx, eventState, abortController, options = {}) {
84990
+ const timeoutMs = options.timeoutMs ?? DEFAULT_PROMPT_START_TIMEOUT_MS;
84991
+ const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_PROMPT_START_POLL_INTERVAL_MS;
84992
+ const startedAt = Date.now();
84993
+ while (!abortController.signal.aborted) {
84994
+ if (hasPromptStartEvidence(eventState)) {
84995
+ return;
84996
+ }
84997
+ if (eventState.mainSessionError) {
84998
+ throw new Error(`Session errored before prompt started: ${eventState.lastError ?? "unknown error"}`);
84999
+ }
85000
+ const status = await readMainSessionStatus(ctx);
85001
+ if (status === "busy" || status === "retry") {
85002
+ eventState.mainSessionStarted = true;
85003
+ eventState.mainSessionIdle = false;
85004
+ return;
85005
+ }
85006
+ if (status === "idle") {
85007
+ eventState.mainSessionIdle = true;
85008
+ }
85009
+ if (await hasPersistedMessages(ctx)) {
85010
+ eventState.mainSessionStarted = true;
85011
+ return;
85012
+ }
85013
+ if (Date.now() - startedAt >= timeoutMs) {
85014
+ throw new Error(`Prompt did not start within ${timeoutMs}ms; no busy status, message event, or persisted message was observed.`);
85015
+ }
85016
+ await sleep(pollIntervalMs);
85017
+ }
85018
+ throw createAbortError();
85019
+ }
85020
+
83578
85021
  // src/cli/run/agent-profile-colors.ts
83579
85022
  init_shared();
83580
85023
  async function loadAgentProfileColors(client3) {
@@ -83684,7 +85127,7 @@ function createTimestampedStdoutController(stdout = process.stdout) {
83684
85127
  // src/shared/posthog.ts
83685
85128
  init_index_node();
83686
85129
  import os5 from "os";
83687
- import { createHash as createHash3 } from "crypto";
85130
+ import { createHash as createHash4 } from "crypto";
83688
85131
  init_plugin_identity();
83689
85132
 
83690
85133
  // src/shared/posthog-activity-state.ts
@@ -83692,11 +85135,11 @@ init_data_path();
83692
85135
  init_logger();
83693
85136
  init_plugin_identity();
83694
85137
  init_write_file_atomically();
83695
- import { existsSync as existsSync31, mkdirSync as mkdirSync11, readFileSync as readFileSync21 } from "fs";
83696
- import { join as join35 } from "path";
85138
+ import { existsSync as existsSync34, mkdirSync as mkdirSync11, readFileSync as readFileSync21 } from "fs";
85139
+ import { join as join40 } from "path";
83697
85140
  var POSTHOG_ACTIVITY_STATE_FILE2 = "posthog-activity.json";
83698
85141
  function getPostHogActivityStateFilePath2() {
83699
- return join35(getDataDir(), CACHE_DIR_NAME, POSTHOG_ACTIVITY_STATE_FILE2);
85142
+ return join40(getDataDir(), CACHE_DIR_NAME, POSTHOG_ACTIVITY_STATE_FILE2);
83700
85143
  }
83701
85144
  function getUtcDayString2(date5) {
83702
85145
  return date5.toISOString().slice(0, 10);
@@ -83706,7 +85149,7 @@ function isPostHogActivityState2(value) {
83706
85149
  }
83707
85150
  function readPostHogActivityState2() {
83708
85151
  const stateFilePath = getPostHogActivityStateFilePath2();
83709
- if (!existsSync31(stateFilePath)) {
85152
+ if (!existsSync34(stateFilePath)) {
83710
85153
  return {};
83711
85154
  }
83712
85155
  try {
@@ -83727,7 +85170,7 @@ function readPostHogActivityState2() {
83727
85170
  function writePostHogActivityState2(nextState) {
83728
85171
  const stateFilePath = getPostHogActivityStateFilePath2();
83729
85172
  try {
83730
- mkdirSync11(join35(getDataDir(), CACHE_DIR_NAME), { recursive: true });
85173
+ mkdirSync11(join40(getDataDir(), CACHE_DIR_NAME), { recursive: true });
83731
85174
  writeFileAtomically(stateFilePath, `${JSON.stringify(nextState, null, 2)}
83732
85175
  `);
83733
85176
  } catch (error51) {
@@ -83858,7 +85301,7 @@ function createPostHogClient2(source, options) {
83858
85301
  };
83859
85302
  }
83860
85303
  function getPostHogDistinctId2() {
83861
- return createHash3("sha256").update(`${PUBLISHED_PACKAGE_NAME}:${resolveOsProvider2().hostname()}`).digest("hex");
85304
+ return createHash4("sha256").update(`${PUBLISHED_PACKAGE_NAME}:${resolveOsProvider2().hostname()}`).digest("hex");
83862
85305
  }
83863
85306
  function createCliPostHog2() {
83864
85307
  return createPostHogClient2("cli", {
@@ -83877,7 +85320,7 @@ var EVENT_PROCESSOR_SHUTDOWN_TIMEOUT_MS = 2000;
83877
85320
  async function waitForEventProcessorShutdown(eventProcessor, timeoutMs = EVENT_PROCESSOR_SHUTDOWN_TIMEOUT_MS) {
83878
85321
  const completed = await Promise.race([
83879
85322
  eventProcessor.then(() => true),
83880
- new Promise((resolve10) => setTimeout(() => resolve10(false), timeoutMs))
85323
+ new Promise((resolve12) => setTimeout(() => resolve12(false), timeoutMs))
83881
85324
  ]);
83882
85325
  }
83883
85326
  async function run(options) {
@@ -83908,7 +85351,7 @@ async function run(options) {
83908
85351
  attach: options.attach,
83909
85352
  signal: abortController.signal
83910
85353
  });
83911
- const cleanup = () => {
85354
+ const cleanup2 = () => {
83912
85355
  serverCleanup();
83913
85356
  };
83914
85357
  const restoreInput = suppressRunInput();
@@ -83916,7 +85359,7 @@ async function run(options) {
83916
85359
  console.log(import_picocolors14.default.yellow(`
83917
85360
  Interrupted. Shutting down...`));
83918
85361
  restoreInput();
83919
- cleanup();
85362
+ cleanup2();
83920
85363
  process.exit(130);
83921
85364
  };
83922
85365
  process.on("SIGINT", handleSigint);
@@ -83974,10 +85417,13 @@ Interrupted. Shutting down...`));
83974
85417
  if (!promptMayHaveBeenAccepted && !isInternalPromptDispatchAccepted(promptResult)) {
83975
85418
  throw new Error(`Session ${sessionID} is not idle; promptAsync skipped by gate: ${promptResult.status}`);
83976
85419
  }
83977
- const exitCode = await pollForCompletion(ctx, eventState, abortController);
85420
+ await waitForPromptStart(ctx, eventState, abortController);
85421
+ const exitCode = await pollForCompletion(ctx, eventState, abortController, {
85422
+ requireMeaningfulWork: true
85423
+ });
83978
85424
  abortController.abort();
83979
85425
  await waitForEventProcessorShutdown(eventProcessor);
83980
- cleanup();
85426
+ cleanup2();
83981
85427
  const durationMs = Date.now() - startTime;
83982
85428
  if (options.onComplete) {
83983
85429
  await executeOnCompleteHook({
@@ -83999,7 +85445,7 @@ Interrupted. Shutting down...`));
83999
85445
  }
84000
85446
  return exitCode;
84001
85447
  } catch (err) {
84002
- cleanup();
85448
+ cleanup2();
84003
85449
  throw err;
84004
85450
  } finally {
84005
85451
  process.removeListener("SIGINT", handleSigint);
@@ -84172,13 +85618,13 @@ async function getLocalVersion(options = {}) {
84172
85618
  }
84173
85619
  }
84174
85620
  // src/cli/doctor/checks/system.ts
84175
- import { existsSync as existsSync42, readFileSync as readFileSync31 } from "fs";
85621
+ import { existsSync as existsSync45, readFileSync as readFileSync31 } from "fs";
84176
85622
 
84177
85623
  // src/cli/doctor/checks/system-binary.ts
84178
85624
  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";
85625
+ import { existsSync as existsSync42, accessSync as accessSync4, constants as constants7 } from "fs";
85626
+ import { homedir as homedir11 } from "os";
85627
+ import { join as join47 } from "path";
84182
85628
 
84183
85629
  // src/cli/doctor/spawn-with-timeout.ts
84184
85630
  init_spawn_with_windows_hide();
@@ -84191,8 +85637,8 @@ async function spawnWithTimeout(command, options, timeoutMs = DEFAULT_SPAWN_TIME
84191
85637
  return { stdout: "", stderr: "", exitCode: 1, timedOut: false };
84192
85638
  }
84193
85639
  let timer;
84194
- const timeoutPromise = new Promise((resolve10) => {
84195
- timer = setTimeout(() => resolve10("timeout"), timeoutMs);
85640
+ const timeoutPromise = new Promise((resolve12) => {
85641
+ timer = setTimeout(() => resolve12("timeout"), timeoutMs);
84196
85642
  });
84197
85643
  const processPromise = (async () => {
84198
85644
  await proc.exited;
@@ -84212,31 +85658,31 @@ async function spawnWithTimeout(command, options, timeoutMs = DEFAULT_SPAWN_TIME
84212
85658
 
84213
85659
  // src/cli/doctor/checks/system-binary.ts
84214
85660
  var WINDOWS_EXECUTABLE_EXTS = [".exe", ".cmd", ".bat", ".ps1"];
84215
- function isExecutable(path14) {
85661
+ function isExecutable2(path14) {
84216
85662
  try {
84217
- accessSync3(path14, constants6.X_OK);
85663
+ accessSync4(path14, constants7.X_OK);
84218
85664
  return true;
84219
85665
  } catch {
84220
85666
  return false;
84221
85667
  }
84222
85668
  }
84223
85669
  function getDesktopAppPaths(platform) {
84224
- const home = homedir9();
85670
+ const home = homedir11();
84225
85671
  switch (platform) {
84226
85672
  case "darwin":
84227
85673
  return [
84228
85674
  "/Applications/OpenCode.app/Contents/MacOS/OpenCode",
84229
- join42(home, "Applications", "OpenCode.app", "Contents", "MacOS", "OpenCode")
85675
+ join47(home, "Applications", "OpenCode.app", "Contents", "MacOS", "OpenCode")
84230
85676
  ];
84231
85677
  case "win32": {
84232
85678
  const programFiles = process.env.ProgramFiles;
84233
85679
  const localAppData = process.env.LOCALAPPDATA;
84234
85680
  const paths = [];
84235
85681
  if (programFiles) {
84236
- paths.push(join42(programFiles, "OpenCode", "OpenCode.exe"));
85682
+ paths.push(join47(programFiles, "OpenCode", "OpenCode.exe"));
84237
85683
  }
84238
85684
  if (localAppData) {
84239
- paths.push(join42(localAppData, "OpenCode", "OpenCode.exe"));
85685
+ paths.push(join47(localAppData, "OpenCode", "OpenCode.exe"));
84240
85686
  }
84241
85687
  return paths;
84242
85688
  }
@@ -84244,8 +85690,8 @@ function getDesktopAppPaths(platform) {
84244
85690
  return [
84245
85691
  "/usr/bin/opencode",
84246
85692
  "/usr/lib/opencode/opencode",
84247
- join42(home, "Applications", "opencode-desktop-linux-x86_64.AppImage"),
84248
- join42(home, "Applications", "opencode-desktop-linux-aarch64.AppImage")
85693
+ join47(home, "Applications", "opencode-desktop-linux-x86_64.AppImage"),
85694
+ join47(home, "Applications", "opencode-desktop-linux-aarch64.AppImage")
84249
85695
  ];
84250
85696
  default:
84251
85697
  return [];
@@ -84257,7 +85703,7 @@ function buildVersionCommand(binaryPath, platform) {
84257
85703
  }
84258
85704
  return [binaryPath, "--version"];
84259
85705
  }
84260
- function findDesktopBinary(platform = process.platform, checkExists = existsSync39) {
85706
+ function findDesktopBinary(platform = process.platform, checkExists = existsSync42) {
84261
85707
  for (const desktopPath of getDesktopAppPaths(platform)) {
84262
85708
  if (checkExists(desktopPath)) {
84263
85709
  return { binary: "opencode", path: desktopPath };
@@ -84265,7 +85711,7 @@ function findDesktopBinary(platform = process.platform, checkExists = existsSync
84265
85711
  }
84266
85712
  return null;
84267
85713
  }
84268
- async function findOpenCodeBinary(platform = process.platform, checkExists = existsSync39) {
85714
+ async function findOpenCodeBinary(platform = process.platform, checkExists = existsSync42) {
84269
85715
  for (const binary2 of OPENCODE_BINARIES2) {
84270
85716
  const path14 = Bun.which(binary2);
84271
85717
  if (path14 && checkExists(path14)) {
@@ -84273,12 +85719,12 @@ async function findOpenCodeBinary(platform = process.platform, checkExists = exi
84273
85719
  }
84274
85720
  }
84275
85721
  const pathEnv = process.env.PATH ?? "";
84276
- const delimiter2 = platform === "win32" ? ";" : ":";
85722
+ const delimiter3 = platform === "win32" ? ";" : ":";
84277
85723
  const candidates = getCommandCandidates2(platform);
84278
- for (const entry of pathEnv.split(delimiter2).filter(Boolean)) {
85724
+ for (const entry of pathEnv.split(delimiter3).filter(Boolean)) {
84279
85725
  for (const command of candidates) {
84280
- const fullPath = join42(entry, command);
84281
- if (checkExists(fullPath) && isExecutable(fullPath)) {
85726
+ const fullPath = join47(entry, command);
85727
+ if (checkExists(fullPath) && isExecutable2(fullPath)) {
84282
85728
  return { binary: command, path: fullPath };
84283
85729
  }
84284
85730
  }
@@ -84320,12 +85766,12 @@ function compareVersions3(current, minimum) {
84320
85766
 
84321
85767
  // src/cli/doctor/checks/system-plugin.ts
84322
85768
  init_shared();
84323
- import { existsSync as existsSync40, readFileSync as readFileSync29 } from "fs";
85769
+ import { existsSync as existsSync43, readFileSync as readFileSync29 } from "fs";
84324
85770
  function detectConfigPath() {
84325
85771
  const paths = getOpenCodeConfigPaths({ binary: "opencode", version: null });
84326
- if (existsSync40(paths.configJsonc))
85772
+ if (existsSync43(paths.configJsonc))
84327
85773
  return paths.configJsonc;
84328
- if (existsSync40(paths.configJson))
85774
+ if (existsSync43(paths.configJson))
84329
85775
  return paths.configJson;
84330
85776
  return null;
84331
85777
  }
@@ -84409,35 +85855,35 @@ function getPluginInfo() {
84409
85855
  init_file_utils2();
84410
85856
  init_checker();
84411
85857
  init_auto_update_checker();
84412
- import { existsSync as existsSync41, readFileSync as readFileSync30 } from "fs";
85858
+ import { existsSync as existsSync44, readFileSync as readFileSync30 } from "fs";
84413
85859
  import { createRequire } from "module";
84414
- import { homedir as homedir10 } from "os";
84415
- import { join as join43 } from "path";
85860
+ import { homedir as homedir12 } from "os";
85861
+ import { join as join48 } from "path";
84416
85862
  init_shared();
84417
85863
  function getPlatformDefaultCacheDir(platform = process.platform) {
84418
85864
  if (platform === "darwin")
84419
- return join43(homedir10(), "Library", "Caches");
85865
+ return join48(homedir12(), "Library", "Caches");
84420
85866
  if (platform === "win32")
84421
- return process.env.LOCALAPPDATA ?? join43(homedir10(), "AppData", "Local");
84422
- return join43(homedir10(), ".cache");
85867
+ return process.env.LOCALAPPDATA ?? join48(homedir12(), "AppData", "Local");
85868
+ return join48(homedir12(), ".cache");
84423
85869
  }
84424
85870
  function resolveOpenCodeCacheDir() {
84425
85871
  const xdgCacheHome = process.env.XDG_CACHE_HOME;
84426
85872
  if (xdgCacheHome)
84427
- return join43(xdgCacheHome, "opencode");
85873
+ return join48(xdgCacheHome, "opencode");
84428
85874
  const fromShared = getOpenCodeCacheDir();
84429
- const platformDefault = join43(getPlatformDefaultCacheDir(), "opencode");
84430
- if (existsSync41(fromShared) || !existsSync41(platformDefault))
85875
+ const platformDefault = join48(getPlatformDefaultCacheDir(), "opencode");
85876
+ if (existsSync44(fromShared) || !existsSync44(platformDefault))
84431
85877
  return fromShared;
84432
85878
  return platformDefault;
84433
85879
  }
84434
85880
  function resolveExistingDir(dirPath) {
84435
- if (!existsSync41(dirPath))
85881
+ if (!existsSync44(dirPath))
84436
85882
  return dirPath;
84437
85883
  return resolveSymlink(dirPath);
84438
85884
  }
84439
85885
  function readPackageJson(filePath) {
84440
- if (!existsSync41(filePath))
85886
+ if (!existsSync44(filePath))
84441
85887
  return null;
84442
85888
  try {
84443
85889
  const content = readFileSync30(filePath, "utf-8");
@@ -84455,11 +85901,11 @@ function normalizeVersion(value) {
84455
85901
  function createPackageCandidates(rootDir) {
84456
85902
  return ACCEPTED_PACKAGE_NAMES.map((packageName) => ({
84457
85903
  packageName,
84458
- installedPackagePath: join43(rootDir, "node_modules", packageName, "package.json")
85904
+ installedPackagePath: join48(rootDir, "node_modules", packageName, "package.json")
84459
85905
  }));
84460
85906
  }
84461
85907
  function selectInstalledPackage(candidate) {
84462
- return candidate.packageCandidates.find((packageCandidate) => existsSync41(packageCandidate.installedPackagePath)) ?? candidate.packageCandidates[0];
85908
+ return candidate.packageCandidates.find((packageCandidate) => existsSync44(packageCandidate.installedPackagePath)) ?? candidate.packageCandidates[0];
84463
85909
  }
84464
85910
  function getExpectedVersion(cachePackage, packageName) {
84465
85911
  return normalizeVersion(cachePackage?.dependencies?.[packageName]) ?? normalizeVersion(cachePackage?.dependencies?.[PACKAGE_NAME]);
@@ -84470,7 +85916,7 @@ function resolveInstalledPackageJsonPath() {
84470
85916
  for (const packageName of ACCEPTED_PACKAGE_NAMES) {
84471
85917
  try {
84472
85918
  const packageJsonPath = require2.resolve(`${packageName}/package.json`);
84473
- if (existsSync41(packageJsonPath)) {
85919
+ if (existsSync44(packageJsonPath)) {
84474
85920
  return { packageName, packageJsonPath };
84475
85921
  }
84476
85922
  } catch {
@@ -84489,20 +85935,20 @@ function getLoadedPluginVersion() {
84489
85935
  const candidates = [
84490
85936
  {
84491
85937
  cacheDir: configDir,
84492
- cachePackagePath: join43(configDir, "package.json"),
85938
+ cachePackagePath: join48(configDir, "package.json"),
84493
85939
  packageCandidates: createPackageCandidates(configDir)
84494
85940
  },
84495
85941
  {
84496
85942
  cacheDir,
84497
- cachePackagePath: join43(cacheDir, "package.json"),
85943
+ cachePackagePath: join48(cacheDir, "package.json"),
84498
85944
  packageCandidates: createPackageCandidates(cacheDir)
84499
85945
  }
84500
85946
  ];
84501
- const selectedCandidate = candidates.find((candidate) => candidate.packageCandidates.some((packageCandidate) => existsSync41(packageCandidate.installedPackagePath))) ?? candidates[0];
85947
+ const selectedCandidate = candidates.find((candidate) => candidate.packageCandidates.some((packageCandidate) => existsSync44(packageCandidate.installedPackagePath))) ?? candidates[0];
84502
85948
  const { cacheDir: selectedDir, cachePackagePath } = selectedCandidate;
84503
85949
  const selectedPackage = selectInstalledPackage(selectedCandidate);
84504
85950
  const candidateInstalledPath = selectedPackage.installedPackagePath;
84505
- const candidateExists = existsSync41(candidateInstalledPath);
85951
+ const candidateExists = existsSync44(candidateInstalledPath);
84506
85952
  const resolvedFallback = candidateExists ? null : resolveInstalledPackageJsonPath();
84507
85953
  const installedPackagePath = resolvedFallback?.packageJsonPath ?? candidateInstalledPath;
84508
85954
  const resolvedPackageName = resolvedFallback?.packageName ?? selectedPackage.packageName;
@@ -84541,7 +85987,7 @@ var defaultDeps5 = {
84541
85987
  function isConfigValid(configPath) {
84542
85988
  if (!configPath)
84543
85989
  return true;
84544
- if (!existsSync42(configPath))
85990
+ if (!existsSync45(configPath))
84545
85991
  return false;
84546
85992
  try {
84547
85993
  parseJsonc(readFileSync31(configPath, "utf-8"));
@@ -84667,29 +86113,29 @@ async function checkSystem(deps = defaultDeps5) {
84667
86113
 
84668
86114
  // src/cli/doctor/checks/config.ts
84669
86115
  import { readFileSync as readFileSync34 } from "fs";
84670
- import { join as join47 } from "path";
86116
+ import { join as join52 } from "path";
84671
86117
  init_shared();
84672
86118
  init_plugin_identity();
84673
86119
 
84674
86120
  // src/cli/doctor/checks/model-resolution-cache.ts
84675
86121
  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";
86122
+ import { existsSync as existsSync46, readFileSync as readFileSync32 } from "fs";
86123
+ import { homedir as homedir13 } from "os";
86124
+ import { join as join49 } from "path";
84679
86125
  function getUserConfigDir2() {
84680
86126
  const xdgConfig = process.env.XDG_CONFIG_HOME;
84681
86127
  if (xdgConfig)
84682
- return join44(xdgConfig, "opencode");
84683
- return join44(homedir11(), ".config", "opencode");
86128
+ return join49(xdgConfig, "opencode");
86129
+ return join49(homedir13(), ".config", "opencode");
84684
86130
  }
84685
86131
  function loadCustomProviderNames() {
84686
86132
  const configDir = getUserConfigDir2();
84687
86133
  const candidatePaths = [
84688
- join44(configDir, "opencode.json"),
84689
- join44(configDir, "opencode.jsonc")
86134
+ join49(configDir, "opencode.json"),
86135
+ join49(configDir, "opencode.jsonc")
84690
86136
  ];
84691
86137
  for (const configPath of candidatePaths) {
84692
- if (!existsSync43(configPath))
86138
+ if (!existsSync46(configPath))
84693
86139
  continue;
84694
86140
  try {
84695
86141
  const content = readFileSync32(configPath, "utf-8");
@@ -84702,9 +86148,9 @@ function loadCustomProviderNames() {
84702
86148
  return [];
84703
86149
  }
84704
86150
  function loadAvailableModelsFromCache() {
84705
- const cacheFile = join44(getOpenCodeCacheDir(), "models.json");
86151
+ const cacheFile = join49(getOpenCodeCacheDir(), "models.json");
84706
86152
  const customProviders = loadCustomProviderNames();
84707
- if (!existsSync43(cacheFile)) {
86153
+ if (!existsSync46(cacheFile)) {
84708
86154
  if (customProviders.length > 0) {
84709
86155
  return { providers: customProviders, modelCount: 0, cacheExists: true };
84710
86156
  }
@@ -84736,8 +86182,8 @@ init_model_capabilities2();
84736
86182
  init_shared();
84737
86183
  init_plugin_identity();
84738
86184
  import { readFileSync as readFileSync33 } from "fs";
84739
- import { join as join45 } from "path";
84740
- var PROJECT_CONFIG_DIR = join45(process.cwd(), ".opencode");
86185
+ import { join as join50 } from "path";
86186
+ var PROJECT_CONFIG_DIR = join50(process.cwd(), ".opencode");
84741
86187
  function loadOmoConfig() {
84742
86188
  const projectDetected = detectPluginConfigFile(PROJECT_CONFIG_DIR, {
84743
86189
  basenames: [CONFIG_BASENAME],
@@ -84769,7 +86215,7 @@ function loadOmoConfig() {
84769
86215
 
84770
86216
  // src/cli/doctor/checks/model-resolution-details.ts
84771
86217
  init_shared();
84772
- import { join as join46 } from "path";
86218
+ import { join as join51 } from "path";
84773
86219
 
84774
86220
  // src/cli/doctor/checks/model-resolution-variant.ts
84775
86221
  function formatModelWithVariant(model, variant) {
@@ -84811,7 +86257,7 @@ function formatCapabilityResolutionLabel(mode) {
84811
86257
  }
84812
86258
  function buildModelResolutionDetails(options) {
84813
86259
  const details = [];
84814
- const cacheFile = join46(getOpenCodeCacheDir(), "models.json");
86260
+ const cacheFile = join51(getOpenCodeCacheDir(), "models.json");
84815
86261
  details.push("\u2550\u2550\u2550 Available Models (from cache) \u2550\u2550\u2550");
84816
86262
  details.push("");
84817
86263
  if (options.available.cacheExists) {
@@ -84966,7 +86412,7 @@ async function checkModels() {
84966
86412
  }
84967
86413
 
84968
86414
  // src/cli/doctor/checks/config.ts
84969
- var PROJECT_CONFIG_DIR2 = join47(process.cwd(), ".opencode");
86415
+ var PROJECT_CONFIG_DIR2 = join52(process.cwd(), ".opencode");
84970
86416
  function findConfigPath() {
84971
86417
  const projectConfig = detectPluginConfigFile(PROJECT_CONFIG_DIR2, {
84972
86418
  basenames: [CONFIG_BASENAME],
@@ -85092,27 +86538,27 @@ async function checkConfig() {
85092
86538
  }
85093
86539
 
85094
86540
  // src/cli/doctor/checks/dependencies.ts
85095
- import { existsSync as existsSync44 } from "fs";
86541
+ import { existsSync as existsSync47 } from "fs";
85096
86542
  import { createRequire as createRequire2 } from "module";
85097
- import { dirname as dirname20, join as join49 } from "path";
86543
+ import { dirname as dirname22, join as join54 } from "path";
85098
86544
 
85099
86545
  // src/hooks/comment-checker/downloader.ts
85100
- import { join as join48 } from "path";
85101
- import { homedir as homedir12, tmpdir as tmpdir3 } from "os";
86546
+ import { join as join53 } from "path";
86547
+ import { homedir as homedir14, tmpdir as tmpdir3 } from "os";
85102
86548
  init_binary_downloader();
85103
86549
  init_logger();
85104
86550
  init_plugin_identity();
85105
86551
  var DEBUG = process.env.COMMENT_CHECKER_DEBUG === "1";
85106
- var DEBUG_FILE = join48(tmpdir3(), "comment-checker-debug.log");
86552
+ var DEBUG_FILE = join53(tmpdir3(), "comment-checker-debug.log");
85107
86553
  function getCacheDir2() {
85108
86554
  if (process.platform === "win32") {
85109
86555
  const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
85110
- const base2 = localAppData || join48(homedir12(), "AppData", "Local");
85111
- return join48(base2, CACHE_DIR_NAME, "bin");
86556
+ const base2 = localAppData || join53(homedir14(), "AppData", "Local");
86557
+ return join53(base2, CACHE_DIR_NAME, "bin");
85112
86558
  }
85113
86559
  const xdgCache = process.env.XDG_CACHE_HOME;
85114
- const base = xdgCache || join48(homedir12(), ".cache");
85115
- return join48(base, CACHE_DIR_NAME, "bin");
86560
+ const base = xdgCache || join53(homedir14(), ".cache");
86561
+ return join53(base, CACHE_DIR_NAME, "bin");
85116
86562
  }
85117
86563
  function getBinaryName() {
85118
86564
  return process.platform === "win32" ? "comment-checker.exe" : "comment-checker";
@@ -85176,15 +86622,15 @@ async function checkAstGrepNapi() {
85176
86622
  path: null
85177
86623
  };
85178
86624
  } catch {
85179
- const { existsSync: existsSync45 } = await import("fs");
85180
- const { join: join50 } = await import("path");
85181
- const { homedir: homedir13 } = await import("os");
86625
+ const { existsSync: existsSync48 } = await import("fs");
86626
+ const { join: join55 } = await import("path");
86627
+ const { homedir: homedir15 } = await import("os");
85182
86628
  const pathsToCheck = [
85183
- join50(homedir13(), ".config", "opencode", "node_modules", "@ast-grep", "napi"),
85184
- join50(process.cwd(), "node_modules", "@ast-grep", "napi")
86629
+ join55(homedir15(), ".config", "opencode", "node_modules", "@ast-grep", "napi"),
86630
+ join55(process.cwd(), "node_modules", "@ast-grep", "napi")
85185
86631
  ];
85186
86632
  for (const napiPath of pathsToCheck) {
85187
- if (existsSync45(napiPath)) {
86633
+ if (existsSync48(napiPath)) {
85188
86634
  return {
85189
86635
  name: "AST-Grep NAPI",
85190
86636
  required: false,
@@ -85209,8 +86655,8 @@ function findCommentCheckerPackageBinary() {
85209
86655
  try {
85210
86656
  const require2 = createRequire2(import.meta.url);
85211
86657
  const pkgPath = require2.resolve("@code-yeongyu/comment-checker/package.json");
85212
- const binaryPath = join49(dirname20(pkgPath), "bin", binaryName);
85213
- if (existsSync44(binaryPath))
86658
+ const binaryPath = join54(dirname22(pkgPath), "bin", binaryName);
86659
+ if (existsSync47(binaryPath))
85214
86660
  return binaryPath;
85215
86661
  } catch {}
85216
86662
  return null;
@@ -85343,11 +86789,11 @@ async function getGhCliInfo() {
85343
86789
 
85344
86790
  // src/cli/doctor/checks/tools-lsp.ts
85345
86791
  import { readFileSync as readFileSync35 } from "fs";
85346
- import { join as join51 } from "path";
86792
+ import { join as join55 } from "path";
85347
86793
 
85348
86794
  // src/mcp/lsp.ts
85349
- import { existsSync as existsSync45 } from "fs";
85350
- import { dirname as dirname21, resolve as resolve10 } from "path";
86795
+ import { existsSync as existsSync48 } from "fs";
86796
+ import { dirname as dirname23, resolve as resolve12 } from "path";
85351
86797
  import { fileURLToPath as fileURLToPath3 } from "url";
85352
86798
 
85353
86799
  // src/mcp/cli-suffix.ts
@@ -85360,66 +86806,6 @@ function hasCliSuffix(candidatePath, suffix) {
85360
86806
 
85361
86807
  // src/mcp/runtime-executable.ts
85362
86808
  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
86809
  var NODE_EXECUTABLE_NAMES = new Set(["node", "node.exe"]);
85424
86810
  function isUnsafeCommandName2(commandName) {
85425
86811
  if (commandName.length === 0)
@@ -85481,9 +86867,9 @@ var LSP_BOOTSTRAP_SCRIPT = [
85481
86867
  "finish(run(process.execPath, [dist, 'mcp'], 'inherit'))"
85482
86868
  ].join(";");
85483
86869
  function addAncestorCommandCandidates(startDirectory, target, seenPaths, pathExists, resolveExecutable) {
85484
- let currentDirectory = resolve10(startDirectory);
86870
+ let currentDirectory = resolve12(startDirectory);
85485
86871
  while (true) {
85486
- const distCliPath = resolve10(currentDirectory, SUBMODULE_REL, DIST_CLI_REL);
86872
+ const distCliPath = resolve12(currentDirectory, SUBMODULE_REL, DIST_CLI_REL);
85487
86873
  if (!seenPaths.has(distCliPath)) {
85488
86874
  const runtime4 = resolveJavaScriptRuntime(resolveExecutable);
85489
86875
  seenPaths.add(distCliPath);
@@ -85494,7 +86880,7 @@ function addAncestorCommandCandidates(startDirectory, target, seenPaths, pathExi
85494
86880
  exists: runtime4.available && pathExists(distCliPath)
85495
86881
  });
85496
86882
  }
85497
- const sourceCliPath = resolve10(currentDirectory, SUBMODULE_REL, SOURCE_CLI_REL);
86883
+ const sourceCliPath = resolve12(currentDirectory, SUBMODULE_REL, SOURCE_CLI_REL);
85498
86884
  if (!seenPaths.has(sourceCliPath)) {
85499
86885
  const runtime4 = resolveExecutable("bun");
85500
86886
  seenPaths.add(sourceCliPath);
@@ -85505,7 +86891,7 @@ function addAncestorCommandCandidates(startDirectory, target, seenPaths, pathExi
85505
86891
  exists: runtime4.available && pathExists(sourceCliPath)
85506
86892
  });
85507
86893
  }
85508
- const parentDirectory = resolve10(currentDirectory, "..");
86894
+ const parentDirectory = resolve12(currentDirectory, "..");
85509
86895
  if (parentDirectory === currentDirectory) {
85510
86896
  return;
85511
86897
  }
@@ -85514,13 +86900,13 @@ function addAncestorCommandCandidates(startDirectory, target, seenPaths, pathExi
85514
86900
  }
85515
86901
  function getModuleDirectory(moduleUrl) {
85516
86902
  try {
85517
- return dirname21(fileURLToPath3(moduleUrl));
86903
+ return dirname23(fileURLToPath3(moduleUrl));
85518
86904
  } catch {
85519
86905
  return null;
85520
86906
  }
85521
86907
  }
85522
86908
  function findBootstrapRoot(candidates, pathExists) {
85523
- return candidates.find((candidate) => pathExists(resolve10(candidate.root, "package.json")))?.root ?? process.cwd();
86909
+ return candidates.find((candidate) => pathExists(resolve12(candidate.root, "package.json")))?.root ?? process.cwd();
85524
86910
  }
85525
86911
  function resolveJavaScriptRuntime(resolveExecutable) {
85526
86912
  const node = resolveExecutable("node");
@@ -85534,12 +86920,12 @@ function createBootstrapCandidate(root, resolveExecutable) {
85534
86920
  return {
85535
86921
  command: [runtime4.command, "-e", LSP_BOOTSTRAP_SCRIPT, root, git.command, npm.command, bun.command],
85536
86922
  root,
85537
- path: resolve10(root, SUBMODULE_REL, DIST_CLI_REL),
86923
+ path: resolve12(root, SUBMODULE_REL, DIST_CLI_REL),
85538
86924
  exists: runtime4.available && git.available && npm.available
85539
86925
  };
85540
86926
  }
85541
86927
  function resolveLspCommand(options = {}) {
85542
- const pathExists = options.exists ?? existsSync45;
86928
+ const pathExists = options.exists ?? existsSync48;
85543
86929
  const resolveExecutable = options.resolveExecutable ?? resolveRuntimeExecutable;
85544
86930
  const candidates = [];
85545
86931
  const seenPaths = new Set;
@@ -85589,7 +86975,7 @@ function readOmoConfig(configDirectory) {
85589
86975
  }
85590
86976
  function isLspMcpDisabled(options) {
85591
86977
  const userConfigDirectory = options.configDirectory ?? getOpenCodeConfigDir({ binary: "opencode" });
85592
- const projectConfigDirectory = join51(options.cwd ?? process.cwd(), ".opencode");
86978
+ const projectConfigDirectory = join55(options.cwd ?? process.cwd(), ".opencode");
85593
86979
  const userConfig = readOmoConfig(userConfigDirectory);
85594
86980
  const projectConfig = readOmoConfig(projectConfigDirectory);
85595
86981
  const disabledMcps = new Set([
@@ -85608,21 +86994,21 @@ function getInstalledLspServers(options = {}) {
85608
86994
 
85609
86995
  // src/cli/doctor/checks/tools-mcp.ts
85610
86996
  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";
86997
+ import { existsSync as existsSync49, readFileSync as readFileSync36 } from "fs";
86998
+ import { homedir as homedir15 } from "os";
86999
+ import { join as join56 } from "path";
85614
87000
  var BUILTIN_MCP_SERVERS = ["websearch", "context7", "grep_app", "lsp", "ast_grep"];
85615
87001
  function getMcpConfigPaths() {
85616
87002
  return [
85617
- join52(homedir13(), ".claude", ".mcp.json"),
85618
- join52(process.cwd(), ".mcp.json"),
85619
- join52(process.cwd(), ".claude", ".mcp.json")
87003
+ join56(homedir15(), ".claude", ".mcp.json"),
87004
+ join56(process.cwd(), ".mcp.json"),
87005
+ join56(process.cwd(), ".claude", ".mcp.json")
85620
87006
  ];
85621
87007
  }
85622
87008
  function loadUserMcpConfig() {
85623
87009
  const servers = {};
85624
87010
  for (const configPath of getMcpConfigPaths()) {
85625
- if (!existsSync46(configPath))
87011
+ if (!existsSync49(configPath))
85626
87012
  continue;
85627
87013
  try {
85628
87014
  const content = readFileSync36(configPath, "utf-8");
@@ -85779,10 +87165,10 @@ async function probeBinary(cmd, args) {
85779
87165
 
85780
87166
  // src/features/team-mode/team-registry/paths.ts
85781
87167
  init_logger();
85782
- import { homedir as homedir14 } from "os";
87168
+ import { homedir as homedir16 } from "os";
85783
87169
  import path14 from "path";
85784
87170
  function resolveBaseDir(config2) {
85785
- return config2.base_dir ?? path14.join(homedir14(), ".omo");
87171
+ return config2.base_dir ?? path14.join(homedir16(), ".omo");
85786
87172
  }
85787
87173
 
85788
87174
  // src/cli/doctor/checks/team-mode.ts
@@ -85849,8 +87235,8 @@ async function pathExists(dir) {
85849
87235
 
85850
87236
  // src/cli/doctor/checks/tui-plugin-config.ts
85851
87237
  init_shared();
85852
- import { existsSync as existsSync47, readFileSync as readFileSync38 } from "fs";
85853
- import { join as join53 } from "path";
87238
+ import { existsSync as existsSync50, readFileSync as readFileSync38 } from "fs";
87239
+ import { join as join57 } from "path";
85854
87240
  var TUI_SUBPATH = "tui";
85855
87241
  function isOurFilePluginEntry(entry) {
85856
87242
  if (!entry.startsWith("file:"))
@@ -85859,8 +87245,8 @@ function isOurFilePluginEntry(entry) {
85859
87245
  if (path16.startsWith("//"))
85860
87246
  path16 = path16.slice(2);
85861
87247
  try {
85862
- const pkgJsonPath = join53(path16, "package.json");
85863
- if (!existsSync47(pkgJsonPath))
87248
+ const pkgJsonPath = join57(path16, "package.json");
87249
+ if (!existsSync50(pkgJsonPath))
85864
87250
  return false;
85865
87251
  const parsed = JSON.parse(readFileSync38(pkgJsonPath, "utf-8"));
85866
87252
  return typeof parsed.name === "string" && ACCEPTED_PACKAGE_NAMES.includes(parsed.name);
@@ -85890,7 +87276,7 @@ function isTuiPluginEntry(entry) {
85890
87276
  }
85891
87277
  function detectServerPluginRegistration() {
85892
87278
  const paths = getOpenCodeConfigPaths({ binary: "opencode", version: null });
85893
- const configPath = existsSync47(paths.configJsonc) ? paths.configJsonc : existsSync47(paths.configJson) ? paths.configJson : null;
87279
+ const configPath = existsSync50(paths.configJsonc) ? paths.configJsonc : existsSync50(paths.configJson) ? paths.configJson : null;
85894
87280
  if (!configPath) {
85895
87281
  return { registered: false, configPath: null };
85896
87282
  }
@@ -85903,8 +87289,8 @@ function detectServerPluginRegistration() {
85903
87289
  }
85904
87290
  }
85905
87291
  function detectTuiPluginRegistration() {
85906
- const tuiJsonPath = join53(getOpenCodeConfigDir({ binary: "opencode" }), "tui.json");
85907
- if (!existsSync47(tuiJsonPath)) {
87292
+ const tuiJsonPath = join57(getOpenCodeConfigDir({ binary: "opencode" }), "tui.json");
87293
+ if (!existsSync50(tuiJsonPath)) {
85908
87294
  return { registered: false, configPath: tuiJsonPath, exists: false };
85909
87295
  }
85910
87296
  try {
@@ -86361,11 +87747,11 @@ async function refreshModelCapabilities(options, deps = {}) {
86361
87747
 
86362
87748
  // src/features/mcp-oauth/storage.ts
86363
87749
  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";
87750
+ import { chmodSync as chmodSync2, existsSync as existsSync51, mkdirSync as mkdirSync13, readFileSync as readFileSync39, renameSync as renameSync6, unlinkSync as unlinkSync8, writeFileSync as writeFileSync10 } from "fs";
87751
+ import { dirname as dirname24, join as join58 } from "path";
86366
87752
  var STORAGE_FILE_NAME = "mcp-oauth.json";
86367
87753
  function getMcpOauthStoragePath() {
86368
- return join54(getOpenCodeConfigDir({ binary: "opencode" }), STORAGE_FILE_NAME);
87754
+ return join58(getOpenCodeConfigDir({ binary: "opencode" }), STORAGE_FILE_NAME);
86369
87755
  }
86370
87756
  function normalizeHost2(serverHost) {
86371
87757
  let host = serverHost.trim();
@@ -86402,7 +87788,7 @@ function buildKey(serverHost, resource) {
86402
87788
  }
86403
87789
  function readStore() {
86404
87790
  const filePath = getMcpOauthStoragePath();
86405
- if (!existsSync48(filePath)) {
87791
+ if (!existsSync51(filePath)) {
86406
87792
  return null;
86407
87793
  }
86408
87794
  try {
@@ -86415,8 +87801,8 @@ function readStore() {
86415
87801
  function writeStore(store2) {
86416
87802
  const filePath = getMcpOauthStoragePath();
86417
87803
  try {
86418
- const dir = dirname22(filePath);
86419
- if (!existsSync48(dir)) {
87804
+ const dir = dirname24(filePath);
87805
+ if (!existsSync51(dir)) {
86420
87806
  mkdirSync13(dir, { recursive: true });
86421
87807
  }
86422
87808
  const tempPath = `${filePath}.tmp.${Date.now()}`;
@@ -86453,7 +87839,7 @@ function deleteToken(serverHost, resource) {
86453
87839
  if (Object.keys(store2).length === 0) {
86454
87840
  try {
86455
87841
  const filePath = getMcpOauthStoragePath();
86456
- if (existsSync48(filePath)) {
87842
+ if (existsSync51(filePath)) {
86457
87843
  unlinkSync8(filePath);
86458
87844
  }
86459
87845
  return true;
@@ -86622,7 +88008,7 @@ async function getOrRegisterClient(options) {
86622
88008
  }
86623
88009
  }
86624
88010
  function parseRegistrationResponse(data) {
86625
- if (!isRecord14(data))
88011
+ if (!isRecord16(data))
86626
88012
  return null;
86627
88013
  const clientId = data.client_id;
86628
88014
  if (typeof clientId !== "string" || clientId.length === 0)
@@ -86633,7 +88019,7 @@ function parseRegistrationResponse(data) {
86633
88019
  }
86634
88020
  return { clientId };
86635
88021
  }
86636
- function isRecord14(value) {
88022
+ function isRecord16(value) {
86637
88023
  return typeof value === "object" && value !== null;
86638
88024
  }
86639
88025
 
@@ -86647,13 +88033,13 @@ async function findAvailablePort2(startPort = DEFAULT_PORT) {
86647
88033
 
86648
88034
  // src/features/mcp-oauth/oauth-authorization-flow.ts
86649
88035
  import { spawn as spawn2 } from "child_process";
86650
- import { createHash as createHash4, randomBytes as randomBytes2 } from "crypto";
88036
+ import { createHash as createHash5, randomBytes as randomBytes2 } from "crypto";
86651
88037
  import { createServer as createServer2 } from "http";
86652
88038
  function generateCodeVerifier() {
86653
88039
  return randomBytes2(32).toString("base64url");
86654
88040
  }
86655
88041
  function generateCodeChallenge(verifier) {
86656
- return createHash4("sha256").update(verifier).digest("base64url");
88042
+ return createHash5("sha256").update(verifier).digest("base64url");
86657
88043
  }
86658
88044
  function buildAuthorizationUrl(authorizationEndpoint, options) {
86659
88045
  const url2 = new URL(authorizationEndpoint);
@@ -86673,7 +88059,7 @@ function buildAuthorizationUrl(authorizationEndpoint, options) {
86673
88059
  }
86674
88060
  var CALLBACK_TIMEOUT_MS = 5 * 60 * 1000;
86675
88061
  function startCallbackServer(port) {
86676
- return new Promise((resolve11, reject) => {
88062
+ return new Promise((resolve13, reject) => {
86677
88063
  let timeoutId;
86678
88064
  const server2 = createServer2((request, response) => {
86679
88065
  clearTimeout(timeoutId);
@@ -86699,7 +88085,7 @@ function startCallbackServer(port) {
86699
88085
  response.writeHead(200, { "content-type": "text/html" });
86700
88086
  response.end("<html><body><h1>Authorization successful. You can close this tab.</h1></body></html>");
86701
88087
  server2.close();
86702
- resolve11({ code, state: state2 });
88088
+ resolve13({ code, state: state2 });
86703
88089
  });
86704
88090
  timeoutId = setTimeout(() => {
86705
88091
  server2.close();
@@ -87033,7 +88419,7 @@ function createMcpOAuthCommand() {
87033
88419
  }
87034
88420
 
87035
88421
  // src/cli/boulder/boulder.ts
87036
- import { existsSync as existsSync49 } from "fs";
88422
+ import { existsSync as existsSync52 } from "fs";
87037
88423
 
87038
88424
  // src/cli/boulder/formatter.ts
87039
88425
  var import_picocolors20 = __toESM(require_picocolors(), 1);
@@ -87170,10 +88556,10 @@ async function boulder(options) {
87170
88556
  const boulderFilePath = getBoulderFilePath(directory);
87171
88557
  const state2 = readBoulderState(directory);
87172
88558
  if (!state2) {
87173
- const message = existsSync49(boulderFilePath) ? formatReadErrorMessage(options.json) : formatNoBoulderMessage(options.json);
88559
+ const message = existsSync52(boulderFilePath) ? formatReadErrorMessage(options.json) : formatNoBoulderMessage(options.json);
87174
88560
  process.stderr.write(`${message}
87175
88561
  `);
87176
- return existsSync49(boulderFilePath) ? 2 : 1;
88562
+ return existsSync52(boulderFilePath) ? 2 : 1;
87177
88563
  }
87178
88564
  const works = getBoulderWorks(state2);
87179
88565
  const filteredWorks = options.workId ? works.filter((work) => work.work_id === options.workId) : works;
@@ -87193,7 +88579,7 @@ async function boulder(options) {
87193
88579
  var VERSION2 = package_default.version;
87194
88580
  var program2 = new Command;
87195
88581
  function resolveInstallArgs(options, invocationName = process.env.OMO_INVOCATION_NAME) {
87196
- const defaultPlatform = invocationName === "lazycodex" ? "codex" : undefined;
88582
+ const defaultPlatform = invocationName === "lazycodex" || invocationName === "lazycodex-ai" ? "codex" : undefined;
87197
88583
  return {
87198
88584
  tui: options.tui !== false,
87199
88585
  claude: options.claude,
@@ -87214,7 +88600,7 @@ program2.name("oh-my-opencode").description("The ultimate OpenCode plugin - mult
87214
88600
  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
88601
  Examples:
87216
88602
  $ bunx oh-my-opencode install
87217
- $ bunx lazycodex install --no-tui
88603
+ $ npx lazycodex-ai install --no-tui
87218
88604
  $ bunx oh-my-opencode install --no-tui --platform=both --claude=max20 --openai=yes --gemini=yes --copilot=no
87219
88605
  $ omo install --platform=codex --codex-autonomous
87220
88606
  $ bunx oh-my-opencode install --no-tui --claude=no --gemini=no --copilot=yes --opencode-zen=yes
@@ -87234,6 +88620,7 @@ Model Providers (Priority: Native > Copilot > OpenCode Zen > Z.ai > Kimi > Verce
87234
88620
  const exitCode = await install(args);
87235
88621
  process.exit(exitCode);
87236
88622
  });
88623
+ configureCleanupCommand(program2);
87237
88624
  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
88625
  Examples:
87239
88626
  $ bunx oh-my-opencode run "Fix the bug in index.ts"