oh-my-opencode 3.7.2 → 3.7.4

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 (43) hide show
  1. package/README.ja.md +8 -8
  2. package/README.ko.md +8 -8
  3. package/README.md +8 -8
  4. package/README.zh-cn.md +8 -8
  5. package/dist/agents/atlas/default.d.ts +1 -1
  6. package/dist/agents/atlas/gpt.d.ts +1 -1
  7. package/dist/agents/metis.d.ts +1 -1
  8. package/dist/agents/prometheus/behavioral-summary.d.ts +1 -1
  9. package/dist/agents/prometheus/identity-constraints.d.ts +1 -1
  10. package/dist/agents/prometheus/interview-mode.d.ts +1 -1
  11. package/dist/agents/prometheus/plan-generation.d.ts +1 -1
  12. package/dist/agents/prometheus/plan-template.d.ts +1 -1
  13. package/dist/agents/prometheus/system-prompt.d.ts +1 -1
  14. package/dist/agents/sisyphus-junior/agent.d.ts +1 -1
  15. package/dist/cli/config-manager/antigravity-provider-configuration.d.ts +2 -2
  16. package/dist/cli/index.js +225 -91
  17. package/dist/cli/model-fallback-types.d.ts +5 -0
  18. package/dist/cli/run/event-state.d.ts +4 -0
  19. package/dist/cli/run/stdin-suppression.d.ts +12 -0
  20. package/dist/config/schema/agent-overrides.d.ts +60 -0
  21. package/dist/config/schema/hooks.d.ts +2 -1
  22. package/dist/config/schema/oh-my-opencode-config.d.ts +58 -1
  23. package/dist/create-hooks.d.ts +2 -1
  24. package/dist/features/background-agent/parent-session-context-resolver.d.ts +1 -0
  25. package/dist/hooks/atlas/recent-model-resolver.d.ts +6 -0
  26. package/dist/hooks/atlas/system-reminder-templates.d.ts +1 -1
  27. package/dist/hooks/index.d.ts +2 -1
  28. package/dist/hooks/{sisyphus-gpt-hephaestus-reminder → no-sisyphus-gpt}/hook.d.ts +6 -1
  29. package/dist/hooks/no-sisyphus-gpt/index.d.ts +1 -0
  30. package/dist/hooks/session-recovery/types.d.ts +1 -0
  31. package/dist/hooks/ultrawork-model-override/hook.d.ts +6 -0
  32. package/dist/hooks/ultrawork-model-override/index.d.ts +1 -0
  33. package/dist/hooks/unstable-agent-babysitter/task-message-analyzer.d.ts +1 -0
  34. package/dist/hooks/unstable-agent-babysitter/unstable-agent-babysitter-hook.d.ts +2 -0
  35. package/dist/index.js +559 -453
  36. package/dist/plugin/hooks/create-core-hooks.d.ts +2 -1
  37. package/dist/plugin/hooks/create-session-hooks.d.ts +4 -2
  38. package/dist/shared/index.d.ts +1 -0
  39. package/dist/shared/prompt-tools.d.ts +3 -0
  40. package/dist/tools/delegate-task/constants.d.ts +1 -1
  41. package/package.json +8 -8
  42. package/dist/cli/run/opencode-bin-path.d.ts +0 -3
  43. package/dist/hooks/sisyphus-gpt-hephaestus-reminder/index.d.ts +0 -1
package/dist/cli/index.js CHANGED
@@ -6513,6 +6513,7 @@ var init_hook_names = __esm(() => {
6513
6513
  HOOK_NAME_MAP = {
6514
6514
  "anthropic-auto-compact": "anthropic-context-window-limit-recovery",
6515
6515
  "sisyphus-orchestrator": "atlas",
6516
+ "sisyphus-gpt-hephaestus-reminder": "no-sisyphus-gpt",
6516
6517
  "empty-message-sanitizer": null
6517
6518
  };
6518
6519
  });
@@ -6550,7 +6551,8 @@ var MODEL_VERSION_MAP;
6550
6551
  var init_model_versions = __esm(() => {
6551
6552
  MODEL_VERSION_MAP = {
6552
6553
  "openai/gpt-5.2-codex": "openai/gpt-5.3-codex",
6553
- "anthropic/claude-opus-4-5": "anthropic/claude-opus-4-6"
6554
+ "anthropic/claude-opus-4-5": "anthropic/claude-opus-4-6",
6555
+ "anthropic/claude-sonnet-4-5": "anthropic/claude-sonnet-4-6"
6554
6556
  };
6555
6557
  });
6556
6558
 
@@ -6786,7 +6788,7 @@ var init_model_requirements = __esm(() => {
6786
6788
  { providers: ["kimi-for-coding"], model: "k2p5" },
6787
6789
  { providers: ["opencode"], model: "kimi-k2.5-free" },
6788
6790
  { providers: ["zai-coding-plan"], model: "glm-4.7" },
6789
- { providers: ["opencode"], model: "glm-4.7-free" }
6791
+ { providers: ["opencode"], model: "big-pickle" }
6790
6792
  ],
6791
6793
  requiresAnyModel: true
6792
6794
  },
@@ -6806,8 +6808,8 @@ var init_model_requirements = __esm(() => {
6806
6808
  librarian: {
6807
6809
  fallbackChain: [
6808
6810
  { providers: ["zai-coding-plan"], model: "glm-4.7" },
6809
- { providers: ["opencode"], model: "glm-4.7-free" },
6810
- { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-5" }
6811
+ { providers: ["opencode"], model: "big-pickle" },
6812
+ { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-6" }
6811
6813
  ]
6812
6814
  },
6813
6815
  explore: {
@@ -6857,7 +6859,7 @@ var init_model_requirements = __esm(() => {
6857
6859
  fallbackChain: [
6858
6860
  { providers: ["kimi-for-coding"], model: "k2p5" },
6859
6861
  { providers: ["opencode"], model: "kimi-k2.5-free" },
6860
- { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-5" },
6862
+ { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-6" },
6861
6863
  { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2" },
6862
6864
  { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" }
6863
6865
  ]
@@ -6904,7 +6906,7 @@ var init_model_requirements = __esm(() => {
6904
6906
  },
6905
6907
  "unspecified-low": {
6906
6908
  fallbackChain: [
6907
- { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-5" },
6909
+ { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-6" },
6908
6910
  { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.3-codex", variant: "medium" },
6909
6911
  { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" }
6910
6912
  ]
@@ -6920,7 +6922,7 @@ var init_model_requirements = __esm(() => {
6920
6922
  fallbackChain: [
6921
6923
  { providers: ["kimi-for-coding"], model: "k2p5" },
6922
6924
  { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" },
6923
- { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-5" }
6925
+ { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-6" }
6924
6926
  ]
6925
6927
  }
6926
6928
  };
@@ -7268,6 +7270,17 @@ var init_safe_create_hook = __esm(() => {
7268
7270
  // src/shared/session-directory-resolver.ts
7269
7271
  var init_session_directory_resolver = () => {};
7270
7272
 
7273
+ // src/shared/session-tools-store.ts
7274
+ var store;
7275
+ var init_session_tools_store = __esm(() => {
7276
+ store = new Map;
7277
+ });
7278
+
7279
+ // src/shared/prompt-tools.ts
7280
+ var init_prompt_tools = __esm(() => {
7281
+ init_session_tools_store();
7282
+ });
7283
+
7271
7284
  // src/shared/index.ts
7272
7285
  var init_shared = __esm(() => {
7273
7286
  init_model_resolution_pipeline();
@@ -7311,6 +7324,7 @@ var init_shared = __esm(() => {
7311
7324
  init_opencode_storage_paths();
7312
7325
  init_opencode_message_dir();
7313
7326
  init_session_directory_resolver();
7327
+ init_prompt_tools();
7314
7328
  });
7315
7329
 
7316
7330
  // src/cli/config-manager/config-context.ts
@@ -7581,7 +7595,7 @@ function isProviderAvailable(provider, availability) {
7581
7595
  // src/cli/provider-model-id-transform.ts
7582
7596
  function transformModelForProvider(provider, model) {
7583
7597
  if (provider === "github-copilot") {
7584
- return model.replace("claude-opus-4-6", "claude-opus-4.6").replace("claude-sonnet-4-5", "claude-sonnet-4.5").replace("claude-haiku-4-5", "claude-haiku-4.5").replace("claude-sonnet-4", "claude-sonnet-4").replace("gemini-3-pro", "gemini-3-pro-preview").replace("gemini-3-flash", "gemini-3-flash-preview");
7598
+ return model.replace("claude-opus-4-6", "claude-opus-4.6").replace("claude-sonnet-4-6", "claude-sonnet-4.6").replace("claude-haiku-4-5", "claude-haiku-4.5").replace("claude-sonnet-4", "claude-sonnet-4").replace("gemini-3-pro", "gemini-3-pro-preview").replace("gemini-3-flash", "gemini-3-flash-preview");
7585
7599
  }
7586
7600
  return model;
7587
7601
  }
@@ -7655,6 +7669,14 @@ function generateModelConfig(config) {
7655
7669
  if (req.requiresAnyModel && !isAnyFallbackEntryAvailable(fallbackChain, avail)) {
7656
7670
  continue;
7657
7671
  }
7672
+ if (avail.native.claude && !avail.isMaxPlan) {
7673
+ agents[role] = {
7674
+ model: "anthropic/claude-sonnet-4-6",
7675
+ variant: "max",
7676
+ ultrawork: { model: "anthropic/claude-opus-4-6", variant: "max" }
7677
+ };
7678
+ continue;
7679
+ }
7658
7680
  const resolved2 = resolveModelFromChain(fallbackChain, avail);
7659
7681
  if (resolved2) {
7660
7682
  const variant = resolved2.variant ?? req.variant;
@@ -7698,7 +7720,7 @@ function generateModelConfig(config) {
7698
7720
  categories
7699
7721
  };
7700
7722
  }
7701
- var ZAI_MODEL = "zai-coding-plan/glm-4.7", ULTIMATE_FALLBACK = "opencode/glm-4.7-free", SCHEMA_URL = "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/master/assets/oh-my-opencode.schema.json";
7723
+ var ZAI_MODEL = "zai-coding-plan/glm-4.7", ULTIMATE_FALLBACK = "opencode/big-pickle", SCHEMA_URL = "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/master/assets/oh-my-opencode.schema.json";
7702
7724
  var init_model_fallback = __esm(() => {
7703
7725
  init_model_requirements();
7704
7726
  init_fallback_chain_resolution();
@@ -7983,13 +8005,13 @@ var init_antigravity_provider_configuration = __esm(() => {
7983
8005
  high: { thinkingLevel: "high" }
7984
8006
  }
7985
8007
  },
7986
- "antigravity-claude-sonnet-4-5": {
7987
- name: "Claude Sonnet 4.5 (Antigravity)",
8008
+ "antigravity-claude-sonnet-4-6": {
8009
+ name: "Claude Sonnet 4.6 (Antigravity)",
7988
8010
  limit: { context: 200000, output: 64000 },
7989
8011
  modalities: { input: ["text", "image", "pdf"], output: ["text"] }
7990
8012
  },
7991
- "antigravity-claude-sonnet-4-5-thinking": {
7992
- name: "Claude Sonnet 4.5 Thinking (Antigravity)",
8013
+ "antigravity-claude-sonnet-4-6-thinking": {
8014
+ name: "Claude Sonnet 4.6 Thinking (Antigravity)",
7993
8015
  limit: { context: 200000, output: 64000 },
7994
8016
  modalities: { input: ["text", "image", "pdf"], output: ["text"] },
7995
8017
  variants: {
@@ -9053,7 +9075,7 @@ var {
9053
9075
  // package.json
9054
9076
  var package_default = {
9055
9077
  name: "oh-my-opencode",
9056
- version: "3.7.2",
9078
+ version: "3.7.4",
9057
9079
  description: "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
9058
9080
  main: "dist/index.js",
9059
9081
  types: "dist/index.d.ts",
@@ -9127,13 +9149,13 @@ var package_default = {
9127
9149
  typescript: "^5.7.3"
9128
9150
  },
9129
9151
  optionalDependencies: {
9130
- "oh-my-opencode-darwin-arm64": "3.7.2",
9131
- "oh-my-opencode-darwin-x64": "3.7.2",
9132
- "oh-my-opencode-linux-arm64": "3.7.2",
9133
- "oh-my-opencode-linux-arm64-musl": "3.7.2",
9134
- "oh-my-opencode-linux-x64": "3.7.2",
9135
- "oh-my-opencode-linux-x64-musl": "3.7.2",
9136
- "oh-my-opencode-windows-x64": "3.7.2"
9152
+ "oh-my-opencode-darwin-arm64": "3.7.4",
9153
+ "oh-my-opencode-darwin-x64": "3.7.4",
9154
+ "oh-my-opencode-linux-arm64": "3.7.4",
9155
+ "oh-my-opencode-linux-arm64-musl": "3.7.4",
9156
+ "oh-my-opencode-linux-x64": "3.7.4",
9157
+ "oh-my-opencode-linux-x64-musl": "3.7.4",
9158
+ "oh-my-opencode-windows-x64": "3.7.4"
9137
9159
  },
9138
9160
  trustedDependencies: [
9139
9161
  "@ast-grep/cli",
@@ -9366,7 +9388,7 @@ async function runCliInstaller(args, version) {
9366
9388
  console.log();
9367
9389
  }
9368
9390
  if (!config.hasClaude && !config.hasOpenAI && !config.hasGemini && !config.hasCopilot && !config.hasOpencodeZen) {
9369
- printWarning("No model providers configured. Using opencode/glm-4.7-free as fallback.");
9391
+ printWarning("No model providers configured. Using opencode/big-pickle as fallback.");
9370
9392
  }
9371
9393
  console.log(`${SYMBOLS.star} ${import_picocolors2.default.bold(import_picocolors2.default.green(isUpdate ? "Configuration updated!" : "Installation complete!"))}`);
9372
9394
  console.log(` Run ${import_picocolors2.default.cyan("opencode")} to start!`);
@@ -9992,9 +10014,9 @@ async function promptInstallConfig(detected) {
9992
10014
  const claude = await selectOrCancel({
9993
10015
  message: "Do you have a Claude Pro/Max subscription?",
9994
10016
  options: [
9995
- { value: "no", label: "No", hint: "Will use opencode/glm-4.7-free as fallback" },
10017
+ { value: "no", label: "No", hint: "Will use opencode/big-pickle as fallback" },
9996
10018
  { value: "yes", label: "Yes (standard)", hint: "Claude Opus 4.5 for orchestration" },
9997
- { value: "max20", label: "Yes (max20 mode)", hint: "Full power with Claude Sonnet 4.5 for Librarian" }
10019
+ { value: "max20", label: "Yes (max20 mode)", hint: "Full power with Claude Sonnet 4.6 for Librarian" }
9998
10020
  ],
9999
10021
  initialValue: initial.claude
10000
10022
  });
@@ -10147,7 +10169,7 @@ async function runTuiInstaller(args, version) {
10147
10169
  console.log();
10148
10170
  }
10149
10171
  if (!config.hasClaude && !config.hasOpenAI && !config.hasGemini && !config.hasCopilot && !config.hasOpencodeZen) {
10150
- M2.warn("No model providers configured. Using opencode/glm-4.7-free as fallback.");
10172
+ M2.warn("No model providers configured. Using opencode/big-pickle as fallback.");
10151
10173
  }
10152
10174
  Me(formatConfigSummary(config), isUpdate ? "Updated Configuration" : "Installation Complete");
10153
10175
  M2.success(import_picocolors4.default.bold(isUpdate ? "Configuration updated!" : "Installation complete!"));
@@ -10212,7 +10234,9 @@ function createEventState() {
10212
10234
  lastThinkingSummary: "",
10213
10235
  textAtLineStart: true,
10214
10236
  thinkingAtLineStart: false,
10215
- currentMessageId: null
10237
+ currentMessageId: null,
10238
+ messageStartedAtById: {},
10239
+ completionMetaPrintedByMessageId: {}
10216
10240
  };
10217
10241
  }
10218
10242
  // src/cli/run/event-formatting.ts
@@ -10572,6 +10596,19 @@ function getPartMessageId(props) {
10572
10596
  function getDeltaMessageId(props) {
10573
10597
  return props?.messageID;
10574
10598
  }
10599
+ function renderCompletionMetaLine(state, messageID) {
10600
+ if (state.completionMetaPrintedByMessageId[messageID])
10601
+ return;
10602
+ const startedAt = state.messageStartedAtById[messageID];
10603
+ const elapsedSec = startedAt ? ((Date.now() - startedAt) / 1000).toFixed(1) : "0.0";
10604
+ const agent = state.currentAgent ?? "assistant";
10605
+ const model = state.currentModel ?? "unknown-model";
10606
+ const variant = state.currentVariant ? ` (${state.currentVariant})` : "";
10607
+ process.stdout.write(import_picocolors7.default.dim(`
10608
+ ${displayChars.treeEnd} ${agent} \xB7 ${model}${variant} \xB7 ${elapsedSec}s
10609
+ `));
10610
+ state.completionMetaPrintedByMessageId[messageID] = true;
10611
+ }
10575
10612
  function handleSessionIdle(ctx, payload, state) {
10576
10613
  if (payload.type !== "session.idle")
10577
10614
  return;
@@ -10646,6 +10683,12 @@ function handleMessagePartUpdated(ctx, payload, state) {
10646
10683
  state.hasReceivedMeaningfulWork = true;
10647
10684
  }
10648
10685
  state.lastPartText = part.text;
10686
+ if (part.time?.end) {
10687
+ const messageID = part.messageID ?? state.currentMessageId;
10688
+ if (messageID) {
10689
+ renderCompletionMetaLine(state, messageID);
10690
+ }
10691
+ }
10649
10692
  }
10650
10693
  if (part.type === "tool") {
10651
10694
  handleToolPart(ctx, part, state);
@@ -10740,6 +10783,10 @@ function handleMessageUpdated(ctx, payload, state) {
10740
10783
  state.textAtLineStart = true;
10741
10784
  state.thinkingAtLineStart = false;
10742
10785
  closeThinkBlockIfNeeded(state);
10786
+ if (messageID) {
10787
+ state.messageStartedAtById[messageID] = Date.now();
10788
+ state.completionMetaPrintedByMessageId[messageID] = false;
10789
+ }
10743
10790
  }
10744
10791
  const agent = props?.info?.agent ?? null;
10745
10792
  const model = props?.info?.modelID ?? null;
@@ -23244,6 +23291,10 @@ var AgentOverrideConfigSchema = exports_external.object({
23244
23291
  type: exports_external.enum(["enabled", "disabled"]),
23245
23292
  budgetTokens: exports_external.number().optional()
23246
23293
  }).optional(),
23294
+ ultrawork: exports_external.object({
23295
+ model: exports_external.string(),
23296
+ variant: exports_external.string().optional()
23297
+ }).optional(),
23247
23298
  reasoningEffort: exports_external.enum(["low", "medium", "high", "xhigh"]).optional(),
23248
23299
  textVerbosity: exports_external.enum(["low", "medium", "high"]).optional(),
23249
23300
  providerOptions: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
@@ -23413,6 +23464,7 @@ var HookNameSchema = exports_external.enum([
23413
23464
  "non-interactive-env",
23414
23465
  "interactive-bash-session",
23415
23466
  "thinking-block-validator",
23467
+ "ultrawork-model-override",
23416
23468
  "ralph-loop",
23417
23469
  "category-skill-reminder",
23418
23470
  "compaction-context-injector",
@@ -23424,7 +23476,7 @@ var HookNameSchema = exports_external.enum([
23424
23476
  "delegate-task-retry",
23425
23477
  "prometheus-md-only",
23426
23478
  "sisyphus-junior-notepad",
23427
- "sisyphus-gpt-hephaestus-reminder",
23479
+ "no-sisyphus-gpt",
23428
23480
  "start-work",
23429
23481
  "atlas",
23430
23482
  "unstable-agent-babysitter",
@@ -25192,34 +25244,31 @@ async function withWorkingOpencodePath(startServer, finder = findWorkingOpencode
25192
25244
  }
25193
25245
  }
25194
25246
 
25195
- // src/cli/run/opencode-bin-path.ts
25196
- import { delimiter as delimiter2, dirname as dirname2 } from "path";
25197
- import { createRequire } from "module";
25198
- var resolveFromCurrentModule = createRequire(import.meta.url).resolve;
25199
- function prependResolvedOpencodeBinToPath(env = process.env, resolve2 = resolveFromCurrentModule) {
25200
- let resolvedPath;
25201
- try {
25202
- resolvedPath = resolve2("opencode-ai/bin/opencode");
25203
- } catch {
25204
- return;
25247
+ // src/cli/run/server-connection.ts
25248
+ function isPortStartFailure(error45, port) {
25249
+ if (!(error45 instanceof Error)) {
25250
+ return false;
25205
25251
  }
25206
- const opencodeBinDir = dirname2(resolvedPath);
25207
- const currentPath = env.PATH ?? "";
25208
- const pathSegments = currentPath ? currentPath.split(delimiter2) : [];
25209
- if (pathSegments.includes(opencodeBinDir)) {
25210
- return;
25252
+ return error45.message.includes(`Failed to start server on port ${port}`);
25253
+ }
25254
+ function isPortRangeExhausted(error45) {
25255
+ if (!(error45 instanceof Error)) {
25256
+ return false;
25211
25257
  }
25212
- env.PATH = currentPath ? `${opencodeBinDir}${delimiter2}${currentPath}` : opencodeBinDir;
25258
+ return error45.message.includes("No available port found in range");
25259
+ }
25260
+ async function startServer(options) {
25261
+ const { signal, port } = options;
25262
+ const { client: client3, server: server2 } = await withWorkingOpencodePath(() => createOpencode({ signal, port, hostname: "127.0.0.1" }));
25263
+ console.log(import_picocolors9.default.dim("Server listening at"), import_picocolors9.default.cyan(server2.url));
25264
+ return { client: client3, cleanup: () => server2.close() };
25213
25265
  }
25214
-
25215
- // src/cli/run/server-connection.ts
25216
25266
  async function createServerConnection(options) {
25217
- prependResolvedOpencodeBinToPath();
25218
25267
  const { port, attach, signal } = options;
25219
25268
  if (attach !== undefined) {
25220
25269
  console.log(import_picocolors9.default.dim("Attaching to existing server at"), import_picocolors9.default.cyan(attach));
25221
- const client4 = createOpencodeClient({ baseUrl: attach });
25222
- return { client: client4, cleanup: () => {} };
25270
+ const client3 = createOpencodeClient({ baseUrl: attach });
25271
+ return { client: client3, cleanup: () => {} };
25223
25272
  }
25224
25273
  if (port !== undefined) {
25225
25274
  if (port < 1 || port > 65535) {
@@ -25228,23 +25277,58 @@ async function createServerConnection(options) {
25228
25277
  const available = await isPortAvailable(port, "127.0.0.1");
25229
25278
  if (available) {
25230
25279
  console.log(import_picocolors9.default.dim("Starting server on port"), import_picocolors9.default.cyan(port.toString()));
25231
- const { client: client5, server: server3 } = await withWorkingOpencodePath(() => createOpencode({ signal, port, hostname: "127.0.0.1" }));
25232
- console.log(import_picocolors9.default.dim("Server listening at"), import_picocolors9.default.cyan(server3.url));
25233
- return { client: client5, cleanup: () => server3.close() };
25280
+ try {
25281
+ return await startServer({ signal, port });
25282
+ } catch (error45) {
25283
+ if (!isPortStartFailure(error45, port)) {
25284
+ throw error45;
25285
+ }
25286
+ const stillAvailable = await isPortAvailable(port, "127.0.0.1");
25287
+ if (stillAvailable) {
25288
+ throw error45;
25289
+ }
25290
+ console.log(import_picocolors9.default.dim("Port"), import_picocolors9.default.cyan(port.toString()), import_picocolors9.default.dim("became occupied, attaching to existing server"));
25291
+ const client4 = createOpencodeClient({ baseUrl: `http://127.0.0.1:${port}` });
25292
+ return { client: client4, cleanup: () => {} };
25293
+ }
25234
25294
  }
25235
25295
  console.log(import_picocolors9.default.dim("Port"), import_picocolors9.default.cyan(port.toString()), import_picocolors9.default.dim("is occupied, attaching to existing server"));
25236
- const client4 = createOpencodeClient({ baseUrl: `http://127.0.0.1:${port}` });
25237
- return { client: client4, cleanup: () => {} };
25296
+ const client3 = createOpencodeClient({ baseUrl: `http://127.0.0.1:${port}` });
25297
+ return { client: client3, cleanup: () => {} };
25298
+ }
25299
+ let selectedPort;
25300
+ let wasAutoSelected;
25301
+ try {
25302
+ const selected = await getAvailableServerPort(DEFAULT_SERVER_PORT, "127.0.0.1");
25303
+ selectedPort = selected.port;
25304
+ wasAutoSelected = selected.wasAutoSelected;
25305
+ } catch (error45) {
25306
+ if (!isPortRangeExhausted(error45)) {
25307
+ throw error45;
25308
+ }
25309
+ const defaultPortIsAvailable = await isPortAvailable(DEFAULT_SERVER_PORT, "127.0.0.1");
25310
+ if (defaultPortIsAvailable) {
25311
+ throw error45;
25312
+ }
25313
+ console.log(import_picocolors9.default.dim("Port range exhausted, attaching to existing server on"), import_picocolors9.default.cyan(DEFAULT_SERVER_PORT.toString()));
25314
+ const client3 = createOpencodeClient({ baseUrl: `http://127.0.0.1:${DEFAULT_SERVER_PORT}` });
25315
+ return { client: client3, cleanup: () => {} };
25238
25316
  }
25239
- const { port: selectedPort, wasAutoSelected } = await getAvailableServerPort(DEFAULT_SERVER_PORT, "127.0.0.1");
25240
25317
  if (wasAutoSelected) {
25241
25318
  console.log(import_picocolors9.default.dim("Auto-selected port"), import_picocolors9.default.cyan(selectedPort.toString()));
25242
25319
  } else {
25243
25320
  console.log(import_picocolors9.default.dim("Starting server on port"), import_picocolors9.default.cyan(selectedPort.toString()));
25244
25321
  }
25245
- const { client: client3, server: server2 } = await withWorkingOpencodePath(() => createOpencode({ signal, port: selectedPort, hostname: "127.0.0.1" }));
25246
- console.log(import_picocolors9.default.dim("Server listening at"), import_picocolors9.default.cyan(server2.url));
25247
- return { client: client3, cleanup: () => server2.close() };
25322
+ try {
25323
+ return await startServer({ signal, port: selectedPort });
25324
+ } catch (error45) {
25325
+ if (!isPortStartFailure(error45, selectedPort)) {
25326
+ throw error45;
25327
+ }
25328
+ const { port: retryPort } = await getAvailableServerPort(selectedPort + 1, "127.0.0.1");
25329
+ console.log(import_picocolors9.default.dim("Retrying server start on port"), import_picocolors9.default.cyan(retryPort.toString()));
25330
+ return await startServer({ signal, port: retryPort });
25331
+ }
25248
25332
  }
25249
25333
 
25250
25334
  // src/cli/run/session-resolver.ts
@@ -25427,7 +25511,7 @@ var NOTEPAD_DIR = "notepads";
25427
25511
  var NOTEPAD_BASE_PATH = `${BOULDER_DIR}/${NOTEPAD_DIR}`;
25428
25512
  // src/features/boulder-state/storage.ts
25429
25513
  import { existsSync as existsSync11, readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync3, readdirSync } from "fs";
25430
- import { dirname as dirname3, join as join9, basename } from "path";
25514
+ import { dirname as dirname2, join as join9, basename } from "path";
25431
25515
  function getBoulderFilePath(directory) {
25432
25516
  return join9(directory, BOULDER_DIR, BOULDER_FILE);
25433
25517
  }
@@ -25508,7 +25592,7 @@ function getActiveContinuationMarkerReason(marker) {
25508
25592
  // src/hooks/ralph-loop/storage.ts
25509
25593
  init_frontmatter();
25510
25594
  import { existsSync as existsSync13, readFileSync as readFileSync13, writeFileSync as writeFileSync9, unlinkSync, mkdirSync as mkdirSync5 } from "fs";
25511
- import { dirname as dirname4, join as join11 } from "path";
25595
+ import { dirname as dirname3, join as join11 } from "path";
25512
25596
 
25513
25597
  // src/hooks/ralph-loop/constants.ts
25514
25598
  var DEFAULT_STATE_FILE = ".sisyphus/ralph-loop.local.md";
@@ -25593,7 +25677,7 @@ async function checkCompletionConditions(ctx) {
25593
25677
  const continuationState = getContinuationState(ctx.directory, ctx.sessionID);
25594
25678
  if (continuationState.hasActiveHookMarker) {
25595
25679
  const reason = continuationState.activeHookMarkerReason ?? "continuation hook is active";
25596
- console.log(import_picocolors13.default.dim(` Waiting: ${reason}`));
25680
+ logWaiting(ctx, reason);
25597
25681
  return false;
25598
25682
  }
25599
25683
  if (!continuationState.hasTodoHookMarker && !await areAllTodosComplete(ctx)) {
@@ -25602,7 +25686,7 @@ async function checkCompletionConditions(ctx) {
25602
25686
  if (!await areAllChildrenIdle(ctx)) {
25603
25687
  return false;
25604
25688
  }
25605
- if (!areContinuationHooksIdle(continuationState)) {
25689
+ if (!areContinuationHooksIdle(ctx, continuationState)) {
25606
25690
  return false;
25607
25691
  }
25608
25692
  return true;
@@ -25611,13 +25695,13 @@ async function checkCompletionConditions(ctx) {
25611
25695
  return false;
25612
25696
  }
25613
25697
  }
25614
- function areContinuationHooksIdle(continuationState) {
25698
+ function areContinuationHooksIdle(ctx, continuationState) {
25615
25699
  if (continuationState.hasActiveBoulder) {
25616
- console.log(import_picocolors13.default.dim(" Waiting: boulder continuation is active"));
25700
+ logWaiting(ctx, "boulder continuation is active");
25617
25701
  return false;
25618
25702
  }
25619
25703
  if (continuationState.hasActiveRalphLoop) {
25620
- console.log(import_picocolors13.default.dim(" Waiting: ralph-loop continuation is active"));
25704
+ logWaiting(ctx, "ralph-loop continuation is active");
25621
25705
  return false;
25622
25706
  }
25623
25707
  return true;
@@ -25630,7 +25714,7 @@ async function areAllTodosComplete(ctx) {
25630
25714
  const todos = normalizeSDKResponse(todosRes, []);
25631
25715
  const incompleteTodos = todos.filter((t) => t.status !== "completed" && t.status !== "cancelled");
25632
25716
  if (incompleteTodos.length > 0) {
25633
- console.log(import_picocolors13.default.dim(` Waiting: ${incompleteTodos.length} todos remaining`));
25717
+ logWaiting(ctx, `${incompleteTodos.length} todos remaining`);
25634
25718
  return false;
25635
25719
  }
25636
25720
  return true;
@@ -25654,7 +25738,7 @@ async function areAllDescendantsIdle(ctx, sessionID, allStatuses) {
25654
25738
  for (const child of children) {
25655
25739
  const status = allStatuses[child.id];
25656
25740
  if (status && status.type !== "idle") {
25657
- console.log(import_picocolors13.default.dim(` Waiting: session ${child.id.slice(0, 8)}... is ${status.type}`));
25741
+ logWaiting(ctx, `session ${child.id.slice(0, 8)}... is ${status.type}`);
25658
25742
  return false;
25659
25743
  }
25660
25744
  const descendantsIdle = await areAllDescendantsIdle(ctx, child.id, allStatuses);
@@ -25664,6 +25748,12 @@ async function areAllDescendantsIdle(ctx, sessionID, allStatuses) {
25664
25748
  }
25665
25749
  return true;
25666
25750
  }
25751
+ function logWaiting(ctx, message) {
25752
+ if (!ctx.verbose) {
25753
+ return;
25754
+ }
25755
+ console.log(import_picocolors13.default.dim(` Waiting: ${message}`));
25756
+ }
25667
25757
 
25668
25758
  // src/cli/run/poll-for-completion.ts
25669
25759
  init_shared();
@@ -25783,6 +25873,41 @@ async function loadAgentProfileColors(client3) {
25783
25873
  }
25784
25874
  }
25785
25875
 
25876
+ // src/cli/run/stdin-suppression.ts
25877
+ function includesCtrlC(chunk) {
25878
+ const text = typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
25879
+ return text.includes("\x03");
25880
+ }
25881
+ function suppressRunInput(stdin = process.stdin, onInterrupt = () => {
25882
+ process.kill(process.pid, "SIGINT");
25883
+ }) {
25884
+ if (!stdin.isTTY) {
25885
+ return () => {};
25886
+ }
25887
+ const wasRaw = stdin.isRaw === true;
25888
+ const wasPaused = stdin.isPaused?.() ?? false;
25889
+ const canSetRawMode = typeof stdin.setRawMode === "function";
25890
+ const onData = (chunk) => {
25891
+ if (includesCtrlC(chunk)) {
25892
+ onInterrupt();
25893
+ }
25894
+ };
25895
+ if (canSetRawMode) {
25896
+ stdin.setRawMode(true);
25897
+ }
25898
+ stdin.on("data", onData);
25899
+ stdin.resume();
25900
+ return () => {
25901
+ stdin.removeListener("data", onData);
25902
+ if (canSetRawMode) {
25903
+ stdin.setRawMode(wasRaw);
25904
+ }
25905
+ if (wasPaused) {
25906
+ stdin.pause();
25907
+ }
25908
+ };
25909
+ }
25910
+
25786
25911
  // src/cli/run/runner.ts
25787
25912
  var EVENT_PROCESSOR_SHUTDOWN_TIMEOUT_MS = 2000;
25788
25913
  async function waitForEventProcessorShutdown(eventProcessor, timeoutMs = EVENT_PROCESSOR_SHUTDOWN_TIMEOUT_MS) {
@@ -25813,12 +25938,15 @@ async function run(options) {
25813
25938
  const cleanup = () => {
25814
25939
  serverCleanup();
25815
25940
  };
25816
- process.on("SIGINT", () => {
25941
+ const restoreInput = suppressRunInput();
25942
+ const handleSigint = () => {
25817
25943
  console.log(import_picocolors15.default.yellow(`
25818
25944
  Interrupted. Shutting down...`));
25945
+ restoreInput();
25819
25946
  cleanup();
25820
25947
  process.exit(130);
25821
- });
25948
+ };
25949
+ process.on("SIGINT", handleSigint);
25822
25950
  try {
25823
25951
  const sessionID = await resolveSession({
25824
25952
  client: client3,
@@ -25841,6 +25969,9 @@ Interrupted. Shutting down...`));
25841
25969
  path: { id: sessionID },
25842
25970
  body: {
25843
25971
  agent: resolvedAgent,
25972
+ tools: {
25973
+ question: false
25974
+ },
25844
25975
  parts: [{ type: "text", text: message }]
25845
25976
  },
25846
25977
  query: { directory }
@@ -25872,6 +26003,9 @@ Interrupted. Shutting down...`));
25872
26003
  } catch (err) {
25873
26004
  cleanup();
25874
26005
  throw err;
26006
+ } finally {
26007
+ process.removeListener("SIGINT", handleSigint);
26008
+ restoreInput();
25875
26009
  }
25876
26010
  } catch (err) {
25877
26011
  if (jsonManager)
@@ -26750,8 +26884,8 @@ async function checkConfig() {
26750
26884
 
26751
26885
  // src/cli/doctor/checks/dependencies.ts
26752
26886
  import { existsSync as existsSync24 } from "fs";
26753
- import { createRequire as createRequire2 } from "module";
26754
- import { dirname as dirname7, join as join22 } from "path";
26887
+ import { createRequire } from "module";
26888
+ import { dirname as dirname6, join as join22 } from "path";
26755
26889
  async function checkBinaryExists(binary2) {
26756
26890
  try {
26757
26891
  const path9 = Bun.which(binary2);
@@ -26838,9 +26972,9 @@ async function checkAstGrepNapi() {
26838
26972
  function findCommentCheckerPackageBinary() {
26839
26973
  const binaryName = process.platform === "win32" ? "comment-checker.exe" : "comment-checker";
26840
26974
  try {
26841
- const require2 = createRequire2(import.meta.url);
26975
+ const require2 = createRequire(import.meta.url);
26842
26976
  const pkgPath = require2.resolve("@code-yeongyu/comment-checker/package.json");
26843
- const binaryPath = join22(dirname7(pkgPath), "bin", binaryName);
26977
+ const binaryPath = join22(dirname6(pkgPath), "bin", binaryName);
26844
26978
  if (existsSync24(binaryPath))
26845
26979
  return binaryPath;
26846
26980
  } catch {}
@@ -27458,7 +27592,7 @@ async function doctor(options = { mode: "default" }) {
27458
27592
  // src/features/mcp-oauth/storage.ts
27459
27593
  init_shared();
27460
27594
  import { chmodSync, existsSync as existsSync27, mkdirSync as mkdirSync6, readFileSync as readFileSync28, unlinkSync as unlinkSync2, writeFileSync as writeFileSync12 } from "fs";
27461
- import { dirname as dirname8, join as join25 } from "path";
27595
+ import { dirname as dirname7, join as join25 } from "path";
27462
27596
  var STORAGE_FILE_NAME = "mcp-oauth.json";
27463
27597
  function getMcpOauthStoragePath() {
27464
27598
  return join25(getOpenCodeConfigDir({ binary: "opencode" }), STORAGE_FILE_NAME);
@@ -27508,14 +27642,14 @@ function readStore() {
27508
27642
  return null;
27509
27643
  }
27510
27644
  }
27511
- function writeStore(store) {
27645
+ function writeStore(store2) {
27512
27646
  const filePath = getMcpOauthStoragePath();
27513
27647
  try {
27514
- const dir = dirname8(filePath);
27648
+ const dir = dirname7(filePath);
27515
27649
  if (!existsSync27(dir)) {
27516
27650
  mkdirSync6(dir, { recursive: true });
27517
27651
  }
27518
- writeFileSync12(filePath, JSON.stringify(store, null, 2), { encoding: "utf-8", mode: 384 });
27652
+ writeFileSync12(filePath, JSON.stringify(store2, null, 2), { encoding: "utf-8", mode: 384 });
27519
27653
  chmodSync(filePath, 384);
27520
27654
  return true;
27521
27655
  } catch {
@@ -27523,28 +27657,28 @@ function writeStore(store) {
27523
27657
  }
27524
27658
  }
27525
27659
  function loadToken(serverHost, resource) {
27526
- const store = readStore();
27527
- if (!store)
27660
+ const store2 = readStore();
27661
+ if (!store2)
27528
27662
  return null;
27529
27663
  const key = buildKey(serverHost, resource);
27530
- return store[key] ?? null;
27664
+ return store2[key] ?? null;
27531
27665
  }
27532
27666
  function saveToken(serverHost, resource, token) {
27533
- const store = readStore() ?? {};
27667
+ const store2 = readStore() ?? {};
27534
27668
  const key = buildKey(serverHost, resource);
27535
- store[key] = token;
27536
- return writeStore(store);
27669
+ store2[key] = token;
27670
+ return writeStore(store2);
27537
27671
  }
27538
27672
  function deleteToken(serverHost, resource) {
27539
- const store = readStore();
27540
- if (!store)
27673
+ const store2 = readStore();
27674
+ if (!store2)
27541
27675
  return true;
27542
27676
  const key = buildKey(serverHost, resource);
27543
- if (!(key in store)) {
27677
+ if (!(key in store2)) {
27544
27678
  return true;
27545
27679
  }
27546
- delete store[key];
27547
- if (Object.keys(store).length === 0) {
27680
+ delete store2[key];
27681
+ if (Object.keys(store2).length === 0) {
27548
27682
  try {
27549
27683
  const filePath = getMcpOauthStoragePath();
27550
27684
  if (existsSync27(filePath)) {
@@ -27555,16 +27689,16 @@ function deleteToken(serverHost, resource) {
27555
27689
  return false;
27556
27690
  }
27557
27691
  }
27558
- return writeStore(store);
27692
+ return writeStore(store2);
27559
27693
  }
27560
27694
  function listTokensByHost(serverHost) {
27561
- const store = readStore();
27562
- if (!store)
27695
+ const store2 = readStore();
27696
+ if (!store2)
27563
27697
  return {};
27564
27698
  const host = normalizeHost(serverHost);
27565
27699
  const prefix = `${host}/`;
27566
27700
  const result = {};
27567
- for (const [key, value] of Object.entries(store)) {
27701
+ for (const [key, value] of Object.entries(store2)) {
27568
27702
  if (key.startsWith(prefix)) {
27569
27703
  result[key] = value;
27570
27704
  }