oh-my-opencode 2.1.0 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2257,7 +2257,7 @@ var oracleAgent = {
2257
2257
  temperature: 0.1,
2258
2258
  reasoningEffort: "medium",
2259
2259
  textVerbosity: "high",
2260
- tools: { write: false, edit: false, read: true, task: false, call_omo_agent: true, background_task: false },
2260
+ tools: { write: false, edit: false, task: false, background_task: false },
2261
2261
  prompt: `You are a strategic technical advisor with deep reasoning capabilities, operating as a specialized consultant within an AI-assisted development environment.
2262
2262
 
2263
2263
  ## Context
@@ -2331,7 +2331,7 @@ var librarianAgent = {
2331
2331
  mode: "subagent",
2332
2332
  model: "anthropic/claude-sonnet-4-5",
2333
2333
  temperature: 0.1,
2334
- tools: { write: false, edit: false, bash: true, read: true, background_task: false },
2334
+ tools: { write: false, edit: false, background_task: false },
2335
2335
  prompt: `# THE LIBRARIAN
2336
2336
 
2337
2337
  You are **THE LIBRARIAN**, a specialized open-source codebase understanding agent.
@@ -2570,7 +2570,7 @@ var exploreAgent = {
2570
2570
  mode: "subagent",
2571
2571
  model: "opencode/grok-code",
2572
2572
  temperature: 0.1,
2573
- tools: { write: false, edit: false, bash: true, read: true, background_task: false },
2573
+ tools: { write: false, edit: false, background_task: false },
2574
2574
  prompt: `You are a file search specialist. You excel at thoroughly navigating and exploring codebases.
2575
2575
 
2576
2576
  === CRITICAL: READ-ONLY MODE - NO FILE MODIFICATIONS ===
@@ -3119,7 +3119,7 @@ var multimodalLookerAgent = {
3119
3119
  mode: "subagent",
3120
3120
  model: "google/gemini-2.5-flash",
3121
3121
  temperature: 0.1,
3122
- tools: { Read: true, background_task: false },
3122
+ tools: { write: false, edit: false, bash: false, background_task: false },
3123
3123
  prompt: `You interpret media files that cannot be read as plain text.
3124
3124
 
3125
3125
  Your job: examine the attached file and extract ONLY what was requested.
@@ -3183,7 +3183,11 @@ import { spawn } from "child_process";
3183
3183
  import { exec } from "child_process";
3184
3184
  import { promisify } from "util";
3185
3185
  import { existsSync } from "fs";
3186
+ import { homedir } from "os";
3186
3187
  var DEFAULT_ZSH_PATHS = ["/bin/zsh", "/usr/bin/zsh", "/usr/local/bin/zsh"];
3188
+ function getHomeDir() {
3189
+ return process.env.HOME || process.env.USERPROFILE || homedir();
3190
+ }
3187
3191
  function findZshPath(customZshPath) {
3188
3192
  if (customZshPath && existsSync(customZshPath)) {
3189
3193
  return customZshPath;
@@ -3197,7 +3201,7 @@ function findZshPath(customZshPath) {
3197
3201
  }
3198
3202
  var execAsync = promisify(exec);
3199
3203
  async function executeHookCommand(command, stdin, cwd, options) {
3200
- const home = process.env.HOME ?? "";
3204
+ const home = getHomeDir();
3201
3205
  let expandedCommand = command.replace(/^~(?=\/|$)/g, home).replace(/\s~(?=\/)/g, ` ${home}`).replace(/\$CLAUDE_PROJECT_DIR/g, cwd).replace(/\$\{CLAUDE_PROJECT_DIR\}/g, cwd);
3202
3206
  let finalCommand = expandedCommand;
3203
3207
  if (options?.forceZsh) {
@@ -3692,8 +3696,8 @@ import { join as join4 } from "path";
3692
3696
 
3693
3697
  // src/features/hook-message-injector/constants.ts
3694
3698
  import { join as join3 } from "path";
3695
- import { homedir } from "os";
3696
- var xdgData = process.env.XDG_DATA_HOME || join3(homedir(), ".local", "share");
3699
+ import { homedir as homedir2 } from "os";
3700
+ var xdgData = process.env.XDG_DATA_HOME || join3(homedir2(), ".local", "share");
3697
3701
  var OPENCODE_STORAGE = join3(xdgData, "opencode", "storage");
3698
3702
  var MESSAGE_STORAGE = join3(OPENCODE_STORAGE, "message");
3699
3703
  var PART_STORAGE = join3(OPENCODE_STORAGE, "part");
@@ -4066,18 +4070,34 @@ function getDefaultSoundPath(p) {
4066
4070
  }
4067
4071
  }
4068
4072
  async function sendNotification(ctx, p, title, message) {
4069
- const escapedTitle = title.replace(/"/g, "\\\"").replace(/'/g, "\\'");
4070
- const escapedMessage = message.replace(/"/g, "\\\"").replace(/'/g, "\\'");
4071
4073
  switch (p) {
4072
- case "darwin":
4073
- await ctx.$`osascript -e ${'display notification "' + escapedMessage + '" with title "' + escapedTitle + '"'}`;
4074
+ case "darwin": {
4075
+ const esTitle = title.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
4076
+ const esMessage = message.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
4077
+ await ctx.$`osascript -e ${'display notification "' + esMessage + '" with title "' + esTitle + '"'}`;
4074
4078
  break;
4079
+ }
4075
4080
  case "linux":
4076
- await ctx.$`notify-send ${escapedTitle} ${escapedMessage}`.catch(() => {});
4081
+ await ctx.$`notify-send ${title} ${message} 2>/dev/null`.catch(() => {});
4077
4082
  break;
4078
- case "win32":
4079
- await ctx.$`powershell -Command ${"[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); [System.Windows.Forms.MessageBox]::Show('" + escapedMessage + "', '" + escapedTitle + "')"}`;
4083
+ case "win32": {
4084
+ const psTitle = title.replace(/'/g, "''");
4085
+ const psMessage = message.replace(/'/g, "''");
4086
+ const toastScript = `
4087
+ [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null
4088
+ $Template = [Windows.UI.Notifications.ToastNotificationManager]::GetTemplateContent([Windows.UI.Notifications.ToastTemplateType]::ToastText02)
4089
+ $RawXml = [xml] $Template.GetXml()
4090
+ ($RawXml.toast.visual.binding.text | Where-Object {$_.id -eq '1'}).AppendChild($RawXml.CreateTextNode('${psTitle}')) | Out-Null
4091
+ ($RawXml.toast.visual.binding.text | Where-Object {$_.id -eq '2'}).AppendChild($RawXml.CreateTextNode('${psMessage}')) | Out-Null
4092
+ $SerializedXml = New-Object Windows.Data.Xml.Dom.XmlDocument
4093
+ $SerializedXml.LoadXml($RawXml.OuterXml)
4094
+ $Toast = [Windows.UI.Notifications.ToastNotification]::new($SerializedXml)
4095
+ $Notifier = [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier('OpenCode')
4096
+ $Notifier.Show($Toast)
4097
+ `.trim().replace(/\n/g, "; ");
4098
+ await ctx.$`powershell -Command ${toastScript}`.catch(() => {});
4080
4099
  break;
4100
+ }
4081
4101
  }
4082
4102
  }
4083
4103
  async function playSound(ctx, p, soundPath) {
@@ -4086,8 +4106,8 @@ async function playSound(ctx, p, soundPath) {
4086
4106
  ctx.$`afplay ${soundPath}`.catch(() => {});
4087
4107
  break;
4088
4108
  case "linux":
4089
- ctx.$`paplay ${soundPath}`.catch(() => {
4090
- ctx.$`aplay ${soundPath}`.catch(() => {});
4109
+ ctx.$`paplay ${soundPath} 2>/dev/null`.catch(() => {
4110
+ ctx.$`aplay ${soundPath} 2>/dev/null`.catch(() => {});
4091
4111
  });
4092
4112
  break;
4093
4113
  case "win32":
@@ -4516,7 +4536,25 @@ function getErrorMessage(error) {
4516
4536
  if (typeof error === "string")
4517
4537
  return error.toLowerCase();
4518
4538
  const errorObj = error;
4519
- return (errorObj.data?.message || errorObj.error?.message || errorObj.message || "").toLowerCase();
4539
+ const paths = [
4540
+ errorObj.data,
4541
+ errorObj.error,
4542
+ errorObj,
4543
+ errorObj.data?.error
4544
+ ];
4545
+ for (const obj of paths) {
4546
+ if (obj && typeof obj === "object") {
4547
+ const msg = obj.message;
4548
+ if (typeof msg === "string" && msg.length > 0) {
4549
+ return msg.toLowerCase();
4550
+ }
4551
+ }
4552
+ }
4553
+ try {
4554
+ return JSON.stringify(error).toLowerCase();
4555
+ } catch {
4556
+ return "";
4557
+ }
4520
4558
  }
4521
4559
  function extractMessageIndex(error) {
4522
4560
  const message = getErrorMessage(error);
@@ -4534,7 +4572,7 @@ function detectErrorType(error) {
4534
4572
  if (message.includes("thinking is disabled") && message.includes("cannot contain")) {
4535
4573
  return "thinking_disabled_violation";
4536
4574
  }
4537
- if (message.includes("non-empty content") || message.includes("must have non-empty content")) {
4575
+ if (message.includes("non-empty content") || message.includes("must have non-empty content") || message.includes("content") && message.includes("is empty") || message.includes("content field") && message.includes("empty")) {
4538
4576
  return "empty_content_message";
4539
4577
  }
4540
4578
  return null;
@@ -4727,15 +4765,16 @@ import { createRequire as createRequire2 } from "module";
4727
4765
  import { dirname, join as join9 } from "path";
4728
4766
  import { existsSync as existsSync7 } from "fs";
4729
4767
  import * as fs2 from "fs";
4768
+ import { tmpdir as tmpdir3 } from "os";
4730
4769
 
4731
4770
  // src/hooks/comment-checker/downloader.ts
4732
4771
  var {spawn: spawn2 } = globalThis.Bun;
4733
4772
  import { existsSync as existsSync6, mkdirSync as mkdirSync3, chmodSync, unlinkSync as unlinkSync2, appendFileSync as appendFileSync2 } from "fs";
4734
4773
  import { join as join8 } from "path";
4735
- import { homedir as homedir2 } from "os";
4774
+ import { homedir as homedir3, tmpdir as tmpdir2 } from "os";
4736
4775
  import { createRequire } from "module";
4737
4776
  var DEBUG = process.env.COMMENT_CHECKER_DEBUG === "1";
4738
- var DEBUG_FILE = "/tmp/comment-checker-debug.log";
4777
+ var DEBUG_FILE = join8(tmpdir2(), "comment-checker-debug.log");
4739
4778
  function debugLog(...args) {
4740
4779
  if (DEBUG) {
4741
4780
  const msg = `[${new Date().toISOString()}] [comment-checker:downloader] ${args.map((a) => typeof a === "object" ? JSON.stringify(a, null, 2) : String(a)).join(" ")}
@@ -4753,7 +4792,7 @@ var PLATFORM_MAP = {
4753
4792
  };
4754
4793
  function getCacheDir() {
4755
4794
  const xdgCache2 = process.env.XDG_CACHE_HOME;
4756
- const base = xdgCache2 || join8(homedir2(), ".cache");
4795
+ const base = xdgCache2 || join8(homedir3(), ".cache");
4757
4796
  return join8(base, "oh-my-opencode", "bin");
4758
4797
  }
4759
4798
  function getBinaryName() {
@@ -4863,7 +4902,7 @@ async function ensureCommentCheckerBinary() {
4863
4902
 
4864
4903
  // src/hooks/comment-checker/cli.ts
4865
4904
  var DEBUG2 = process.env.COMMENT_CHECKER_DEBUG === "1";
4866
- var DEBUG_FILE2 = "/tmp/comment-checker-debug.log";
4905
+ var DEBUG_FILE2 = join9(tmpdir3(), "comment-checker-debug.log");
4867
4906
  function debugLog2(...args) {
4868
4907
  if (DEBUG2) {
4869
4908
  const msg = `[${new Date().toISOString()}] [comment-checker:cli] ${args.map((a) => typeof a === "object" ? JSON.stringify(a, null, 2) : String(a)).join(" ")}
@@ -4976,8 +5015,10 @@ async function runCommentChecker(input, cliPath) {
4976
5015
  // src/hooks/comment-checker/index.ts
4977
5016
  import * as fs3 from "fs";
4978
5017
  import { existsSync as existsSync8 } from "fs";
5018
+ import { tmpdir as tmpdir4 } from "os";
5019
+ import { join as join10 } from "path";
4979
5020
  var DEBUG3 = process.env.COMMENT_CHECKER_DEBUG === "1";
4980
- var DEBUG_FILE3 = "/tmp/comment-checker-debug.log";
5021
+ var DEBUG_FILE3 = join10(tmpdir4(), "comment-checker-debug.log");
4981
5022
  function debugLog3(...args) {
4982
5023
  if (DEBUG3) {
4983
5024
  const msg = `[${new Date().toISOString()}] [comment-checker:hook] ${args.map((a) => typeof a === "object" ? JSON.stringify(a, null, 2) : String(a)).join(" ")}
@@ -5123,7 +5164,7 @@ function createToolOutputTruncatorHook(ctx) {
5123
5164
  }
5124
5165
  // src/hooks/directory-agents-injector/index.ts
5125
5166
  import { existsSync as existsSync10, readFileSync as readFileSync5 } from "fs";
5126
- import { dirname as dirname2, join as join12, resolve as resolve2 } from "path";
5167
+ import { dirname as dirname2, join as join13, resolve as resolve2 } from "path";
5127
5168
 
5128
5169
  // src/hooks/directory-agents-injector/storage.ts
5129
5170
  import {
@@ -5133,17 +5174,17 @@ import {
5133
5174
  writeFileSync as writeFileSync3,
5134
5175
  unlinkSync as unlinkSync3
5135
5176
  } from "fs";
5136
- import { join as join11 } from "path";
5177
+ import { join as join12 } from "path";
5137
5178
 
5138
5179
  // src/hooks/directory-agents-injector/constants.ts
5139
- import { join as join10 } from "path";
5140
- var OPENCODE_STORAGE3 = join10(xdgData2 ?? "", "opencode", "storage");
5141
- var AGENTS_INJECTOR_STORAGE = join10(OPENCODE_STORAGE3, "directory-agents");
5180
+ import { join as join11 } from "path";
5181
+ var OPENCODE_STORAGE3 = join11(xdgData2 ?? "", "opencode", "storage");
5182
+ var AGENTS_INJECTOR_STORAGE = join11(OPENCODE_STORAGE3, "directory-agents");
5142
5183
  var AGENTS_FILENAME = "AGENTS.md";
5143
5184
 
5144
5185
  // src/hooks/directory-agents-injector/storage.ts
5145
5186
  function getStoragePath(sessionID) {
5146
- return join11(AGENTS_INJECTOR_STORAGE, `${sessionID}.json`);
5187
+ return join12(AGENTS_INJECTOR_STORAGE, `${sessionID}.json`);
5147
5188
  }
5148
5189
  function loadInjectedPaths(sessionID) {
5149
5190
  const filePath = getStoragePath(sessionID);
@@ -5195,7 +5236,7 @@ function createDirectoryAgentsInjectorHook(ctx) {
5195
5236
  const found = [];
5196
5237
  let current = startDir;
5197
5238
  while (true) {
5198
- const agentsPath = join12(current, AGENTS_FILENAME);
5239
+ const agentsPath = join13(current, AGENTS_FILENAME);
5199
5240
  if (existsSync10(agentsPath)) {
5200
5241
  found.push(agentsPath);
5201
5242
  }
@@ -5264,7 +5305,7 @@ ${content}`;
5264
5305
  }
5265
5306
  // src/hooks/directory-readme-injector/index.ts
5266
5307
  import { existsSync as existsSync12, readFileSync as readFileSync7 } from "fs";
5267
- import { dirname as dirname3, join as join15, resolve as resolve3 } from "path";
5308
+ import { dirname as dirname3, join as join16, resolve as resolve3 } from "path";
5268
5309
 
5269
5310
  // src/hooks/directory-readme-injector/storage.ts
5270
5311
  import {
@@ -5274,17 +5315,17 @@ import {
5274
5315
  writeFileSync as writeFileSync4,
5275
5316
  unlinkSync as unlinkSync4
5276
5317
  } from "fs";
5277
- import { join as join14 } from "path";
5318
+ import { join as join15 } from "path";
5278
5319
 
5279
5320
  // src/hooks/directory-readme-injector/constants.ts
5280
- import { join as join13 } from "path";
5281
- var OPENCODE_STORAGE4 = join13(xdgData2 ?? "", "opencode", "storage");
5282
- var README_INJECTOR_STORAGE = join13(OPENCODE_STORAGE4, "directory-readme");
5321
+ import { join as join14 } from "path";
5322
+ var OPENCODE_STORAGE4 = join14(xdgData2 ?? "", "opencode", "storage");
5323
+ var README_INJECTOR_STORAGE = join14(OPENCODE_STORAGE4, "directory-readme");
5283
5324
  var README_FILENAME = "README.md";
5284
5325
 
5285
5326
  // src/hooks/directory-readme-injector/storage.ts
5286
5327
  function getStoragePath2(sessionID) {
5287
- return join14(README_INJECTOR_STORAGE, `${sessionID}.json`);
5328
+ return join15(README_INJECTOR_STORAGE, `${sessionID}.json`);
5288
5329
  }
5289
5330
  function loadInjectedPaths2(sessionID) {
5290
5331
  const filePath = getStoragePath2(sessionID);
@@ -5336,7 +5377,7 @@ function createDirectoryReadmeInjectorHook(ctx) {
5336
5377
  const found = [];
5337
5378
  let current = startDir;
5338
5379
  while (true) {
5339
- const readmePath = join15(current, README_FILENAME);
5380
+ const readmePath = join16(current, README_FILENAME);
5340
5381
  if (existsSync12(readmePath)) {
5341
5382
  found.push(readmePath);
5342
5383
  }
@@ -5565,11 +5606,15 @@ function parseAnthropicTokenLimitError(err) {
5565
5606
 
5566
5607
  // src/hooks/anthropic-auto-compact/types.ts
5567
5608
  var RETRY_CONFIG = {
5568
- maxAttempts: 5,
5609
+ maxAttempts: 2,
5569
5610
  initialDelayMs: 2000,
5570
5611
  backoffFactor: 2,
5571
5612
  maxDelayMs: 30000
5572
5613
  };
5614
+ var FALLBACK_CONFIG = {
5615
+ maxRevertAttempts: 3,
5616
+ minMessagesRequired: 2
5617
+ };
5573
5618
 
5574
5619
  // src/hooks/anthropic-auto-compact/executor.ts
5575
5620
  function calculateRetryDelay(attempt) {
@@ -5589,6 +5634,92 @@ function getOrCreateRetryState(autoCompactState, sessionID) {
5589
5634
  }
5590
5635
  return state;
5591
5636
  }
5637
+ function getOrCreateFallbackState(autoCompactState, sessionID) {
5638
+ let state = autoCompactState.fallbackStateBySession.get(sessionID);
5639
+ if (!state) {
5640
+ state = { revertAttempt: 0 };
5641
+ autoCompactState.fallbackStateBySession.set(sessionID, state);
5642
+ }
5643
+ return state;
5644
+ }
5645
+ async function getLastMessagePair(sessionID, client, directory) {
5646
+ try {
5647
+ const resp = await client.session.messages({
5648
+ path: { id: sessionID },
5649
+ query: { directory }
5650
+ });
5651
+ const data = resp.data;
5652
+ if (!Array.isArray(data) || data.length < FALLBACK_CONFIG.minMessagesRequired) {
5653
+ return null;
5654
+ }
5655
+ const reversed = [...data].reverse();
5656
+ const lastAssistant = reversed.find((m) => {
5657
+ const msg = m;
5658
+ const info = msg.info;
5659
+ return info?.role === "assistant";
5660
+ });
5661
+ const lastUser = reversed.find((m) => {
5662
+ const msg = m;
5663
+ const info = msg.info;
5664
+ return info?.role === "user";
5665
+ });
5666
+ if (!lastUser)
5667
+ return null;
5668
+ const userInfo = lastUser.info;
5669
+ const userMessageID = userInfo?.id;
5670
+ if (!userMessageID)
5671
+ return null;
5672
+ let assistantMessageID;
5673
+ if (lastAssistant) {
5674
+ const assistantInfo = lastAssistant.info;
5675
+ assistantMessageID = assistantInfo?.id;
5676
+ }
5677
+ return { userMessageID, assistantMessageID };
5678
+ } catch {
5679
+ return null;
5680
+ }
5681
+ }
5682
+ async function executeRevertFallback(sessionID, autoCompactState, client, directory) {
5683
+ const fallbackState = getOrCreateFallbackState(autoCompactState, sessionID);
5684
+ if (fallbackState.revertAttempt >= FALLBACK_CONFIG.maxRevertAttempts) {
5685
+ return false;
5686
+ }
5687
+ const pair = await getLastMessagePair(sessionID, client, directory);
5688
+ if (!pair) {
5689
+ return false;
5690
+ }
5691
+ await client.tui.showToast({
5692
+ body: {
5693
+ title: "\u26A0\uFE0F Emergency Recovery",
5694
+ message: `Context too large. Removing last message pair to recover session...`,
5695
+ variant: "warning",
5696
+ duration: 4000
5697
+ }
5698
+ }).catch(() => {});
5699
+ try {
5700
+ if (pair.assistantMessageID) {
5701
+ await client.session.revert({
5702
+ path: { id: sessionID },
5703
+ body: { messageID: pair.assistantMessageID },
5704
+ query: { directory }
5705
+ });
5706
+ }
5707
+ await client.session.revert({
5708
+ path: { id: sessionID },
5709
+ body: { messageID: pair.userMessageID },
5710
+ query: { directory }
5711
+ });
5712
+ fallbackState.revertAttempt++;
5713
+ fallbackState.lastRevertedMessageID = pair.userMessageID;
5714
+ const retryState = autoCompactState.retryStateBySession.get(sessionID);
5715
+ if (retryState) {
5716
+ retryState.attempt = 0;
5717
+ }
5718
+ return true;
5719
+ } catch {
5720
+ return false;
5721
+ }
5722
+ }
5592
5723
  async function getLastAssistant(sessionID, client, directory) {
5593
5724
  try {
5594
5725
  const resp = await client.session.messages({
@@ -5615,15 +5746,34 @@ function clearSessionState(autoCompactState, sessionID) {
5615
5746
  autoCompactState.pendingCompact.delete(sessionID);
5616
5747
  autoCompactState.errorDataBySession.delete(sessionID);
5617
5748
  autoCompactState.retryStateBySession.delete(sessionID);
5749
+ autoCompactState.fallbackStateBySession.delete(sessionID);
5618
5750
  }
5619
5751
  async function executeCompact(sessionID, msg, autoCompactState, client, directory) {
5620
5752
  const retryState = getOrCreateRetryState(autoCompactState, sessionID);
5621
5753
  if (!shouldRetry(retryState)) {
5754
+ const fallbackState = getOrCreateFallbackState(autoCompactState, sessionID);
5755
+ if (fallbackState.revertAttempt < FALLBACK_CONFIG.maxRevertAttempts) {
5756
+ const reverted = await executeRevertFallback(sessionID, autoCompactState, client, directory);
5757
+ if (reverted) {
5758
+ await client.tui.showToast({
5759
+ body: {
5760
+ title: "Recovery Attempt",
5761
+ message: "Message removed. Retrying compaction...",
5762
+ variant: "info",
5763
+ duration: 3000
5764
+ }
5765
+ }).catch(() => {});
5766
+ setTimeout(() => {
5767
+ executeCompact(sessionID, msg, autoCompactState, client, directory);
5768
+ }, 1000);
5769
+ return;
5770
+ }
5771
+ }
5622
5772
  clearSessionState(autoCompactState, sessionID);
5623
5773
  await client.tui.showToast({
5624
5774
  body: {
5625
5775
  title: "Auto Compact Failed",
5626
- message: `Failed after ${RETRY_CONFIG.maxAttempts} attempts. Please try manual compact.`,
5776
+ message: `Failed after ${RETRY_CONFIG.maxAttempts} retries and ${FALLBACK_CONFIG.maxRevertAttempts} message removals. Please start a new session.`,
5627
5777
  variant: "error",
5628
5778
  duration: 5000
5629
5779
  }
@@ -5669,7 +5819,8 @@ function createAutoCompactState() {
5669
5819
  return {
5670
5820
  pendingCompact: new Set,
5671
5821
  errorDataBySession: new Map,
5672
- retryStateBySession: new Map
5822
+ retryStateBySession: new Map,
5823
+ fallbackStateBySession: new Map
5673
5824
  };
5674
5825
  }
5675
5826
  function createAnthropicAutoCompactHook(ctx) {
@@ -5682,6 +5833,7 @@ function createAnthropicAutoCompactHook(ctx) {
5682
5833
  autoCompactState.pendingCompact.delete(sessionInfo.id);
5683
5834
  autoCompactState.errorDataBySession.delete(sessionInfo.id);
5684
5835
  autoCompactState.retryStateBySession.delete(sessionInfo.id);
5836
+ autoCompactState.fallbackStateBySession.delete(sessionInfo.id);
5685
5837
  }
5686
5838
  return;
5687
5839
  }
@@ -6024,8 +6176,8 @@ function createThinkModeHook() {
6024
6176
  };
6025
6177
  }
6026
6178
  // src/hooks/claude-code-hooks/config.ts
6027
- import { homedir as homedir3 } from "os";
6028
- import { join as join16 } from "path";
6179
+ import { homedir as homedir4 } from "os";
6180
+ import { join as join17 } from "path";
6029
6181
  import { existsSync as existsSync13 } from "fs";
6030
6182
  function normalizeHookMatcher(raw) {
6031
6183
  return {
@@ -6049,11 +6201,11 @@ function normalizeHooksConfig(raw) {
6049
6201
  return result;
6050
6202
  }
6051
6203
  function getClaudeSettingsPaths(customPath) {
6052
- const home = homedir3();
6204
+ const home = homedir4();
6053
6205
  const paths = [
6054
- join16(home, ".claude", "settings.json"),
6055
- join16(process.cwd(), ".claude", "settings.json"),
6056
- join16(process.cwd(), ".claude", "settings.local.json")
6206
+ join17(home, ".claude", "settings.json"),
6207
+ join17(process.cwd(), ".claude", "settings.json"),
6208
+ join17(process.cwd(), ".claude", "settings.local.json")
6057
6209
  ];
6058
6210
  if (customPath && existsSync13(customPath)) {
6059
6211
  paths.unshift(customPath);
@@ -6097,11 +6249,11 @@ async function loadClaudeHooksConfig(customSettingsPath) {
6097
6249
 
6098
6250
  // src/hooks/claude-code-hooks/config-loader.ts
6099
6251
  import { existsSync as existsSync14 } from "fs";
6100
- import { homedir as homedir4 } from "os";
6101
- import { join as join17 } from "path";
6102
- var USER_CONFIG_PATH = join17(homedir4(), ".config", "opencode", "opencode-cc-plugin.json");
6252
+ import { homedir as homedir5 } from "os";
6253
+ import { join as join18 } from "path";
6254
+ var USER_CONFIG_PATH = join18(homedir5(), ".config", "opencode", "opencode-cc-plugin.json");
6103
6255
  function getProjectConfigPath() {
6104
- return join17(process.cwd(), ".opencode", "opencode-cc-plugin.json");
6256
+ return join18(process.cwd(), ".opencode", "opencode-cc-plugin.json");
6105
6257
  }
6106
6258
  async function loadConfigFromPath(path3) {
6107
6259
  if (!existsSync14(path3)) {
@@ -6169,8 +6321,9 @@ function isHookCommandDisabled(eventType, command, config) {
6169
6321
  }
6170
6322
 
6171
6323
  // src/hooks/claude-code-hooks/plugin-config.ts
6324
+ var isWindows = process.platform === "win32";
6172
6325
  var DEFAULT_CONFIG = {
6173
- forceZsh: true,
6326
+ forceZsh: !isWindows,
6174
6327
  zshPath: "/bin/zsh"
6175
6328
  };
6176
6329
 
@@ -6282,13 +6435,13 @@ async function executePreToolUseHooks(ctx, config, extendedConfig) {
6282
6435
  }
6283
6436
 
6284
6437
  // src/hooks/claude-code-hooks/transcript.ts
6285
- import { join as join18 } from "path";
6438
+ import { join as join19 } from "path";
6286
6439
  import { mkdirSync as mkdirSync6, appendFileSync as appendFileSync5, existsSync as existsSync15, writeFileSync as writeFileSync5, unlinkSync as unlinkSync5 } from "fs";
6287
- import { homedir as homedir5, tmpdir as tmpdir2 } from "os";
6440
+ import { homedir as homedir6, tmpdir as tmpdir5 } from "os";
6288
6441
  import { randomUUID } from "crypto";
6289
- var TRANSCRIPT_DIR = join18(homedir5(), ".claude", "transcripts");
6442
+ var TRANSCRIPT_DIR = join19(homedir6(), ".claude", "transcripts");
6290
6443
  function getTranscriptPath(sessionId) {
6291
- return join18(TRANSCRIPT_DIR, `${sessionId}.jsonl`);
6444
+ return join19(TRANSCRIPT_DIR, `${sessionId}.jsonl`);
6292
6445
  }
6293
6446
  function ensureTranscriptDir() {
6294
6447
  if (!existsSync15(TRANSCRIPT_DIR)) {
@@ -6378,7 +6531,7 @@ async function buildTranscriptFromSession(client, sessionId, directory, currentT
6378
6531
  }
6379
6532
  };
6380
6533
  entries.push(JSON.stringify(currentEntry));
6381
- const tempPath = join18(tmpdir2(), `opencode-transcript-${sessionId}-${randomUUID()}.jsonl`);
6534
+ const tempPath = join19(tmpdir5(), `opencode-transcript-${sessionId}-${randomUUID()}.jsonl`);
6382
6535
  writeFileSync5(tempPath, entries.join(`
6383
6536
  `) + `
6384
6537
  `);
@@ -6398,7 +6551,7 @@ async function buildTranscriptFromSession(client, sessionId, directory, currentT
6398
6551
  ]
6399
6552
  }
6400
6553
  };
6401
- const tempPath = join18(tmpdir2(), `opencode-transcript-${sessionId}-${randomUUID()}.jsonl`);
6554
+ const tempPath = join19(tmpdir5(), `opencode-transcript-${sessionId}-${randomUUID()}.jsonl`);
6402
6555
  writeFileSync5(tempPath, JSON.stringify(currentEntry) + `
6403
6556
  `);
6404
6557
  return tempPath;
@@ -6610,11 +6763,11 @@ ${USER_PROMPT_SUBMIT_TAG_CLOSE}`);
6610
6763
  }
6611
6764
 
6612
6765
  // src/hooks/claude-code-hooks/todo.ts
6613
- import { join as join19 } from "path";
6614
- import { homedir as homedir6 } from "os";
6615
- var TODO_DIR = join19(homedir6(), ".claude", "todos");
6766
+ import { join as join20 } from "path";
6767
+ import { homedir as homedir7 } from "os";
6768
+ var TODO_DIR = join20(homedir7(), ".claude", "todos");
6616
6769
  function getTodoPath(sessionId) {
6617
- return join19(TODO_DIR, `${sessionId}-agent-${sessionId}.json`);
6770
+ return join20(TODO_DIR, `${sessionId}-agent-${sessionId}.json`);
6618
6771
  }
6619
6772
 
6620
6773
  // src/hooks/claude-code-hooks/stop.ts
@@ -6946,7 +7099,7 @@ ${result.message}`;
6946
7099
  }
6947
7100
  // src/hooks/rules-injector/index.ts
6948
7101
  import { readFileSync as readFileSync9 } from "fs";
6949
- import { homedir as homedir7 } from "os";
7102
+ import { homedir as homedir8 } from "os";
6950
7103
  import { relative as relative3, resolve as resolve4 } from "path";
6951
7104
 
6952
7105
  // src/hooks/rules-injector/finder.ts
@@ -6956,12 +7109,12 @@ import {
6956
7109
  realpathSync,
6957
7110
  statSync as statSync2
6958
7111
  } from "fs";
6959
- import { dirname as dirname4, join as join21, relative } from "path";
7112
+ import { dirname as dirname4, join as join22, relative } from "path";
6960
7113
 
6961
7114
  // src/hooks/rules-injector/constants.ts
6962
- import { join as join20 } from "path";
6963
- var OPENCODE_STORAGE5 = join20(xdgData2 ?? "", "opencode", "storage");
6964
- var RULES_INJECTOR_STORAGE = join20(OPENCODE_STORAGE5, "rules-injector");
7115
+ import { join as join21 } from "path";
7116
+ var OPENCODE_STORAGE5 = join21(xdgData2 ?? "", "opencode", "storage");
7117
+ var RULES_INJECTOR_STORAGE = join21(OPENCODE_STORAGE5, "rules-injector");
6965
7118
  var PROJECT_MARKERS = [
6966
7119
  ".git",
6967
7120
  "pyproject.toml",
@@ -6988,7 +7141,7 @@ function findProjectRoot(startPath) {
6988
7141
  }
6989
7142
  while (true) {
6990
7143
  for (const marker of PROJECT_MARKERS) {
6991
- const markerPath = join21(current, marker);
7144
+ const markerPath = join22(current, marker);
6992
7145
  if (existsSync16(markerPath)) {
6993
7146
  return current;
6994
7147
  }
@@ -7006,7 +7159,7 @@ function findRuleFilesRecursive(dir, results) {
7006
7159
  try {
7007
7160
  const entries = readdirSync4(dir, { withFileTypes: true });
7008
7161
  for (const entry of entries) {
7009
- const fullPath = join21(dir, entry.name);
7162
+ const fullPath = join22(dir, entry.name);
7010
7163
  if (entry.isDirectory()) {
7011
7164
  findRuleFilesRecursive(fullPath, results);
7012
7165
  } else if (entry.isFile()) {
@@ -7032,7 +7185,7 @@ function findRuleFiles(projectRoot, homeDir, currentFile) {
7032
7185
  let distance = 0;
7033
7186
  while (true) {
7034
7187
  for (const [parent, subdir] of PROJECT_RULE_SUBDIRS) {
7035
- const ruleDir = join21(currentDir, parent, subdir);
7188
+ const ruleDir = join22(currentDir, parent, subdir);
7036
7189
  const files = [];
7037
7190
  findRuleFilesRecursive(ruleDir, files);
7038
7191
  for (const filePath of files) {
@@ -7056,7 +7209,7 @@ function findRuleFiles(projectRoot, homeDir, currentFile) {
7056
7209
  currentDir = parentDir;
7057
7210
  distance++;
7058
7211
  }
7059
- const userRuleDir = join21(homeDir, USER_RULE_DIR);
7212
+ const userRuleDir = join22(homeDir, USER_RULE_DIR);
7060
7213
  const userFiles = [];
7061
7214
  findRuleFilesRecursive(userRuleDir, userFiles);
7062
7215
  for (const filePath of userFiles) {
@@ -7251,9 +7404,9 @@ import {
7251
7404
  writeFileSync as writeFileSync6,
7252
7405
  unlinkSync as unlinkSync6
7253
7406
  } from "fs";
7254
- import { join as join22 } from "path";
7407
+ import { join as join23 } from "path";
7255
7408
  function getStoragePath3(sessionID) {
7256
- return join22(RULES_INJECTOR_STORAGE, `${sessionID}.json`);
7409
+ return join23(RULES_INJECTOR_STORAGE, `${sessionID}.json`);
7257
7410
  }
7258
7411
  function loadInjectedRules(sessionID) {
7259
7412
  const filePath = getStoragePath3(sessionID);
@@ -7314,7 +7467,7 @@ function createRulesInjectorHook(ctx) {
7314
7467
  return;
7315
7468
  const projectRoot = findProjectRoot(filePath);
7316
7469
  const cache2 = getSessionCache(input.sessionID);
7317
- const home = homedir7();
7470
+ const home = homedir8();
7318
7471
  const ruleFileCandidates = findRuleFiles(projectRoot, home, filePath);
7319
7472
  const toInject = [];
7320
7473
  for (const candidate of ruleFileCandidates) {
@@ -7692,12 +7845,12 @@ import {
7692
7845
  writeFileSync as writeFileSync8,
7693
7846
  unlinkSync as unlinkSync7
7694
7847
  } from "fs";
7695
- import { join as join27 } from "path";
7848
+ import { join as join28 } from "path";
7696
7849
 
7697
7850
  // src/hooks/agent-usage-reminder/constants.ts
7698
- import { join as join26 } from "path";
7699
- var OPENCODE_STORAGE6 = join26(xdgData2 ?? "", "opencode", "storage");
7700
- var AGENT_USAGE_REMINDER_STORAGE = join26(OPENCODE_STORAGE6, "agent-usage-reminder");
7851
+ import { join as join27 } from "path";
7852
+ var OPENCODE_STORAGE6 = join27(xdgData2 ?? "", "opencode", "storage");
7853
+ var AGENT_USAGE_REMINDER_STORAGE = join27(OPENCODE_STORAGE6, "agent-usage-reminder");
7701
7854
  var TARGET_TOOLS = new Set([
7702
7855
  "grep",
7703
7856
  "safe_grep",
@@ -7742,7 +7895,7 @@ ALWAYS prefer: Multiple parallel background_task calls > Direct tool calls
7742
7895
 
7743
7896
  // src/hooks/agent-usage-reminder/storage.ts
7744
7897
  function getStoragePath4(sessionID) {
7745
- return join27(AGENT_USAGE_REMINDER_STORAGE, `${sessionID}.json`);
7898
+ return join28(AGENT_USAGE_REMINDER_STORAGE, `${sessionID}.json`);
7746
7899
  }
7747
7900
  function loadAgentUsageState(sessionID) {
7748
7901
  const filePath = getStoragePath4(sessionID);
@@ -7991,12 +8144,12 @@ import {
7991
8144
  writeFileSync as writeFileSync9,
7992
8145
  unlinkSync as unlinkSync8
7993
8146
  } from "fs";
7994
- import { join as join29 } from "path";
8147
+ import { join as join30 } from "path";
7995
8148
 
7996
8149
  // src/hooks/interactive-bash-session/constants.ts
7997
- import { join as join28 } from "path";
7998
- var OPENCODE_STORAGE7 = join28(xdgData2 ?? "", "opencode", "storage");
7999
- var INTERACTIVE_BASH_SESSION_STORAGE = join28(OPENCODE_STORAGE7, "interactive-bash-session");
8150
+ import { join as join29 } from "path";
8151
+ var OPENCODE_STORAGE7 = join29(xdgData2 ?? "", "opencode", "storage");
8152
+ var INTERACTIVE_BASH_SESSION_STORAGE = join29(OPENCODE_STORAGE7, "interactive-bash-session");
8000
8153
  var OMO_SESSION_PREFIX = "omo-";
8001
8154
  function buildSessionReminderMessage(sessions) {
8002
8155
  if (sessions.length === 0)
@@ -8008,7 +8161,7 @@ function buildSessionReminderMessage(sessions) {
8008
8161
 
8009
8162
  // src/hooks/interactive-bash-session/storage.ts
8010
8163
  function getStoragePath5(sessionID) {
8011
- return join29(INTERACTIVE_BASH_SESSION_STORAGE, `${sessionID}.json`);
8164
+ return join30(INTERACTIVE_BASH_SESSION_STORAGE, `${sessionID}.json`);
8012
8165
  }
8013
8166
  function loadInteractiveBashSessionState(sessionID) {
8014
8167
  const filePath = getStoragePath5(sessionID);
@@ -9782,8 +9935,8 @@ async function createGoogleAntigravityAuthPlugin({
9782
9935
  }
9783
9936
  // src/features/claude-code-command-loader/loader.ts
9784
9937
  import { existsSync as existsSync22, readdirSync as readdirSync5, readFileSync as readFileSync14 } from "fs";
9785
- import { homedir as homedir9 } from "os";
9786
- import { join as join30, basename } from "path";
9938
+ import { homedir as homedir10 } from "os";
9939
+ import { join as join31, basename } from "path";
9787
9940
  function loadCommandsFromDir(commandsDir, scope) {
9788
9941
  if (!existsSync22(commandsDir)) {
9789
9942
  return [];
@@ -9793,7 +9946,7 @@ function loadCommandsFromDir(commandsDir, scope) {
9793
9946
  for (const entry of entries) {
9794
9947
  if (!isMarkdownFile(entry))
9795
9948
  continue;
9796
- const commandPath = join30(commandsDir, entry.name);
9949
+ const commandPath = join31(commandsDir, entry.name);
9797
9950
  const commandName = basename(entry.name, ".md");
9798
9951
  try {
9799
9952
  const content = readFileSync14(commandPath, "utf-8");
@@ -9836,29 +9989,29 @@ function commandsToRecord(commands) {
9836
9989
  return result;
9837
9990
  }
9838
9991
  function loadUserCommands() {
9839
- const userCommandsDir = join30(homedir9(), ".claude", "commands");
9992
+ const userCommandsDir = join31(homedir10(), ".claude", "commands");
9840
9993
  const commands = loadCommandsFromDir(userCommandsDir, "user");
9841
9994
  return commandsToRecord(commands);
9842
9995
  }
9843
9996
  function loadProjectCommands() {
9844
- const projectCommandsDir = join30(process.cwd(), ".claude", "commands");
9997
+ const projectCommandsDir = join31(process.cwd(), ".claude", "commands");
9845
9998
  const commands = loadCommandsFromDir(projectCommandsDir, "project");
9846
9999
  return commandsToRecord(commands);
9847
10000
  }
9848
10001
  function loadOpencodeGlobalCommands() {
9849
- const opencodeCommandsDir = join30(homedir9(), ".config", "opencode", "command");
10002
+ const opencodeCommandsDir = join31(homedir10(), ".config", "opencode", "command");
9850
10003
  const commands = loadCommandsFromDir(opencodeCommandsDir, "opencode");
9851
10004
  return commandsToRecord(commands);
9852
10005
  }
9853
10006
  function loadOpencodeProjectCommands() {
9854
- const opencodeProjectDir = join30(process.cwd(), ".opencode", "command");
10007
+ const opencodeProjectDir = join31(process.cwd(), ".opencode", "command");
9855
10008
  const commands = loadCommandsFromDir(opencodeProjectDir, "opencode-project");
9856
10009
  return commandsToRecord(commands);
9857
10010
  }
9858
10011
  // src/features/claude-code-skill-loader/loader.ts
9859
10012
  import { existsSync as existsSync23, readdirSync as readdirSync6, readFileSync as readFileSync15 } from "fs";
9860
- import { homedir as homedir10 } from "os";
9861
- import { join as join31 } from "path";
10013
+ import { homedir as homedir11 } from "os";
10014
+ import { join as join32 } from "path";
9862
10015
  function loadSkillsFromDir(skillsDir, scope) {
9863
10016
  if (!existsSync23(skillsDir)) {
9864
10017
  return [];
@@ -9868,11 +10021,11 @@ function loadSkillsFromDir(skillsDir, scope) {
9868
10021
  for (const entry of entries) {
9869
10022
  if (entry.name.startsWith("."))
9870
10023
  continue;
9871
- const skillPath = join31(skillsDir, entry.name);
10024
+ const skillPath = join32(skillsDir, entry.name);
9872
10025
  if (!entry.isDirectory() && !entry.isSymbolicLink())
9873
10026
  continue;
9874
10027
  const resolvedPath = resolveSymlink(skillPath);
9875
- const skillMdPath = join31(resolvedPath, "SKILL.md");
10028
+ const skillMdPath = join32(resolvedPath, "SKILL.md");
9876
10029
  if (!existsSync23(skillMdPath))
9877
10030
  continue;
9878
10031
  try {
@@ -9907,7 +10060,7 @@ $ARGUMENTS
9907
10060
  return skills;
9908
10061
  }
9909
10062
  function loadUserSkillsAsCommands() {
9910
- const userSkillsDir = join31(homedir10(), ".claude", "skills");
10063
+ const userSkillsDir = join32(homedir11(), ".claude", "skills");
9911
10064
  const skills = loadSkillsFromDir(userSkillsDir, "user");
9912
10065
  return skills.reduce((acc, skill) => {
9913
10066
  acc[skill.name] = skill.definition;
@@ -9915,7 +10068,7 @@ function loadUserSkillsAsCommands() {
9915
10068
  }, {});
9916
10069
  }
9917
10070
  function loadProjectSkillsAsCommands() {
9918
- const projectSkillsDir = join31(process.cwd(), ".claude", "skills");
10071
+ const projectSkillsDir = join32(process.cwd(), ".claude", "skills");
9919
10072
  const skills = loadSkillsFromDir(projectSkillsDir, "project");
9920
10073
  return skills.reduce((acc, skill) => {
9921
10074
  acc[skill.name] = skill.definition;
@@ -9924,8 +10077,8 @@ function loadProjectSkillsAsCommands() {
9924
10077
  }
9925
10078
  // src/features/claude-code-agent-loader/loader.ts
9926
10079
  import { existsSync as existsSync24, readdirSync as readdirSync7, readFileSync as readFileSync16 } from "fs";
9927
- import { homedir as homedir11 } from "os";
9928
- import { join as join32, basename as basename2 } from "path";
10080
+ import { homedir as homedir12 } from "os";
10081
+ import { join as join33, basename as basename2 } from "path";
9929
10082
  function parseToolsConfig(toolsStr) {
9930
10083
  if (!toolsStr)
9931
10084
  return;
@@ -9947,7 +10100,7 @@ function loadAgentsFromDir(agentsDir, scope) {
9947
10100
  for (const entry of entries) {
9948
10101
  if (!isMarkdownFile(entry))
9949
10102
  continue;
9950
- const agentPath = join32(agentsDir, entry.name);
10103
+ const agentPath = join33(agentsDir, entry.name);
9951
10104
  const agentName = basename2(entry.name, ".md");
9952
10105
  try {
9953
10106
  const content = readFileSync16(agentPath, "utf-8");
@@ -9977,7 +10130,7 @@ function loadAgentsFromDir(agentsDir, scope) {
9977
10130
  return agents;
9978
10131
  }
9979
10132
  function loadUserAgents() {
9980
- const userAgentsDir = join32(homedir11(), ".claude", "agents");
10133
+ const userAgentsDir = join33(homedir12(), ".claude", "agents");
9981
10134
  const agents = loadAgentsFromDir(userAgentsDir, "user");
9982
10135
  const result = {};
9983
10136
  for (const agent of agents) {
@@ -9986,7 +10139,7 @@ function loadUserAgents() {
9986
10139
  return result;
9987
10140
  }
9988
10141
  function loadProjectAgents() {
9989
- const projectAgentsDir = join32(process.cwd(), ".claude", "agents");
10142
+ const projectAgentsDir = join33(process.cwd(), ".claude", "agents");
9990
10143
  const agents = loadAgentsFromDir(projectAgentsDir, "project");
9991
10144
  const result = {};
9992
10145
  for (const agent of agents) {
@@ -9996,8 +10149,8 @@ function loadProjectAgents() {
9996
10149
  }
9997
10150
  // src/features/claude-code-mcp-loader/loader.ts
9998
10151
  import { existsSync as existsSync25 } from "fs";
9999
- import { homedir as homedir12 } from "os";
10000
- import { join as join33 } from "path";
10152
+ import { homedir as homedir13 } from "os";
10153
+ import { join as join34 } from "path";
10001
10154
 
10002
10155
  // src/features/claude-code-mcp-loader/env-expander.ts
10003
10156
  function expandEnvVars(value) {
@@ -10063,12 +10216,12 @@ function transformMcpServer(name, server) {
10063
10216
 
10064
10217
  // src/features/claude-code-mcp-loader/loader.ts
10065
10218
  function getMcpConfigPaths() {
10066
- const home = homedir12();
10219
+ const home = homedir13();
10067
10220
  const cwd = process.cwd();
10068
10221
  return [
10069
- { path: join33(home, ".claude", ".mcp.json"), scope: "user" },
10070
- { path: join33(cwd, ".mcp.json"), scope: "project" },
10071
- { path: join33(cwd, ".claude", ".mcp.json"), scope: "local" }
10222
+ { path: join34(home, ".claude", ".mcp.json"), scope: "user" },
10223
+ { path: join34(cwd, ".mcp.json"), scope: "project" },
10224
+ { path: join34(cwd, ".claude", ".mcp.json"), scope: "local" }
10072
10225
  ];
10073
10226
  }
10074
10227
  async function loadMcpConfigFile(filePath) {
@@ -10359,8 +10512,8 @@ var EXT_TO_LANG = {
10359
10512
  };
10360
10513
  // src/tools/lsp/config.ts
10361
10514
  import { existsSync as existsSync26, readFileSync as readFileSync17 } from "fs";
10362
- import { join as join34 } from "path";
10363
- import { homedir as homedir13 } from "os";
10515
+ import { join as join35 } from "path";
10516
+ import { homedir as homedir14 } from "os";
10364
10517
  function loadJsonFile(path6) {
10365
10518
  if (!existsSync26(path6))
10366
10519
  return null;
@@ -10373,9 +10526,9 @@ function loadJsonFile(path6) {
10373
10526
  function getConfigPaths2() {
10374
10527
  const cwd = process.cwd();
10375
10528
  return {
10376
- project: join34(cwd, ".opencode", "oh-my-opencode.json"),
10377
- user: join34(homedir13(), ".config", "opencode", "oh-my-opencode.json"),
10378
- opencode: join34(homedir13(), ".config", "opencode", "opencode.json")
10529
+ project: join35(cwd, ".opencode", "oh-my-opencode.json"),
10530
+ user: join35(homedir14(), ".config", "opencode", "oh-my-opencode.json"),
10531
+ opencode: join35(homedir14(), ".config", "opencode", "opencode.json")
10379
10532
  };
10380
10533
  }
10381
10534
  function loadAllConfigs() {
@@ -10468,7 +10621,7 @@ function isServerInstalled(command) {
10468
10621
  const pathEnv = process.env.PATH || "";
10469
10622
  const paths = pathEnv.split(":");
10470
10623
  for (const p of paths) {
10471
- if (existsSync26(join34(p, cmd))) {
10624
+ if (existsSync26(join35(p, cmd))) {
10472
10625
  return true;
10473
10626
  }
10474
10627
  }
@@ -23967,14 +24120,14 @@ var lsp_code_action_resolve = tool({
23967
24120
  });
23968
24121
  // src/tools/ast-grep/constants.ts
23969
24122
  import { createRequire as createRequire4 } from "module";
23970
- import { dirname as dirname6, join as join36 } from "path";
24123
+ import { dirname as dirname6, join as join37 } from "path";
23971
24124
  import { existsSync as existsSync29, statSync as statSync4 } from "fs";
23972
24125
 
23973
24126
  // src/tools/ast-grep/downloader.ts
23974
24127
  var {spawn: spawn5 } = globalThis.Bun;
23975
24128
  import { existsSync as existsSync28, mkdirSync as mkdirSync10, chmodSync as chmodSync2, unlinkSync as unlinkSync9 } from "fs";
23976
- import { join as join35 } from "path";
23977
- import { homedir as homedir14 } from "os";
24129
+ import { join as join36 } from "path";
24130
+ import { homedir as homedir15 } from "os";
23978
24131
  import { createRequire as createRequire3 } from "module";
23979
24132
  var REPO2 = "ast-grep/ast-grep";
23980
24133
  var DEFAULT_VERSION = "0.40.0";
@@ -23999,18 +24152,18 @@ var PLATFORM_MAP2 = {
23999
24152
  function getCacheDir3() {
24000
24153
  if (process.platform === "win32") {
24001
24154
  const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
24002
- const base2 = localAppData || join35(homedir14(), "AppData", "Local");
24003
- return join35(base2, "oh-my-opencode", "bin");
24155
+ const base2 = localAppData || join36(homedir15(), "AppData", "Local");
24156
+ return join36(base2, "oh-my-opencode", "bin");
24004
24157
  }
24005
24158
  const xdgCache2 = process.env.XDG_CACHE_HOME;
24006
- const base = xdgCache2 || join35(homedir14(), ".cache");
24007
- return join35(base, "oh-my-opencode", "bin");
24159
+ const base = xdgCache2 || join36(homedir15(), ".cache");
24160
+ return join36(base, "oh-my-opencode", "bin");
24008
24161
  }
24009
24162
  function getBinaryName3() {
24010
24163
  return process.platform === "win32" ? "sg.exe" : "sg";
24011
24164
  }
24012
24165
  function getCachedBinaryPath2() {
24013
- const binaryPath = join35(getCacheDir3(), getBinaryName3());
24166
+ const binaryPath = join36(getCacheDir3(), getBinaryName3());
24014
24167
  return existsSync28(binaryPath) ? binaryPath : null;
24015
24168
  }
24016
24169
  async function extractZip2(archivePath, destDir) {
@@ -24037,7 +24190,7 @@ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
24037
24190
  }
24038
24191
  const cacheDir = getCacheDir3();
24039
24192
  const binaryName = getBinaryName3();
24040
- const binaryPath = join35(cacheDir, binaryName);
24193
+ const binaryPath = join36(cacheDir, binaryName);
24041
24194
  if (existsSync28(binaryPath)) {
24042
24195
  return binaryPath;
24043
24196
  }
@@ -24053,7 +24206,7 @@ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
24053
24206
  if (!response2.ok) {
24054
24207
  throw new Error(`HTTP ${response2.status}: ${response2.statusText}`);
24055
24208
  }
24056
- const archivePath = join35(cacheDir, assetName);
24209
+ const archivePath = join36(cacheDir, assetName);
24057
24210
  const arrayBuffer = await response2.arrayBuffer();
24058
24211
  await Bun.write(archivePath, arrayBuffer);
24059
24212
  await extractZip2(archivePath, cacheDir);
@@ -24111,7 +24264,7 @@ function findSgCliPathSync() {
24111
24264
  const require2 = createRequire4(import.meta.url);
24112
24265
  const cliPkgPath = require2.resolve("@ast-grep/cli/package.json");
24113
24266
  const cliDir = dirname6(cliPkgPath);
24114
- const sgPath = join36(cliDir, binaryName);
24267
+ const sgPath = join37(cliDir, binaryName);
24115
24268
  if (existsSync29(sgPath) && isValidBinary(sgPath)) {
24116
24269
  return sgPath;
24117
24270
  }
@@ -24123,7 +24276,7 @@ function findSgCliPathSync() {
24123
24276
  const pkgPath = require2.resolve(`${platformPkg}/package.json`);
24124
24277
  const pkgDir = dirname6(pkgPath);
24125
24278
  const astGrepName = process.platform === "win32" ? "ast-grep.exe" : "ast-grep";
24126
- const binaryPath = join36(pkgDir, astGrepName);
24279
+ const binaryPath = join37(pkgDir, astGrepName);
24127
24280
  if (existsSync29(binaryPath) && isValidBinary(binaryPath)) {
24128
24281
  return binaryPath;
24129
24282
  }
@@ -24499,19 +24652,19 @@ var {spawn: spawn7 } = globalThis.Bun;
24499
24652
 
24500
24653
  // src/tools/grep/constants.ts
24501
24654
  import { existsSync as existsSync32 } from "fs";
24502
- import { join as join38, dirname as dirname7 } from "path";
24655
+ import { join as join39, dirname as dirname7 } from "path";
24503
24656
  import { spawnSync } from "child_process";
24504
24657
 
24505
24658
  // src/tools/grep/downloader.ts
24506
24659
  import { existsSync as existsSync31, mkdirSync as mkdirSync11, chmodSync as chmodSync3, unlinkSync as unlinkSync10, readdirSync as readdirSync8 } from "fs";
24507
- import { join as join37 } from "path";
24660
+ import { join as join38 } from "path";
24508
24661
  function getInstallDir() {
24509
24662
  const homeDir = process.env.HOME || process.env.USERPROFILE || ".";
24510
- return join37(homeDir, ".cache", "oh-my-opencode", "bin");
24663
+ return join38(homeDir, ".cache", "oh-my-opencode", "bin");
24511
24664
  }
24512
24665
  function getRgPath() {
24513
- const isWindows = process.platform === "win32";
24514
- return join37(getInstallDir(), isWindows ? "rg.exe" : "rg");
24666
+ const isWindows2 = process.platform === "win32";
24667
+ return join38(getInstallDir(), isWindows2 ? "rg.exe" : "rg");
24515
24668
  }
24516
24669
  function getInstalledRipgrepPath() {
24517
24670
  const rgPath = getRgPath();
@@ -24521,8 +24674,8 @@ function getInstalledRipgrepPath() {
24521
24674
  // src/tools/grep/constants.ts
24522
24675
  var cachedCli = null;
24523
24676
  function findExecutable(name) {
24524
- const isWindows = process.platform === "win32";
24525
- const cmd = isWindows ? "where" : "which";
24677
+ const isWindows2 = process.platform === "win32";
24678
+ const cmd = isWindows2 ? "where" : "which";
24526
24679
  try {
24527
24680
  const result = spawnSync(cmd, [name], { encoding: "utf-8", timeout: 5000 });
24528
24681
  if (result.status === 0 && result.stdout.trim()) {
@@ -24535,13 +24688,13 @@ function findExecutable(name) {
24535
24688
  function getOpenCodeBundledRg() {
24536
24689
  const execPath = process.execPath;
24537
24690
  const execDir = dirname7(execPath);
24538
- const isWindows = process.platform === "win32";
24539
- const rgName = isWindows ? "rg.exe" : "rg";
24691
+ const isWindows2 = process.platform === "win32";
24692
+ const rgName = isWindows2 ? "rg.exe" : "rg";
24540
24693
  const candidates = [
24541
- join38(execDir, rgName),
24542
- join38(execDir, "bin", rgName),
24543
- join38(execDir, "..", "bin", rgName),
24544
- join38(execDir, "..", "libexec", rgName)
24694
+ join39(execDir, rgName),
24695
+ join39(execDir, "bin", rgName),
24696
+ join39(execDir, "..", "bin", rgName),
24697
+ join39(execDir, "..", "libexec", rgName)
24545
24698
  ];
24546
24699
  for (const candidate of candidates) {
24547
24700
  if (existsSync32(candidate)) {
@@ -24948,8 +25101,8 @@ var glob = tool({
24948
25101
  });
24949
25102
  // src/tools/slashcommand/tools.ts
24950
25103
  import { existsSync as existsSync33, readdirSync as readdirSync9, readFileSync as readFileSync20 } from "fs";
24951
- import { homedir as homedir15 } from "os";
24952
- import { join as join39, basename as basename3, dirname as dirname8 } from "path";
25104
+ import { homedir as homedir16 } from "os";
25105
+ import { join as join40, basename as basename3, dirname as dirname8 } from "path";
24953
25106
  function discoverCommandsFromDir(commandsDir, scope) {
24954
25107
  if (!existsSync33(commandsDir)) {
24955
25108
  return [];
@@ -24959,7 +25112,7 @@ function discoverCommandsFromDir(commandsDir, scope) {
24959
25112
  for (const entry of entries) {
24960
25113
  if (!isMarkdownFile(entry))
24961
25114
  continue;
24962
- const commandPath = join39(commandsDir, entry.name);
25115
+ const commandPath = join40(commandsDir, entry.name);
24963
25116
  const commandName = basename3(entry.name, ".md");
24964
25117
  try {
24965
25118
  const content = readFileSync20(commandPath, "utf-8");
@@ -24987,10 +25140,10 @@ function discoverCommandsFromDir(commandsDir, scope) {
24987
25140
  return commands;
24988
25141
  }
24989
25142
  function discoverCommandsSync() {
24990
- const userCommandsDir = join39(homedir15(), ".claude", "commands");
24991
- const projectCommandsDir = join39(process.cwd(), ".claude", "commands");
24992
- const opencodeGlobalDir = join39(homedir15(), ".config", "opencode", "command");
24993
- const opencodeProjectDir = join39(process.cwd(), ".opencode", "command");
25143
+ const userCommandsDir = join40(homedir16(), ".claude", "commands");
25144
+ const projectCommandsDir = join40(process.cwd(), ".claude", "commands");
25145
+ const opencodeGlobalDir = join40(homedir16(), ".config", "opencode", "command");
25146
+ const opencodeProjectDir = join40(process.cwd(), ".opencode", "command");
24994
25147
  const userCommands = discoverCommandsFromDir(userCommandsDir, "user");
24995
25148
  const opencodeGlobalCommands = discoverCommandsFromDir(opencodeGlobalDir, "opencode");
24996
25149
  const projectCommands = discoverCommandsFromDir(projectCommandsDir, "project");
@@ -25123,8 +25276,8 @@ var SkillFrontmatterSchema = exports_external.object({
25123
25276
  });
25124
25277
  // src/tools/skill/tools.ts
25125
25278
  import { existsSync as existsSync34, readdirSync as readdirSync10, readFileSync as readFileSync21 } from "fs";
25126
- import { homedir as homedir16 } from "os";
25127
- import { join as join40, basename as basename4 } from "path";
25279
+ import { homedir as homedir17 } from "os";
25280
+ import { join as join41, basename as basename4 } from "path";
25128
25281
  function parseSkillFrontmatter(data) {
25129
25282
  return {
25130
25283
  name: typeof data.name === "string" ? data.name : "",
@@ -25143,10 +25296,10 @@ function discoverSkillsFromDir(skillsDir, scope) {
25143
25296
  for (const entry of entries) {
25144
25297
  if (entry.name.startsWith("."))
25145
25298
  continue;
25146
- const skillPath = join40(skillsDir, entry.name);
25299
+ const skillPath = join41(skillsDir, entry.name);
25147
25300
  if (entry.isDirectory() || entry.isSymbolicLink()) {
25148
25301
  const resolvedPath = resolveSymlink(skillPath);
25149
- const skillMdPath = join40(resolvedPath, "SKILL.md");
25302
+ const skillMdPath = join41(resolvedPath, "SKILL.md");
25150
25303
  if (!existsSync34(skillMdPath))
25151
25304
  continue;
25152
25305
  try {
@@ -25165,8 +25318,8 @@ function discoverSkillsFromDir(skillsDir, scope) {
25165
25318
  return skills;
25166
25319
  }
25167
25320
  function discoverSkillsSync() {
25168
- const userSkillsDir = join40(homedir16(), ".claude", "skills");
25169
- const projectSkillsDir = join40(process.cwd(), ".claude", "skills");
25321
+ const userSkillsDir = join41(homedir17(), ".claude", "skills");
25322
+ const projectSkillsDir = join41(process.cwd(), ".claude", "skills");
25170
25323
  const userSkills = discoverSkillsFromDir(userSkillsDir, "user");
25171
25324
  const projectSkills = discoverSkillsFromDir(projectSkillsDir, "project");
25172
25325
  return [...projectSkills, ...userSkills];
@@ -25176,7 +25329,7 @@ var skillListForDescription = availableSkills.map((s) => `- ${s.name}: ${s.descr
25176
25329
  `);
25177
25330
  async function parseSkillMd(skillPath) {
25178
25331
  const resolvedPath = resolveSymlink(skillPath);
25179
- const skillMdPath = join40(resolvedPath, "SKILL.md");
25332
+ const skillMdPath = join41(resolvedPath, "SKILL.md");
25180
25333
  if (!existsSync34(skillMdPath)) {
25181
25334
  return null;
25182
25335
  }
@@ -25192,9 +25345,9 @@ async function parseSkillMd(skillPath) {
25192
25345
  allowedTools: frontmatter2["allowed-tools"],
25193
25346
  metadata: frontmatter2.metadata
25194
25347
  };
25195
- const referencesDir = join40(resolvedPath, "references");
25196
- const scriptsDir = join40(resolvedPath, "scripts");
25197
- const assetsDir = join40(resolvedPath, "assets");
25348
+ const referencesDir = join41(resolvedPath, "references");
25349
+ const scriptsDir = join41(resolvedPath, "scripts");
25350
+ const assetsDir = join41(resolvedPath, "assets");
25198
25351
  const references = existsSync34(referencesDir) ? readdirSync10(referencesDir).filter((f) => !f.startsWith(".")) : [];
25199
25352
  const scripts = existsSync34(scriptsDir) ? readdirSync10(scriptsDir).filter((f) => !f.startsWith(".") && !f.startsWith("__")) : [];
25200
25353
  const assets = existsSync34(assetsDir) ? readdirSync10(assetsDir).filter((f) => !f.startsWith(".")) : [];
@@ -25221,7 +25374,7 @@ async function discoverSkillsFromDirAsync(skillsDir) {
25221
25374
  for (const entry of entries) {
25222
25375
  if (entry.name.startsWith("."))
25223
25376
  continue;
25224
- const skillPath = join40(skillsDir, entry.name);
25377
+ const skillPath = join41(skillsDir, entry.name);
25225
25378
  if (entry.isDirectory() || entry.isSymbolicLink()) {
25226
25379
  const skillInfo = await parseSkillMd(skillPath);
25227
25380
  if (skillInfo) {
@@ -25232,8 +25385,8 @@ async function discoverSkillsFromDirAsync(skillsDir) {
25232
25385
  return skills;
25233
25386
  }
25234
25387
  async function discoverSkills() {
25235
- const userSkillsDir = join40(homedir16(), ".claude", "skills");
25236
- const projectSkillsDir = join40(process.cwd(), ".claude", "skills");
25388
+ const userSkillsDir = join41(homedir17(), ".claude", "skills");
25389
+ const projectSkillsDir = join41(process.cwd(), ".claude", "skills");
25237
25390
  const userSkills = await discoverSkillsFromDirAsync(userSkillsDir);
25238
25391
  const projectSkills = await discoverSkillsFromDirAsync(projectSkillsDir);
25239
25392
  return [...projectSkills, ...userSkills];
@@ -25262,7 +25415,7 @@ async function loadSkillWithReferences(skill, includeRefs) {
25262
25415
  const referencesLoaded = [];
25263
25416
  if (includeRefs && skill.references.length > 0) {
25264
25417
  for (const ref of skill.references) {
25265
- const refPath = join40(skill.path, "references", ref);
25418
+ const refPath = join41(skill.path, "references", ref);
25266
25419
  try {
25267
25420
  let content = readFileSync21(refPath, "utf-8");
25268
25421
  content = await resolveCommandsInText(content);
@@ -25355,17 +25508,33 @@ Try a different skill name.`;
25355
25508
  });
25356
25509
  // src/tools/interactive-bash/constants.ts
25357
25510
  var DEFAULT_TIMEOUT_MS4 = 60000;
25511
+ var BLOCKED_TMUX_SUBCOMMANDS = [
25512
+ "capture-pane",
25513
+ "capturep",
25514
+ "save-buffer",
25515
+ "saveb",
25516
+ "show-buffer",
25517
+ "showb",
25518
+ "pipe-pane",
25519
+ "pipep"
25520
+ ];
25358
25521
  var INTERACTIVE_BASH_DESCRIPTION = `Execute tmux commands for interactive terminal session management.
25359
25522
 
25360
- Use session names following the pattern "omo-{name}" for automatic tracking.`;
25523
+ Use session names following the pattern "omo-{name}" for automatic tracking.
25524
+
25525
+ BLOCKED COMMANDS (use bash tool instead):
25526
+ - capture-pane / capturep: Use bash to read output files or pipe output
25527
+ - save-buffer / saveb: Use bash to save content to files
25528
+ - show-buffer / showb: Use bash to read buffer content
25529
+ - pipe-pane / pipep: Use bash for piping output`;
25361
25530
 
25362
25531
  // src/tools/interactive-bash/utils.ts
25363
25532
  var {spawn: spawn9 } = globalThis.Bun;
25364
25533
  var tmuxPath = null;
25365
25534
  var initPromise3 = null;
25366
25535
  async function findTmuxPath() {
25367
- const isWindows = process.platform === "win32";
25368
- const cmd = isWindows ? "where" : "which";
25536
+ const isWindows2 = process.platform === "win32";
25537
+ const cmd = isWindows2 ? "where" : "which";
25369
25538
  try {
25370
25539
  const proc = spawn9([cmd, "tmux"], {
25371
25540
  stdout: "pipe",
@@ -25461,6 +25630,10 @@ var interactive_bash = tool({
25461
25630
  if (parts.length === 0) {
25462
25631
  return "Error: Empty tmux command";
25463
25632
  }
25633
+ const subcommand = parts[0].toLowerCase();
25634
+ if (BLOCKED_TMUX_SUBCOMMANDS.includes(subcommand)) {
25635
+ return `Error: '${parts[0]}' is blocked. Use bash tool instead for capturing/printing terminal output.`;
25636
+ }
25464
25637
  const proc = Bun.spawn([tmuxPath2, ...parts], {
25465
25638
  stdout: "pipe",
25466
25639
  stderr: "pipe"
@@ -26041,15 +26214,15 @@ var builtinTools = {
26041
26214
  };
26042
26215
  // src/features/background-agent/manager.ts
26043
26216
  import { existsSync as existsSync35, readdirSync as readdirSync11 } from "fs";
26044
- import { join as join41 } from "path";
26217
+ import { join as join42 } from "path";
26045
26218
  function getMessageDir3(sessionID) {
26046
26219
  if (!existsSync35(MESSAGE_STORAGE))
26047
26220
  return null;
26048
- const directPath = join41(MESSAGE_STORAGE, sessionID);
26221
+ const directPath = join42(MESSAGE_STORAGE, sessionID);
26049
26222
  if (existsSync35(directPath))
26050
26223
  return directPath;
26051
26224
  for (const dir of readdirSync11(MESSAGE_STORAGE)) {
26052
- const sessionPath = join41(MESSAGE_STORAGE, dir, sessionID);
26225
+ const sessionPath = join42(MESSAGE_STORAGE, dir, sessionID);
26053
26226
  if (existsSync35(sessionPath))
26054
26227
  return sessionPath;
26055
26228
  }
@@ -26106,7 +26279,6 @@ class BackgroundManager {
26106
26279
  agent: input.agent,
26107
26280
  tools: {
26108
26281
  task: false,
26109
- call_omo_agent: false,
26110
26282
  background_task: false
26111
26283
  },
26112
26284
  parts: [{ type: "text", text: input.prompt }]
@@ -26149,6 +26321,20 @@ class BackgroundManager {
26149
26321
  }
26150
26322
  return;
26151
26323
  }
26324
+ async checkSessionTodos(sessionID) {
26325
+ try {
26326
+ const response2 = await this.client.session.todo({
26327
+ path: { id: sessionID }
26328
+ });
26329
+ const todos = response2.data ?? response2;
26330
+ if (!todos || todos.length === 0)
26331
+ return false;
26332
+ const incomplete = todos.filter((t) => t.status !== "completed" && t.status !== "cancelled");
26333
+ return incomplete.length > 0;
26334
+ } catch {
26335
+ return false;
26336
+ }
26337
+ }
26152
26338
  handleEvent(event) {
26153
26339
  const props = event.properties;
26154
26340
  if (event.type === "message.part.updated") {
@@ -26180,11 +26366,17 @@ class BackgroundManager {
26180
26366
  const task = this.findBySession(sessionID);
26181
26367
  if (!task || task.status !== "running")
26182
26368
  return;
26183
- task.status = "completed";
26184
- task.completedAt = new Date;
26185
- this.markForNotification(task);
26186
- this.notifyParentSession(task);
26187
- log("[background-agent] Task completed via session.idle event:", task.id);
26369
+ this.checkSessionTodos(sessionID).then((hasIncompleteTodos2) => {
26370
+ if (hasIncompleteTodos2) {
26371
+ log("[background-agent] Task has incomplete todos, waiting for todo-continuation:", task.id);
26372
+ return;
26373
+ }
26374
+ task.status = "completed";
26375
+ task.completedAt = new Date;
26376
+ this.markForNotification(task);
26377
+ this.notifyParentSession(task);
26378
+ log("[background-agent] Task completed via session.idle event:", task.id);
26379
+ });
26188
26380
  }
26189
26381
  if (event.type === "session.deleted") {
26190
26382
  const info = props?.info;
@@ -26304,6 +26496,11 @@ class BackgroundManager {
26304
26496
  continue;
26305
26497
  }
26306
26498
  if (sessionStatus.type === "idle") {
26499
+ const hasIncompleteTodos2 = await this.checkSessionTodos(task.sessionID);
26500
+ if (hasIncompleteTodos2) {
26501
+ log("[background-agent] Task has incomplete todos via polling, waiting:", task.id);
26502
+ continue;
26503
+ }
26307
26504
  task.status = "completed";
26308
26505
  task.completedAt = new Date;
26309
26506
  this.markForNotification(task);