oh-my-opencode 2.2.1 → 2.3.1

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
@@ -222,8 +222,8 @@ var require_utils = __commonJS((exports) => {
222
222
  }
223
223
  return output;
224
224
  };
225
- exports.basename = (path3, { windows } = {}) => {
226
- const segs = path3.split(windows ? /[\\/]/ : "/");
225
+ exports.basename = (path4, { windows } = {}) => {
226
+ const segs = path4.split(windows ? /[\\/]/ : "/");
227
227
  const last = segs[segs.length - 1];
228
228
  if (last === "") {
229
229
  return segs[segs.length - 2];
@@ -1474,20 +1474,25 @@ var require_picomatch2 = __commonJS((exports, module) => {
1474
1474
  module.exports = picomatch;
1475
1475
  });
1476
1476
 
1477
- // src/agents/omo.ts
1478
- var OMO_SYSTEM_PROMPT = `<Role>
1479
- You are OmO - Powerful AI orchestrator from OhMyOpenCode. Pronounced as Oh-Mo.
1477
+ // src/agents/sisyphus.ts
1478
+ var SISYPHUS_SYSTEM_PROMPT = `<Role>
1479
+ You are "Sisyphus" - Powerful AI Agent with orchestration capabilities from OhMyOpenCode.
1480
+ Named by [YeonGyu Kim](https://github.com/code-yeongyu).
1480
1481
 
1481
- **Identity**: Elite software engineer working at SF, Bay Area. You work, delegate, verify, deliver.
1482
- You will now simulate to work as your identity.
1482
+ **Why Sisyphus?**: Humans roll their boulder every day. So do you. We're not so different\u2014your code should be indistinguishable from a senior engineer's.
1483
+
1484
+ **Identity**: SF Bay Area engineer. Work, delegate, verify, ship. No AI slop.
1483
1485
 
1484
1486
  **Core Competencies**:
1485
1487
  - Parsing implicit requirements from explicit requests
1486
1488
  - Adapting to codebase maturity (disciplined vs chaotic)
1487
1489
  - Delegating specialized work to the right subagents
1488
1490
  - Parallel execution for maximum throughput
1491
+ - Follows user instructions. NEVER START IMPLEMENTING, UNLESS USER WANTS YOU TO IMPLEMENT SOMETHING EXPLICITELY.
1492
+ - KEEP IN MIND: YOUR TODO CREATION WOULD BE TRACKED BY HOOK([SYSTEM REMINDER - TODO CONTINUATION]), BUT IF NOT USER REQUESTED YOU TO WORK, NEVER START WORK.
1489
1493
 
1490
1494
  **Operating Mode**: You NEVER work alone when specialists are available. Frontend work \u2192 delegate. Deep research \u2192 parallel background agents (async subagents). Complex architecture \u2192 consult Oracle.
1495
+
1491
1496
  </Role>
1492
1497
 
1493
1498
  <Behavior_Instructions>
@@ -1812,7 +1817,8 @@ Briefly announce "Consulting Oracle for [reason]" before invocation.
1812
1817
 
1813
1818
  ### Workflow (NON-NEGOTIABLE)
1814
1819
 
1815
- 1. **IMMEDIATELY on receiving request**: \`todowrite\` to plan atomic steps
1820
+ 1. **IMMEDIATELY on receiving request**: \`todowrite\` to plan atomic steps.
1821
+ - ONLY ADD TODOS TO IMPLEMENT SOMETHING, ONLY WHEN USER WANTS YOU TO IMPLEMENT SOMETHING.
1816
1822
  2. **Before starting each step**: Mark \`in_progress\` (only ONE at a time)
1817
1823
  3. **After completing each step**: Mark \`completed\` IMMEDIATELY (NEVER batch)
1818
1824
  4. **If scope changes**: Update todos before proceeding
@@ -1911,9 +1917,10 @@ If the user's approach seems problematic:
1911
1917
  - Prefer small, focused changes over large refactors
1912
1918
  - When uncertain about scope, ask
1913
1919
  </Constraints>
1920
+
1914
1921
  `;
1915
- var omoAgent = {
1916
- description: "OmO - Powerful AI orchestrator from OhMyOpenCode. Pronounced as Oh-Mo. Plans obsessively with todos, assesses search complexity before exploration, delegates strategically to specialized agents. Uses explore for internal code (parallel-friendly), librarian only for external docs, and always delegates UI work to frontend engineer.",
1922
+ var sisyphusAgent = {
1923
+ description: "Sisyphus - Powerful AI orchestrator from OhMyOpenCode. Plans obsessively with todos, assesses search complexity before exploration, delegates strategically to specialized agents. Uses explore for internal code (parallel-friendly), librarian only for external docs, and always delegates UI work to frontend engineer.",
1917
1924
  mode: "primary",
1918
1925
  model: "anthropic/claude-opus-4-5",
1919
1926
  thinking: {
@@ -1921,7 +1928,7 @@ var omoAgent = {
1921
1928
  budgetTokens: 32000
1922
1929
  },
1923
1930
  maxTokens: 64000,
1924
- prompt: OMO_SYSTEM_PROMPT,
1931
+ prompt: SISYPHUS_SYSTEM_PROMPT,
1925
1932
  color: "#00CED1"
1926
1933
  };
1927
1934
 
@@ -3139,9 +3146,29 @@ function createDynamicTruncator(ctx) {
3139
3146
  truncateSync: (output, maxTokens, preserveHeaderLines) => truncateToTokenLimit(output, maxTokens, preserveHeaderLines)
3140
3147
  };
3141
3148
  }
3149
+ // src/shared/config-path.ts
3150
+ import * as path2 from "path";
3151
+ import * as os2 from "os";
3152
+ function getUserConfigDir() {
3153
+ if (process.platform === "win32") {
3154
+ return process.env.APPDATA || path2.join(os2.homedir(), "AppData", "Roaming");
3155
+ }
3156
+ return process.env.XDG_CONFIG_HOME || path2.join(os2.homedir(), ".config");
3157
+ }
3158
+ // src/shared/config-errors.ts
3159
+ var configLoadErrors = [];
3160
+ function getConfigLoadErrors() {
3161
+ return configLoadErrors;
3162
+ }
3163
+ function clearConfigLoadErrors() {
3164
+ configLoadErrors = [];
3165
+ }
3166
+ function addConfigLoadError(error) {
3167
+ configLoadErrors.push(error);
3168
+ }
3142
3169
  // src/agents/utils.ts
3143
3170
  var allBuiltinAgents = {
3144
- OmO: omoAgent,
3171
+ Sisyphus: sisyphusAgent,
3145
3172
  oracle: oracleAgent,
3146
3173
  librarian: librarianAgent,
3147
3174
  explore: exploreAgent,
@@ -3188,7 +3215,7 @@ function createBuiltinAgents(disabledAgents = [], agentOverrides = {}, directory
3188
3215
  continue;
3189
3216
  }
3190
3217
  let finalConfig = config;
3191
- if ((agentName === "OmO" || agentName === "librarian") && directory && config.prompt) {
3218
+ if ((agentName === "Sisyphus" || agentName === "librarian") && directory && config.prompt) {
3192
3219
  const envContext = createEnvContext(directory);
3193
3220
  finalConfig = {
3194
3221
  ...config,
@@ -3196,7 +3223,7 @@ function createBuiltinAgents(disabledAgents = [], agentOverrides = {}, directory
3196
3223
  };
3197
3224
  }
3198
3225
  const override = agentOverrides[agentName];
3199
- if (agentName === "OmO" && systemDefaultModel && !override?.model) {
3226
+ if (agentName === "Sisyphus" && systemDefaultModel && !override?.model) {
3200
3227
  finalConfig = {
3201
3228
  ...finalConfig,
3202
3229
  model: systemDefaultModel
@@ -3212,19 +3239,19 @@ function createBuiltinAgents(disabledAgents = [], agentOverrides = {}, directory
3212
3239
  }
3213
3240
  // src/hooks/todo-continuation-enforcer.ts
3214
3241
  import { existsSync as existsSync4, readdirSync as readdirSync2 } from "fs";
3215
- import { join as join5 } from "path";
3242
+ import { join as join6 } from "path";
3216
3243
 
3217
3244
  // src/features/hook-message-injector/injector.ts
3218
3245
  import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, readdirSync, writeFileSync } from "fs";
3219
- import { join as join4 } from "path";
3246
+ import { join as join5 } from "path";
3220
3247
 
3221
3248
  // src/features/hook-message-injector/constants.ts
3222
- import { join as join3 } from "path";
3223
- import { homedir as homedir2 } from "os";
3224
- var xdgData = process.env.XDG_DATA_HOME || join3(homedir2(), ".local", "share");
3225
- var OPENCODE_STORAGE = join3(xdgData, "opencode", "storage");
3226
- var MESSAGE_STORAGE = join3(OPENCODE_STORAGE, "message");
3227
- var PART_STORAGE = join3(OPENCODE_STORAGE, "part");
3249
+ import { join as join4 } from "path";
3250
+ import { homedir as homedir3 } from "os";
3251
+ var xdgData = process.env.XDG_DATA_HOME || join4(homedir3(), ".local", "share");
3252
+ var OPENCODE_STORAGE = join4(xdgData, "opencode", "storage");
3253
+ var MESSAGE_STORAGE = join4(OPENCODE_STORAGE, "message");
3254
+ var PART_STORAGE = join4(OPENCODE_STORAGE, "part");
3228
3255
 
3229
3256
  // src/features/hook-message-injector/injector.ts
3230
3257
  function findNearestMessageWithFields(messageDir) {
@@ -3232,7 +3259,7 @@ function findNearestMessageWithFields(messageDir) {
3232
3259
  const files = readdirSync(messageDir).filter((f) => f.endsWith(".json")).sort().reverse();
3233
3260
  for (const file of files) {
3234
3261
  try {
3235
- const content = readFileSync2(join4(messageDir, file), "utf-8");
3262
+ const content = readFileSync2(join5(messageDir, file), "utf-8");
3236
3263
  const msg = JSON.parse(content);
3237
3264
  if (msg.agent && msg.model?.providerID && msg.model?.modelID) {
3238
3265
  return msg;
@@ -3260,12 +3287,12 @@ function getOrCreateMessageDir(sessionID) {
3260
3287
  if (!existsSync3(MESSAGE_STORAGE)) {
3261
3288
  mkdirSync(MESSAGE_STORAGE, { recursive: true });
3262
3289
  }
3263
- const directPath = join4(MESSAGE_STORAGE, sessionID);
3290
+ const directPath = join5(MESSAGE_STORAGE, sessionID);
3264
3291
  if (existsSync3(directPath)) {
3265
3292
  return directPath;
3266
3293
  }
3267
3294
  for (const dir of readdirSync(MESSAGE_STORAGE)) {
3268
- const sessionPath = join4(MESSAGE_STORAGE, dir, sessionID);
3295
+ const sessionPath = join5(MESSAGE_STORAGE, dir, sessionID);
3269
3296
  if (existsSync3(sessionPath)) {
3270
3297
  return sessionPath;
3271
3298
  }
@@ -3319,12 +3346,12 @@ function injectHookMessage(sessionID, hookContent, originalMessage) {
3319
3346
  sessionID
3320
3347
  };
3321
3348
  try {
3322
- writeFileSync(join4(messageDir, `${messageID}.json`), JSON.stringify(messageMeta, null, 2));
3323
- const partDir = join4(PART_STORAGE, messageID);
3349
+ writeFileSync(join5(messageDir, `${messageID}.json`), JSON.stringify(messageMeta, null, 2));
3350
+ const partDir = join5(PART_STORAGE, messageID);
3324
3351
  if (!existsSync3(partDir)) {
3325
3352
  mkdirSync(partDir, { recursive: true });
3326
3353
  }
3327
- writeFileSync(join4(partDir, `${partID}.json`), JSON.stringify(textPart, null, 2));
3354
+ writeFileSync(join5(partDir, `${partID}.json`), JSON.stringify(textPart, null, 2));
3328
3355
  return true;
3329
3356
  } catch {
3330
3357
  return false;
@@ -3342,11 +3369,11 @@ Incomplete tasks remain in your todo list. Continue working on the next pending
3342
3369
  function getMessageDir(sessionID) {
3343
3370
  if (!existsSync4(MESSAGE_STORAGE))
3344
3371
  return null;
3345
- const directPath = join5(MESSAGE_STORAGE, sessionID);
3372
+ const directPath = join6(MESSAGE_STORAGE, sessionID);
3346
3373
  if (existsSync4(directPath))
3347
3374
  return directPath;
3348
3375
  for (const dir of readdirSync2(MESSAGE_STORAGE)) {
3349
- const sessionPath = join5(MESSAGE_STORAGE, dir, sessionID);
3376
+ const sessionPath = join6(MESSAGE_STORAGE, dir, sessionID);
3350
3377
  if (existsSync4(sessionPath))
3351
3378
  return sessionPath;
3352
3379
  }
@@ -3462,6 +3489,12 @@ function createTodoContinuationEnforcer(ctx) {
3462
3489
  try {
3463
3490
  const messageDir = getMessageDir(sessionID);
3464
3491
  const prevMessage = messageDir ? findNearestMessageWithFields(messageDir) : null;
3492
+ const agentHasWritePermission = !prevMessage?.tools || prevMessage.tools.write !== false && prevMessage.tools.edit !== false;
3493
+ if (!agentHasWritePermission) {
3494
+ log(`[${HOOK_NAME}] Skipped: previous agent lacks write permission`, { sessionID, agent: prevMessage?.agent, tools: prevMessage?.tools });
3495
+ remindedSessions.delete(sessionID);
3496
+ return;
3497
+ }
3465
3498
  log(`[${HOOK_NAME}] Injecting continuation prompt`, { sessionID, agent: prevMessage?.agent });
3466
3499
  await ctx.client.session.prompt({
3467
3500
  path: { id: sessionID },
@@ -3483,7 +3516,7 @@ function createTodoContinuationEnforcer(ctx) {
3483
3516
  log(`[${HOOK_NAME}] Prompt injection failed`, { sessionID, error: String(err) });
3484
3517
  remindedSessions.delete(sessionID);
3485
3518
  }
3486
- }, 200);
3519
+ }, 5000);
3487
3520
  pendingTimers.set(sessionID, timer);
3488
3521
  }
3489
3522
  if (event.type === "message.updated") {
@@ -3827,20 +3860,20 @@ function createSessionNotification(ctx, config = {}) {
3827
3860
  }
3828
3861
  // src/hooks/session-recovery/storage.ts
3829
3862
  import { existsSync as existsSync5, mkdirSync as mkdirSync2, readdirSync as readdirSync3, readFileSync as readFileSync3, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
3830
- import { join as join7 } from "path";
3863
+ import { join as join8 } from "path";
3831
3864
 
3832
3865
  // src/hooks/session-recovery/constants.ts
3833
- import { join as join6 } from "path";
3866
+ import { join as join7 } from "path";
3834
3867
 
3835
3868
  // node_modules/xdg-basedir/index.js
3836
- import os2 from "os";
3837
- import path2 from "path";
3838
- var homeDirectory = os2.homedir();
3869
+ import os3 from "os";
3870
+ import path3 from "path";
3871
+ var homeDirectory = os3.homedir();
3839
3872
  var { env } = process;
3840
- var xdgData2 = env.XDG_DATA_HOME || (homeDirectory ? path2.join(homeDirectory, ".local", "share") : undefined);
3841
- var xdgConfig = env.XDG_CONFIG_HOME || (homeDirectory ? path2.join(homeDirectory, ".config") : undefined);
3842
- var xdgState = env.XDG_STATE_HOME || (homeDirectory ? path2.join(homeDirectory, ".local", "state") : undefined);
3843
- var xdgCache = env.XDG_CACHE_HOME || (homeDirectory ? path2.join(homeDirectory, ".cache") : undefined);
3873
+ var xdgData2 = env.XDG_DATA_HOME || (homeDirectory ? path3.join(homeDirectory, ".local", "share") : undefined);
3874
+ var xdgConfig = env.XDG_CONFIG_HOME || (homeDirectory ? path3.join(homeDirectory, ".config") : undefined);
3875
+ var xdgState = env.XDG_STATE_HOME || (homeDirectory ? path3.join(homeDirectory, ".local", "state") : undefined);
3876
+ var xdgCache = env.XDG_CACHE_HOME || (homeDirectory ? path3.join(homeDirectory, ".cache") : undefined);
3844
3877
  var xdgRuntime = env.XDG_RUNTIME_DIR || undefined;
3845
3878
  var xdgDataDirectories = (env.XDG_DATA_DIRS || "/usr/local/share/:/usr/share/").split(":");
3846
3879
  if (xdgData2) {
@@ -3852,9 +3885,9 @@ if (xdgConfig) {
3852
3885
  }
3853
3886
 
3854
3887
  // src/hooks/session-recovery/constants.ts
3855
- var OPENCODE_STORAGE2 = join6(xdgData2 ?? "", "opencode", "storage");
3856
- var MESSAGE_STORAGE2 = join6(OPENCODE_STORAGE2, "message");
3857
- var PART_STORAGE2 = join6(OPENCODE_STORAGE2, "part");
3888
+ var OPENCODE_STORAGE2 = join7(xdgData2 ?? "", "opencode", "storage");
3889
+ var MESSAGE_STORAGE2 = join7(OPENCODE_STORAGE2, "message");
3890
+ var PART_STORAGE2 = join7(OPENCODE_STORAGE2, "part");
3858
3891
  var THINKING_TYPES = new Set(["thinking", "redacted_thinking", "reasoning"]);
3859
3892
  var META_TYPES = new Set(["step-start", "step-finish"]);
3860
3893
  var CONTENT_TYPES = new Set(["text", "tool", "tool_use", "tool_result"]);
@@ -3868,12 +3901,12 @@ function generatePartId2() {
3868
3901
  function getMessageDir2(sessionID) {
3869
3902
  if (!existsSync5(MESSAGE_STORAGE2))
3870
3903
  return "";
3871
- const directPath = join7(MESSAGE_STORAGE2, sessionID);
3904
+ const directPath = join8(MESSAGE_STORAGE2, sessionID);
3872
3905
  if (existsSync5(directPath)) {
3873
3906
  return directPath;
3874
3907
  }
3875
3908
  for (const dir of readdirSync3(MESSAGE_STORAGE2)) {
3876
- const sessionPath = join7(MESSAGE_STORAGE2, dir, sessionID);
3909
+ const sessionPath = join8(MESSAGE_STORAGE2, dir, sessionID);
3877
3910
  if (existsSync5(sessionPath)) {
3878
3911
  return sessionPath;
3879
3912
  }
@@ -3889,7 +3922,7 @@ function readMessages(sessionID) {
3889
3922
  if (!file.endsWith(".json"))
3890
3923
  continue;
3891
3924
  try {
3892
- const content = readFileSync3(join7(messageDir, file), "utf-8");
3925
+ const content = readFileSync3(join8(messageDir, file), "utf-8");
3893
3926
  messages.push(JSON.parse(content));
3894
3927
  } catch {
3895
3928
  continue;
@@ -3904,7 +3937,7 @@ function readMessages(sessionID) {
3904
3937
  });
3905
3938
  }
3906
3939
  function readParts(messageID) {
3907
- const partDir = join7(PART_STORAGE2, messageID);
3940
+ const partDir = join8(PART_STORAGE2, messageID);
3908
3941
  if (!existsSync5(partDir))
3909
3942
  return [];
3910
3943
  const parts = [];
@@ -3912,7 +3945,7 @@ function readParts(messageID) {
3912
3945
  if (!file.endsWith(".json"))
3913
3946
  continue;
3914
3947
  try {
3915
- const content = readFileSync3(join7(partDir, file), "utf-8");
3948
+ const content = readFileSync3(join8(partDir, file), "utf-8");
3916
3949
  parts.push(JSON.parse(content));
3917
3950
  } catch {
3918
3951
  continue;
@@ -3942,7 +3975,7 @@ function messageHasContent(messageID) {
3942
3975
  return parts.some(hasContent);
3943
3976
  }
3944
3977
  function injectTextPart(sessionID, messageID, text) {
3945
- const partDir = join7(PART_STORAGE2, messageID);
3978
+ const partDir = join8(PART_STORAGE2, messageID);
3946
3979
  if (!existsSync5(partDir)) {
3947
3980
  mkdirSync2(partDir, { recursive: true });
3948
3981
  }
@@ -3956,7 +3989,7 @@ function injectTextPart(sessionID, messageID, text) {
3956
3989
  synthetic: true
3957
3990
  };
3958
3991
  try {
3959
- writeFileSync2(join7(partDir, `${partId}.json`), JSON.stringify(part, null, 2));
3992
+ writeFileSync2(join8(partDir, `${partId}.json`), JSON.stringify(part, null, 2));
3960
3993
  return true;
3961
3994
  } catch {
3962
3995
  return false;
@@ -3972,19 +4005,6 @@ function findEmptyMessages(sessionID) {
3972
4005
  }
3973
4006
  return emptyIds;
3974
4007
  }
3975
- function findEmptyMessageByIndex(sessionID, targetIndex) {
3976
- const messages = readMessages(sessionID);
3977
- const indicesToTry = [targetIndex, targetIndex - 1, targetIndex - 2];
3978
- for (const idx of indicesToTry) {
3979
- if (idx < 0 || idx >= messages.length)
3980
- continue;
3981
- const targetMsg = messages[idx];
3982
- if (!messageHasContent(targetMsg.id)) {
3983
- return targetMsg.id;
3984
- }
3985
- }
3986
- return null;
3987
- }
3988
4008
  function findMessagesWithThinkingBlocks(sessionID) {
3989
4009
  const messages = readMessages(sessionID);
3990
4010
  const result = [];
@@ -3999,23 +4019,6 @@ function findMessagesWithThinkingBlocks(sessionID) {
3999
4019
  }
4000
4020
  return result;
4001
4021
  }
4002
- function findMessagesWithThinkingOnly(sessionID) {
4003
- const messages = readMessages(sessionID);
4004
- const result = [];
4005
- for (const msg of messages) {
4006
- if (msg.role !== "assistant")
4007
- continue;
4008
- const parts = readParts(msg.id);
4009
- if (parts.length === 0)
4010
- continue;
4011
- const hasThinking = parts.some((p) => THINKING_TYPES.has(p.type));
4012
- const hasTextContent = parts.some(hasContent);
4013
- if (hasThinking && !hasTextContent) {
4014
- result.push(msg.id);
4015
- }
4016
- }
4017
- return result;
4018
- }
4019
4022
  function findMessagesWithOrphanThinking(sessionID) {
4020
4023
  const messages = readMessages(sessionID);
4021
4024
  const result = [];
@@ -4036,7 +4039,7 @@ function findMessagesWithOrphanThinking(sessionID) {
4036
4039
  return result;
4037
4040
  }
4038
4041
  function prependThinkingPart(sessionID, messageID) {
4039
- const partDir = join7(PART_STORAGE2, messageID);
4042
+ const partDir = join8(PART_STORAGE2, messageID);
4040
4043
  if (!existsSync5(partDir)) {
4041
4044
  mkdirSync2(partDir, { recursive: true });
4042
4045
  }
@@ -4050,14 +4053,14 @@ function prependThinkingPart(sessionID, messageID) {
4050
4053
  synthetic: true
4051
4054
  };
4052
4055
  try {
4053
- writeFileSync2(join7(partDir, `${partId}.json`), JSON.stringify(part, null, 2));
4056
+ writeFileSync2(join8(partDir, `${partId}.json`), JSON.stringify(part, null, 2));
4054
4057
  return true;
4055
4058
  } catch {
4056
4059
  return false;
4057
4060
  }
4058
4061
  }
4059
4062
  function stripThinkingParts(messageID) {
4060
- const partDir = join7(PART_STORAGE2, messageID);
4063
+ const partDir = join8(PART_STORAGE2, messageID);
4061
4064
  if (!existsSync5(partDir))
4062
4065
  return false;
4063
4066
  let anyRemoved = false;
@@ -4065,7 +4068,7 @@ function stripThinkingParts(messageID) {
4065
4068
  if (!file.endsWith(".json"))
4066
4069
  continue;
4067
4070
  try {
4068
- const filePath = join7(partDir, file);
4071
+ const filePath = join8(partDir, file);
4069
4072
  const content = readFileSync3(filePath, "utf-8");
4070
4073
  const part = JSON.parse(content);
4071
4074
  if (THINKING_TYPES.has(part.type)) {
@@ -4078,50 +4081,6 @@ function stripThinkingParts(messageID) {
4078
4081
  }
4079
4082
  return anyRemoved;
4080
4083
  }
4081
- function replaceEmptyTextParts(messageID, replacementText) {
4082
- const partDir = join7(PART_STORAGE2, messageID);
4083
- if (!existsSync5(partDir))
4084
- return false;
4085
- let anyReplaced = false;
4086
- for (const file of readdirSync3(partDir)) {
4087
- if (!file.endsWith(".json"))
4088
- continue;
4089
- try {
4090
- const filePath = join7(partDir, file);
4091
- const content = readFileSync3(filePath, "utf-8");
4092
- const part = JSON.parse(content);
4093
- if (part.type === "text") {
4094
- const textPart = part;
4095
- if (!textPart.text?.trim()) {
4096
- textPart.text = replacementText;
4097
- textPart.synthetic = true;
4098
- writeFileSync2(filePath, JSON.stringify(textPart, null, 2));
4099
- anyReplaced = true;
4100
- }
4101
- }
4102
- } catch {
4103
- continue;
4104
- }
4105
- }
4106
- return anyReplaced;
4107
- }
4108
- function findMessagesWithEmptyTextParts(sessionID) {
4109
- const messages = readMessages(sessionID);
4110
- const result = [];
4111
- for (const msg of messages) {
4112
- const parts = readParts(msg.id);
4113
- const hasEmptyTextPart = parts.some((p) => {
4114
- if (p.type !== "text")
4115
- return false;
4116
- const textPart = p;
4117
- return !textPart.text?.trim();
4118
- });
4119
- if (hasEmptyTextPart) {
4120
- result.push(msg.id);
4121
- }
4122
- }
4123
- return result;
4124
- }
4125
4084
  function findMessageByIndexNeedingThinking(sessionID, targetIndex) {
4126
4085
  const messages = readMessages(sessionID);
4127
4086
  if (targetIndex < 0 || targetIndex >= messages.length)
@@ -4142,6 +4101,37 @@ function findMessageByIndexNeedingThinking(sessionID, targetIndex) {
4142
4101
  }
4143
4102
 
4144
4103
  // src/hooks/session-recovery/index.ts
4104
+ var RECOVERY_RESUME_TEXT = "[session recovered - continuing previous task]";
4105
+ function findLastUserMessage(messages) {
4106
+ for (let i = messages.length - 1;i >= 0; i--) {
4107
+ if (messages[i].info?.role === "user") {
4108
+ return messages[i];
4109
+ }
4110
+ }
4111
+ return;
4112
+ }
4113
+ function extractResumeConfig(userMessage, sessionID) {
4114
+ return {
4115
+ sessionID,
4116
+ agent: userMessage?.info?.agent,
4117
+ model: userMessage?.info?.model
4118
+ };
4119
+ }
4120
+ async function resumeSession(client, config) {
4121
+ try {
4122
+ await client.session.prompt({
4123
+ path: { id: config.sessionID },
4124
+ body: {
4125
+ parts: [{ type: "text", text: RECOVERY_RESUME_TEXT }],
4126
+ agent: config.agent,
4127
+ model: config.model
4128
+ }
4129
+ });
4130
+ return true;
4131
+ } catch {
4132
+ return false;
4133
+ }
4134
+ }
4145
4135
  function getErrorMessage(error) {
4146
4136
  if (!error)
4147
4137
  return "";
@@ -4184,9 +4174,6 @@ function detectErrorType(error) {
4184
4174
  if (message.includes("thinking is disabled") && message.includes("cannot contain")) {
4185
4175
  return "thinking_disabled_violation";
4186
4176
  }
4187
- 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")) {
4188
- return "empty_content_message";
4189
- }
4190
4177
  return null;
4191
4178
  }
4192
4179
  function extractToolUseIds(parts) {
@@ -4255,55 +4242,9 @@ async function recoverThinkingDisabledViolation(_client, sessionID, _failedAssis
4255
4242
  }
4256
4243
  return anySuccess;
4257
4244
  }
4258
- var PLACEHOLDER_TEXT = "[user interrupted]";
4259
- async function recoverEmptyContentMessage(_client, sessionID, failedAssistantMsg, _directory, error) {
4260
- const targetIndex = extractMessageIndex(error);
4261
- const failedID = failedAssistantMsg.info?.id;
4262
- let anySuccess = false;
4263
- const messagesWithEmptyText = findMessagesWithEmptyTextParts(sessionID);
4264
- for (const messageID of messagesWithEmptyText) {
4265
- if (replaceEmptyTextParts(messageID, PLACEHOLDER_TEXT)) {
4266
- anySuccess = true;
4267
- }
4268
- }
4269
- const thinkingOnlyIDs = findMessagesWithThinkingOnly(sessionID);
4270
- for (const messageID of thinkingOnlyIDs) {
4271
- if (injectTextPart(sessionID, messageID, PLACEHOLDER_TEXT)) {
4272
- anySuccess = true;
4273
- }
4274
- }
4275
- if (targetIndex !== null) {
4276
- const targetMessageID = findEmptyMessageByIndex(sessionID, targetIndex);
4277
- if (targetMessageID) {
4278
- if (replaceEmptyTextParts(targetMessageID, PLACEHOLDER_TEXT)) {
4279
- return true;
4280
- }
4281
- if (injectTextPart(sessionID, targetMessageID, PLACEHOLDER_TEXT)) {
4282
- return true;
4283
- }
4284
- }
4285
- }
4286
- if (failedID) {
4287
- if (replaceEmptyTextParts(failedID, PLACEHOLDER_TEXT)) {
4288
- return true;
4289
- }
4290
- if (injectTextPart(sessionID, failedID, PLACEHOLDER_TEXT)) {
4291
- return true;
4292
- }
4293
- }
4294
- const emptyMessageIDs = findEmptyMessages(sessionID);
4295
- for (const messageID of emptyMessageIDs) {
4296
- if (replaceEmptyTextParts(messageID, PLACEHOLDER_TEXT)) {
4297
- anySuccess = true;
4298
- }
4299
- if (injectTextPart(sessionID, messageID, PLACEHOLDER_TEXT)) {
4300
- anySuccess = true;
4301
- }
4302
- }
4303
- return anySuccess;
4304
- }
4305
- function createSessionRecoveryHook(ctx) {
4245
+ function createSessionRecoveryHook(ctx, options) {
4306
4246
  const processingErrors = new Set;
4247
+ const experimental = options?.experimental;
4307
4248
  let onAbortCallback = null;
4308
4249
  let onRecoveryCompleteCallback = null;
4309
4250
  const setOnAbortCallback = (callback) => {
@@ -4345,14 +4286,12 @@ function createSessionRecoveryHook(ctx) {
4345
4286
  const toastTitles = {
4346
4287
  tool_result_missing: "Tool Crash Recovery",
4347
4288
  thinking_block_order: "Thinking Block Recovery",
4348
- thinking_disabled_violation: "Thinking Strip Recovery",
4349
- empty_content_message: "Empty Message Recovery"
4289
+ thinking_disabled_violation: "Thinking Strip Recovery"
4350
4290
  };
4351
4291
  const toastMessages = {
4352
4292
  tool_result_missing: "Injecting cancelled tool results...",
4353
4293
  thinking_block_order: "Fixing message structure...",
4354
- thinking_disabled_violation: "Stripping thinking blocks...",
4355
- empty_content_message: "Fixing empty message..."
4294
+ thinking_disabled_violation: "Stripping thinking blocks..."
4356
4295
  };
4357
4296
  await ctx.client.tui.showToast({
4358
4297
  body: {
@@ -4367,10 +4306,18 @@ function createSessionRecoveryHook(ctx) {
4367
4306
  success = await recoverToolResultMissing(ctx.client, sessionID, failedMsg);
4368
4307
  } else if (errorType === "thinking_block_order") {
4369
4308
  success = await recoverThinkingBlockOrder(ctx.client, sessionID, failedMsg, ctx.directory, info.error);
4309
+ if (success && experimental?.auto_resume) {
4310
+ const lastUser = findLastUserMessage(msgs ?? []);
4311
+ const resumeConfig = extractResumeConfig(lastUser, sessionID);
4312
+ await resumeSession(ctx.client, resumeConfig);
4313
+ }
4370
4314
  } else if (errorType === "thinking_disabled_violation") {
4371
4315
  success = await recoverThinkingDisabledViolation(ctx.client, sessionID, failedMsg);
4372
- } else if (errorType === "empty_content_message") {
4373
- success = await recoverEmptyContentMessage(ctx.client, sessionID, failedMsg, ctx.directory, info.error);
4316
+ if (success && experimental?.auto_resume) {
4317
+ const lastUser = findLastUserMessage(msgs ?? []);
4318
+ const resumeConfig = extractResumeConfig(lastUser, sessionID);
4319
+ await resumeSession(ctx.client, resumeConfig);
4320
+ }
4374
4321
  }
4375
4322
  return success;
4376
4323
  } catch (err) {
@@ -4393,7 +4340,7 @@ function createSessionRecoveryHook(ctx) {
4393
4340
  // src/hooks/comment-checker/cli.ts
4394
4341
  var {spawn: spawn3 } = globalThis.Bun;
4395
4342
  import { createRequire as createRequire2 } from "module";
4396
- import { dirname, join as join9 } from "path";
4343
+ import { dirname, join as join10 } from "path";
4397
4344
  import { existsSync as existsSync7 } from "fs";
4398
4345
  import * as fs2 from "fs";
4399
4346
  import { tmpdir as tmpdir3 } from "os";
@@ -4401,11 +4348,11 @@ import { tmpdir as tmpdir3 } from "os";
4401
4348
  // src/hooks/comment-checker/downloader.ts
4402
4349
  var {spawn: spawn2 } = globalThis.Bun;
4403
4350
  import { existsSync as existsSync6, mkdirSync as mkdirSync3, chmodSync, unlinkSync as unlinkSync2, appendFileSync as appendFileSync2 } from "fs";
4404
- import { join as join8 } from "path";
4405
- import { homedir as homedir3, tmpdir as tmpdir2 } from "os";
4351
+ import { join as join9 } from "path";
4352
+ import { homedir as homedir4, tmpdir as tmpdir2 } from "os";
4406
4353
  import { createRequire } from "module";
4407
4354
  var DEBUG = process.env.COMMENT_CHECKER_DEBUG === "1";
4408
- var DEBUG_FILE = join8(tmpdir2(), "comment-checker-debug.log");
4355
+ var DEBUG_FILE = join9(tmpdir2(), "comment-checker-debug.log");
4409
4356
  function debugLog(...args) {
4410
4357
  if (DEBUG) {
4411
4358
  const msg = `[${new Date().toISOString()}] [comment-checker:downloader] ${args.map((a) => typeof a === "object" ? JSON.stringify(a, null, 2) : String(a)).join(" ")}
@@ -4423,14 +4370,14 @@ var PLATFORM_MAP = {
4423
4370
  };
4424
4371
  function getCacheDir() {
4425
4372
  const xdgCache2 = process.env.XDG_CACHE_HOME;
4426
- const base = xdgCache2 || join8(homedir3(), ".cache");
4427
- return join8(base, "oh-my-opencode", "bin");
4373
+ const base = xdgCache2 || join9(homedir4(), ".cache");
4374
+ return join9(base, "oh-my-opencode", "bin");
4428
4375
  }
4429
4376
  function getBinaryName() {
4430
4377
  return process.platform === "win32" ? "comment-checker.exe" : "comment-checker";
4431
4378
  }
4432
4379
  function getCachedBinaryPath() {
4433
- const binaryPath = join8(getCacheDir(), getBinaryName());
4380
+ const binaryPath = join9(getCacheDir(), getBinaryName());
4434
4381
  return existsSync6(binaryPath) ? binaryPath : null;
4435
4382
  }
4436
4383
  function getPackageVersion() {
@@ -4478,14 +4425,14 @@ async function downloadCommentChecker() {
4478
4425
  }
4479
4426
  const cacheDir = getCacheDir();
4480
4427
  const binaryName = getBinaryName();
4481
- const binaryPath = join8(cacheDir, binaryName);
4428
+ const binaryPath = join9(cacheDir, binaryName);
4482
4429
  if (existsSync6(binaryPath)) {
4483
4430
  debugLog("Binary already cached at:", binaryPath);
4484
4431
  return binaryPath;
4485
4432
  }
4486
4433
  const version = getPackageVersion();
4487
- const { os: os3, arch, ext } = platformInfo;
4488
- const assetName = `comment-checker_v${version}_${os3}_${arch}.${ext}`;
4434
+ const { os: os4, arch, ext } = platformInfo;
4435
+ const assetName = `comment-checker_v${version}_${os4}_${arch}.${ext}`;
4489
4436
  const downloadUrl = `https://github.com/${REPO}/releases/download/v${version}/${assetName}`;
4490
4437
  debugLog(`Downloading from: ${downloadUrl}`);
4491
4438
  console.log(`[oh-my-opencode] Downloading comment-checker binary...`);
@@ -4497,7 +4444,7 @@ async function downloadCommentChecker() {
4497
4444
  if (!response.ok) {
4498
4445
  throw new Error(`HTTP ${response.status}: ${response.statusText}`);
4499
4446
  }
4500
- const archivePath = join8(cacheDir, assetName);
4447
+ const archivePath = join9(cacheDir, assetName);
4501
4448
  const arrayBuffer = await response.arrayBuffer();
4502
4449
  await Bun.write(archivePath, arrayBuffer);
4503
4450
  debugLog(`Downloaded archive to: ${archivePath}`);
@@ -4533,7 +4480,7 @@ async function ensureCommentCheckerBinary() {
4533
4480
 
4534
4481
  // src/hooks/comment-checker/cli.ts
4535
4482
  var DEBUG2 = process.env.COMMENT_CHECKER_DEBUG === "1";
4536
- var DEBUG_FILE2 = join9(tmpdir3(), "comment-checker-debug.log");
4483
+ var DEBUG_FILE2 = join10(tmpdir3(), "comment-checker-debug.log");
4537
4484
  function debugLog2(...args) {
4538
4485
  if (DEBUG2) {
4539
4486
  const msg = `[${new Date().toISOString()}] [comment-checker:cli] ${args.map((a) => typeof a === "object" ? JSON.stringify(a, null, 2) : String(a)).join(" ")}
@@ -4550,7 +4497,7 @@ function findCommentCheckerPathSync() {
4550
4497
  const require2 = createRequire2(import.meta.url);
4551
4498
  const cliPkgPath = require2.resolve("@code-yeongyu/comment-checker/package.json");
4552
4499
  const cliDir = dirname(cliPkgPath);
4553
- const binaryPath = join9(cliDir, "bin", binaryName);
4500
+ const binaryPath = join10(cliDir, "bin", binaryName);
4554
4501
  if (existsSync7(binaryPath)) {
4555
4502
  debugLog2("found binary in main package:", binaryPath);
4556
4503
  return binaryPath;
@@ -4597,8 +4544,8 @@ async function getCommentCheckerPath() {
4597
4544
  function startBackgroundInit() {
4598
4545
  if (!initPromise) {
4599
4546
  initPromise = getCommentCheckerPath();
4600
- initPromise.then((path3) => {
4601
- debugLog2("background init complete:", path3 || "no binary");
4547
+ initPromise.then((path4) => {
4548
+ debugLog2("background init complete:", path4 || "no binary");
4602
4549
  }).catch((err) => {
4603
4550
  debugLog2("background init error:", err);
4604
4551
  });
@@ -4647,9 +4594,9 @@ async function runCommentChecker(input, cliPath) {
4647
4594
  import * as fs3 from "fs";
4648
4595
  import { existsSync as existsSync8 } from "fs";
4649
4596
  import { tmpdir as tmpdir4 } from "os";
4650
- import { join as join10 } from "path";
4597
+ import { join as join11 } from "path";
4651
4598
  var DEBUG3 = process.env.COMMENT_CHECKER_DEBUG === "1";
4652
- var DEBUG_FILE3 = join10(tmpdir4(), "comment-checker-debug.log");
4599
+ var DEBUG_FILE3 = join11(tmpdir4(), "comment-checker-debug.log");
4653
4600
  function debugLog3(...args) {
4654
4601
  if (DEBUG3) {
4655
4602
  const msg = `[${new Date().toISOString()}] [comment-checker:hook] ${args.map((a) => typeof a === "object" ? JSON.stringify(a, null, 2) : String(a)).join(" ")}
@@ -4673,8 +4620,8 @@ function createCommentCheckerHooks() {
4673
4620
  debugLog3("createCommentCheckerHooks called");
4674
4621
  startBackgroundInit();
4675
4622
  cliPathPromise = getCommentCheckerPath();
4676
- cliPathPromise.then((path3) => {
4677
- debugLog3("CLI path resolved:", path3 || "disabled (no binary)");
4623
+ cliPathPromise.then((path4) => {
4624
+ debugLog3("CLI path resolved:", path4 || "disabled (no binary)");
4678
4625
  }).catch((err) => {
4679
4626
  debugLog3("CLI path resolution error:", err);
4680
4627
  });
@@ -4765,6 +4712,8 @@ ${result.message}`;
4765
4712
  }
4766
4713
  // src/hooks/tool-output-truncator.ts
4767
4714
  var TRUNCATABLE_TOOLS = [
4715
+ "grep",
4716
+ "Grep",
4768
4717
  "safe_grep",
4769
4718
  "glob",
4770
4719
  "Glob",
@@ -4795,7 +4744,7 @@ function createToolOutputTruncatorHook(ctx) {
4795
4744
  }
4796
4745
  // src/hooks/directory-agents-injector/index.ts
4797
4746
  import { existsSync as existsSync10, readFileSync as readFileSync5 } from "fs";
4798
- import { dirname as dirname2, join as join13, resolve as resolve2 } from "path";
4747
+ import { dirname as dirname2, join as join14, resolve as resolve2 } from "path";
4799
4748
 
4800
4749
  // src/hooks/directory-agents-injector/storage.ts
4801
4750
  import {
@@ -4805,17 +4754,17 @@ import {
4805
4754
  writeFileSync as writeFileSync3,
4806
4755
  unlinkSync as unlinkSync3
4807
4756
  } from "fs";
4808
- import { join as join12 } from "path";
4757
+ import { join as join13 } from "path";
4809
4758
 
4810
4759
  // src/hooks/directory-agents-injector/constants.ts
4811
- import { join as join11 } from "path";
4812
- var OPENCODE_STORAGE3 = join11(xdgData2 ?? "", "opencode", "storage");
4813
- var AGENTS_INJECTOR_STORAGE = join11(OPENCODE_STORAGE3, "directory-agents");
4760
+ import { join as join12 } from "path";
4761
+ var OPENCODE_STORAGE3 = join12(xdgData2 ?? "", "opencode", "storage");
4762
+ var AGENTS_INJECTOR_STORAGE = join12(OPENCODE_STORAGE3, "directory-agents");
4814
4763
  var AGENTS_FILENAME = "AGENTS.md";
4815
4764
 
4816
4765
  // src/hooks/directory-agents-injector/storage.ts
4817
4766
  function getStoragePath(sessionID) {
4818
- return join12(AGENTS_INJECTOR_STORAGE, `${sessionID}.json`);
4767
+ return join13(AGENTS_INJECTOR_STORAGE, `${sessionID}.json`);
4819
4768
  }
4820
4769
  function loadInjectedPaths(sessionID) {
4821
4770
  const filePath = getStoragePath(sessionID);
@@ -4867,7 +4816,7 @@ function createDirectoryAgentsInjectorHook(ctx) {
4867
4816
  const found = [];
4868
4817
  let current = startDir;
4869
4818
  while (true) {
4870
- const agentsPath = join13(current, AGENTS_FILENAME);
4819
+ const agentsPath = join14(current, AGENTS_FILENAME);
4871
4820
  if (existsSync10(agentsPath)) {
4872
4821
  found.push(agentsPath);
4873
4822
  }
@@ -4904,10 +4853,10 @@ function createDirectoryAgentsInjectorHook(ctx) {
4904
4853
  }
4905
4854
  if (toInject.length === 0)
4906
4855
  return;
4907
- for (const { path: path3, content } of toInject) {
4856
+ for (const { path: path4, content } of toInject) {
4908
4857
  output.output += `
4909
4858
 
4910
- [Directory Context: ${path3}]
4859
+ [Directory Context: ${path4}]
4911
4860
  ${content}`;
4912
4861
  }
4913
4862
  saveInjectedPaths(input.sessionID, cache);
@@ -4936,7 +4885,7 @@ ${content}`;
4936
4885
  }
4937
4886
  // src/hooks/directory-readme-injector/index.ts
4938
4887
  import { existsSync as existsSync12, readFileSync as readFileSync7 } from "fs";
4939
- import { dirname as dirname3, join as join16, resolve as resolve3 } from "path";
4888
+ import { dirname as dirname3, join as join17, resolve as resolve3 } from "path";
4940
4889
 
4941
4890
  // src/hooks/directory-readme-injector/storage.ts
4942
4891
  import {
@@ -4946,17 +4895,17 @@ import {
4946
4895
  writeFileSync as writeFileSync4,
4947
4896
  unlinkSync as unlinkSync4
4948
4897
  } from "fs";
4949
- import { join as join15 } from "path";
4898
+ import { join as join16 } from "path";
4950
4899
 
4951
4900
  // src/hooks/directory-readme-injector/constants.ts
4952
- import { join as join14 } from "path";
4953
- var OPENCODE_STORAGE4 = join14(xdgData2 ?? "", "opencode", "storage");
4954
- var README_INJECTOR_STORAGE = join14(OPENCODE_STORAGE4, "directory-readme");
4901
+ import { join as join15 } from "path";
4902
+ var OPENCODE_STORAGE4 = join15(xdgData2 ?? "", "opencode", "storage");
4903
+ var README_INJECTOR_STORAGE = join15(OPENCODE_STORAGE4, "directory-readme");
4955
4904
  var README_FILENAME = "README.md";
4956
4905
 
4957
4906
  // src/hooks/directory-readme-injector/storage.ts
4958
4907
  function getStoragePath2(sessionID) {
4959
- return join15(README_INJECTOR_STORAGE, `${sessionID}.json`);
4908
+ return join16(README_INJECTOR_STORAGE, `${sessionID}.json`);
4960
4909
  }
4961
4910
  function loadInjectedPaths2(sessionID) {
4962
4911
  const filePath = getStoragePath2(sessionID);
@@ -5008,7 +4957,7 @@ function createDirectoryReadmeInjectorHook(ctx) {
5008
4957
  const found = [];
5009
4958
  let current = startDir;
5010
4959
  while (true) {
5011
- const readmePath = join16(current, README_FILENAME);
4960
+ const readmePath = join17(current, README_FILENAME);
5012
4961
  if (existsSync12(readmePath)) {
5013
4962
  found.push(readmePath);
5014
4963
  }
@@ -5045,10 +4994,10 @@ function createDirectoryReadmeInjectorHook(ctx) {
5045
4994
  }
5046
4995
  if (toInject.length === 0)
5047
4996
  return;
5048
- for (const { path: path3, content } of toInject) {
4997
+ for (const { path: path4, content } of toInject) {
5049
4998
  output.output += `
5050
4999
 
5051
- [Project README: ${path3}]
5000
+ [Project README: ${path4}]
5052
5001
  ${content}`;
5053
5002
  }
5054
5003
  saveInjectedPaths2(input.sessionID, cache);
@@ -5111,7 +5060,8 @@ var TOKEN_LIMIT_KEYWORDS = [
5111
5060
  "max_tokens",
5112
5061
  "token limit",
5113
5062
  "context length",
5114
- "too many tokens"
5063
+ "too many tokens",
5064
+ "non-empty content"
5115
5065
  ];
5116
5066
  function extractTokensFromMessage(message) {
5117
5067
  for (const pattern of TOKEN_LIMIT_PATTERNS) {
@@ -5130,6 +5080,13 @@ function isTokenLimitError(text) {
5130
5080
  }
5131
5081
  function parseAnthropicTokenLimitError(err) {
5132
5082
  if (typeof err === "string") {
5083
+ if (err.toLowerCase().includes("non-empty content")) {
5084
+ return {
5085
+ currentTokens: 0,
5086
+ maxTokens: 0,
5087
+ errorType: "non-empty content"
5088
+ };
5089
+ }
5133
5090
  if (isTokenLimitError(err)) {
5134
5091
  const tokens = extractTokensFromMessage(err);
5135
5092
  return {
@@ -5225,6 +5182,13 @@ function parseAnthropicTokenLimitError(err) {
5225
5182
  };
5226
5183
  }
5227
5184
  }
5185
+ if (combinedText.toLowerCase().includes("non-empty content")) {
5186
+ return {
5187
+ currentTokens: 0,
5188
+ maxTokens: 0,
5189
+ errorType: "non-empty content"
5190
+ };
5191
+ }
5228
5192
  if (isTokenLimitError(combinedText)) {
5229
5193
  return {
5230
5194
  currentTokens: 0,
@@ -5247,26 +5211,35 @@ var FALLBACK_CONFIG = {
5247
5211
  minMessagesRequired: 2
5248
5212
  };
5249
5213
  var TRUNCATE_CONFIG = {
5250
- maxTruncateAttempts: 10,
5251
- minOutputSizeToTruncate: 1000
5214
+ maxTruncateAttempts: 20,
5215
+ minOutputSizeToTruncate: 500,
5216
+ targetTokenRatio: 0.5,
5217
+ charsPerToken: 4
5252
5218
  };
5253
5219
 
5254
5220
  // src/hooks/anthropic-auto-compact/storage.ts
5255
5221
  import { existsSync as existsSync13, readdirSync as readdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
5256
- import { join as join17 } from "path";
5257
- var OPENCODE_STORAGE5 = join17(xdgData2 ?? "", "opencode", "storage");
5258
- var MESSAGE_STORAGE3 = join17(OPENCODE_STORAGE5, "message");
5259
- var PART_STORAGE3 = join17(OPENCODE_STORAGE5, "part");
5222
+ import { homedir as homedir5 } from "os";
5223
+ import { join as join18 } from "path";
5224
+ var OPENCODE_STORAGE5 = join18(xdgData2 ?? "", "opencode", "storage");
5225
+ if (process.platform === "darwin" && !existsSync13(OPENCODE_STORAGE5)) {
5226
+ const localShare = join18(homedir5(), ".local", "share", "opencode", "storage");
5227
+ if (existsSync13(localShare)) {
5228
+ OPENCODE_STORAGE5 = localShare;
5229
+ }
5230
+ }
5231
+ var MESSAGE_STORAGE3 = join18(OPENCODE_STORAGE5, "message");
5232
+ var PART_STORAGE3 = join18(OPENCODE_STORAGE5, "part");
5260
5233
  var TRUNCATION_MESSAGE = "[TOOL RESULT TRUNCATED - Context limit exceeded. Original output was too large and has been truncated to recover the session. Please re-run this tool if you need the full output.]";
5261
5234
  function getMessageDir3(sessionID) {
5262
5235
  if (!existsSync13(MESSAGE_STORAGE3))
5263
5236
  return "";
5264
- const directPath = join17(MESSAGE_STORAGE3, sessionID);
5237
+ const directPath = join18(MESSAGE_STORAGE3, sessionID);
5265
5238
  if (existsSync13(directPath)) {
5266
5239
  return directPath;
5267
5240
  }
5268
5241
  for (const dir of readdirSync4(MESSAGE_STORAGE3)) {
5269
- const sessionPath = join17(MESSAGE_STORAGE3, dir, sessionID);
5242
+ const sessionPath = join18(MESSAGE_STORAGE3, dir, sessionID);
5270
5243
  if (existsSync13(sessionPath)) {
5271
5244
  return sessionPath;
5272
5245
  }
@@ -5290,14 +5263,14 @@ function findToolResultsBySize(sessionID) {
5290
5263
  const messageIds = getMessageIds(sessionID);
5291
5264
  const results = [];
5292
5265
  for (const messageID of messageIds) {
5293
- const partDir = join17(PART_STORAGE3, messageID);
5266
+ const partDir = join18(PART_STORAGE3, messageID);
5294
5267
  if (!existsSync13(partDir))
5295
5268
  continue;
5296
5269
  for (const file of readdirSync4(partDir)) {
5297
5270
  if (!file.endsWith(".json"))
5298
5271
  continue;
5299
5272
  try {
5300
- const partPath = join17(partDir, file);
5273
+ const partPath = join18(partDir, file);
5301
5274
  const content = readFileSync8(partPath, "utf-8");
5302
5275
  const part = JSON.parse(content);
5303
5276
  if (part.type === "tool" && part.state?.output && !part.truncated) {
@@ -5342,6 +5315,56 @@ function truncateToolResult(partPath) {
5342
5315
  return { success: false };
5343
5316
  }
5344
5317
  }
5318
+ function truncateUntilTargetTokens(sessionID, currentTokens, maxTokens, targetRatio = 0.8, charsPerToken = 4) {
5319
+ const targetTokens = Math.floor(maxTokens * targetRatio);
5320
+ const tokensToReduce = currentTokens - targetTokens;
5321
+ const charsToReduce = tokensToReduce * charsPerToken;
5322
+ if (tokensToReduce <= 0) {
5323
+ return {
5324
+ success: true,
5325
+ sufficient: true,
5326
+ truncatedCount: 0,
5327
+ totalBytesRemoved: 0,
5328
+ targetBytesToRemove: 0,
5329
+ truncatedTools: []
5330
+ };
5331
+ }
5332
+ const results = findToolResultsBySize(sessionID);
5333
+ if (results.length === 0) {
5334
+ return {
5335
+ success: false,
5336
+ sufficient: false,
5337
+ truncatedCount: 0,
5338
+ totalBytesRemoved: 0,
5339
+ targetBytesToRemove: charsToReduce,
5340
+ truncatedTools: []
5341
+ };
5342
+ }
5343
+ let totalRemoved = 0;
5344
+ let truncatedCount = 0;
5345
+ const truncatedTools = [];
5346
+ for (const result of results) {
5347
+ const truncateResult = truncateToolResult(result.partPath);
5348
+ if (truncateResult.success) {
5349
+ truncatedCount++;
5350
+ const removedSize = truncateResult.originalSize ?? result.outputSize;
5351
+ totalRemoved += removedSize;
5352
+ truncatedTools.push({
5353
+ toolName: truncateResult.toolName ?? result.toolName,
5354
+ originalSize: removedSize
5355
+ });
5356
+ }
5357
+ }
5358
+ const sufficient = totalRemoved >= charsToReduce;
5359
+ return {
5360
+ success: truncatedCount > 0,
5361
+ sufficient,
5362
+ truncatedCount,
5363
+ totalBytesRemoved: totalRemoved,
5364
+ targetBytesToRemove: charsToReduce,
5365
+ truncatedTools
5366
+ };
5367
+ }
5345
5368
 
5346
5369
  // src/hooks/anthropic-auto-compact/executor.ts
5347
5370
  function getOrCreateRetryState(autoCompactState, sessionID) {
@@ -5440,14 +5463,97 @@ function clearSessionState(autoCompactState, sessionID) {
5440
5463
  autoCompactState.retryStateBySession.delete(sessionID);
5441
5464
  autoCompactState.fallbackStateBySession.delete(sessionID);
5442
5465
  autoCompactState.truncateStateBySession.delete(sessionID);
5466
+ autoCompactState.emptyContentAttemptBySession.delete(sessionID);
5443
5467
  autoCompactState.compactionInProgress.delete(sessionID);
5444
5468
  }
5445
- async function executeCompact(sessionID, msg, autoCompactState, client, directory) {
5469
+ function getOrCreateEmptyContentAttempt(autoCompactState, sessionID) {
5470
+ return autoCompactState.emptyContentAttemptBySession.get(sessionID) ?? 0;
5471
+ }
5472
+ async function fixEmptyMessages(sessionID, autoCompactState, client) {
5473
+ const attempt = getOrCreateEmptyContentAttempt(autoCompactState, sessionID);
5474
+ autoCompactState.emptyContentAttemptBySession.set(sessionID, attempt + 1);
5475
+ const emptyMessageIds = findEmptyMessages(sessionID);
5476
+ if (emptyMessageIds.length === 0) {
5477
+ await client.tui.showToast({
5478
+ body: {
5479
+ title: "Empty Content Error",
5480
+ message: "No empty messages found in storage. Cannot auto-recover.",
5481
+ variant: "error",
5482
+ duration: 5000
5483
+ }
5484
+ }).catch(() => {});
5485
+ return false;
5486
+ }
5487
+ let fixed = false;
5488
+ for (const messageID of emptyMessageIds) {
5489
+ const success = injectTextPart(sessionID, messageID, "[user interrupted]");
5490
+ if (success)
5491
+ fixed = true;
5492
+ }
5493
+ if (fixed) {
5494
+ await client.tui.showToast({
5495
+ body: {
5496
+ title: "Session Recovery",
5497
+ message: `Fixed ${emptyMessageIds.length} empty messages. Retrying...`,
5498
+ variant: "warning",
5499
+ duration: 3000
5500
+ }
5501
+ }).catch(() => {});
5502
+ }
5503
+ return fixed;
5504
+ }
5505
+ async function executeCompact(sessionID, msg, autoCompactState, client, directory, experimental) {
5446
5506
  if (autoCompactState.compactionInProgress.has(sessionID)) {
5447
5507
  return;
5448
5508
  }
5449
5509
  autoCompactState.compactionInProgress.add(sessionID);
5510
+ const errorData = autoCompactState.errorDataBySession.get(sessionID);
5450
5511
  const truncateState = getOrCreateTruncateState(autoCompactState, sessionID);
5512
+ if (experimental?.aggressive_truncation && errorData?.currentTokens && errorData?.maxTokens && errorData.currentTokens > errorData.maxTokens && truncateState.truncateAttempt < TRUNCATE_CONFIG.maxTruncateAttempts) {
5513
+ log("[auto-compact] aggressive truncation triggered (experimental)", {
5514
+ currentTokens: errorData.currentTokens,
5515
+ maxTokens: errorData.maxTokens,
5516
+ targetRatio: TRUNCATE_CONFIG.targetTokenRatio
5517
+ });
5518
+ const aggressiveResult = truncateUntilTargetTokens(sessionID, errorData.currentTokens, errorData.maxTokens, TRUNCATE_CONFIG.targetTokenRatio, TRUNCATE_CONFIG.charsPerToken);
5519
+ if (aggressiveResult.truncatedCount > 0) {
5520
+ truncateState.truncateAttempt += aggressiveResult.truncatedCount;
5521
+ const toolNames = aggressiveResult.truncatedTools.map((t) => t.toolName).join(", ");
5522
+ const statusMsg = aggressiveResult.sufficient ? `Truncated ${aggressiveResult.truncatedCount} outputs (${formatBytes(aggressiveResult.totalBytesRemoved)})` : `Truncated ${aggressiveResult.truncatedCount} outputs (${formatBytes(aggressiveResult.totalBytesRemoved)}) but need ${formatBytes(aggressiveResult.targetBytesToRemove)}. Falling back to summarize/revert...`;
5523
+ await client.tui.showToast({
5524
+ body: {
5525
+ title: aggressiveResult.sufficient ? "Aggressive Truncation" : "Partial Truncation",
5526
+ message: `${statusMsg}: ${toolNames}`,
5527
+ variant: "warning",
5528
+ duration: 4000
5529
+ }
5530
+ }).catch(() => {});
5531
+ log("[auto-compact] aggressive truncation completed", aggressiveResult);
5532
+ if (aggressiveResult.sufficient) {
5533
+ autoCompactState.compactionInProgress.delete(sessionID);
5534
+ setTimeout(async () => {
5535
+ try {
5536
+ await client.session.prompt_async({
5537
+ path: { sessionID },
5538
+ body: { parts: [{ type: "text", text: "Continue" }] },
5539
+ query: { directory }
5540
+ });
5541
+ } catch {}
5542
+ }, 500);
5543
+ return;
5544
+ }
5545
+ } else {
5546
+ await client.tui.showToast({
5547
+ body: {
5548
+ title: "Truncation Skipped",
5549
+ message: "No tool outputs found to truncate.",
5550
+ variant: "warning",
5551
+ duration: 3000
5552
+ }
5553
+ }).catch(() => {});
5554
+ }
5555
+ }
5556
+ let skipSummarize = false;
5451
5557
  if (truncateState.truncateAttempt < TRUNCATE_CONFIG.maxTruncateAttempts) {
5452
5558
  const largest = findLargestToolResult(sessionID);
5453
5559
  if (largest && largest.outputSize >= TRUNCATE_CONFIG.minOutputSizeToTruncate) {
@@ -5475,10 +5581,58 @@ async function executeCompact(sessionID, msg, autoCompactState, client, director
5475
5581
  }, 500);
5476
5582
  return;
5477
5583
  }
5584
+ } else if (errorData?.currentTokens && errorData?.maxTokens && errorData.currentTokens > errorData.maxTokens) {
5585
+ skipSummarize = true;
5586
+ await client.tui.showToast({
5587
+ body: {
5588
+ title: "Summarize Skipped",
5589
+ message: `Over token limit (${errorData.currentTokens}/${errorData.maxTokens}) with nothing to truncate. Going to revert...`,
5590
+ variant: "warning",
5591
+ duration: 3000
5592
+ }
5593
+ }).catch(() => {});
5594
+ } else if (!errorData?.currentTokens) {
5595
+ await client.tui.showToast({
5596
+ body: {
5597
+ title: "Truncation Skipped",
5598
+ message: "No large tool outputs found.",
5599
+ variant: "warning",
5600
+ duration: 3000
5601
+ }
5602
+ }).catch(() => {});
5478
5603
  }
5479
5604
  }
5480
5605
  const retryState = getOrCreateRetryState(autoCompactState, sessionID);
5481
- if (retryState.attempt < RETRY_CONFIG.maxAttempts) {
5606
+ if (experimental?.empty_message_recovery && errorData?.errorType?.includes("non-empty content")) {
5607
+ const attempt = getOrCreateEmptyContentAttempt(autoCompactState, sessionID);
5608
+ if (attempt < 3) {
5609
+ const fixed = await fixEmptyMessages(sessionID, autoCompactState, client);
5610
+ if (fixed) {
5611
+ autoCompactState.compactionInProgress.delete(sessionID);
5612
+ setTimeout(() => {
5613
+ executeCompact(sessionID, msg, autoCompactState, client, directory, experimental);
5614
+ }, 500);
5615
+ return;
5616
+ }
5617
+ } else {
5618
+ await client.tui.showToast({
5619
+ body: {
5620
+ title: "Recovery Failed",
5621
+ message: "Max recovery attempts (3) reached for empty content error. Please start a new session.",
5622
+ variant: "error",
5623
+ duration: 1e4
5624
+ }
5625
+ }).catch(() => {});
5626
+ autoCompactState.compactionInProgress.delete(sessionID);
5627
+ return;
5628
+ }
5629
+ }
5630
+ if (Date.now() - retryState.lastAttemptTime > 300000) {
5631
+ retryState.attempt = 0;
5632
+ autoCompactState.fallbackStateBySession.delete(sessionID);
5633
+ autoCompactState.truncateStateBySession.delete(sessionID);
5634
+ }
5635
+ if (!skipSummarize && retryState.attempt < RETRY_CONFIG.maxAttempts) {
5482
5636
  retryState.attempt++;
5483
5637
  retryState.lastAttemptTime = Date.now();
5484
5638
  const providerID = msg.providerID;
@@ -5498,7 +5652,7 @@ async function executeCompact(sessionID, msg, autoCompactState, client, director
5498
5652
  body: { providerID, modelID },
5499
5653
  query: { directory }
5500
5654
  });
5501
- clearSessionState(autoCompactState, sessionID);
5655
+ autoCompactState.compactionInProgress.delete(sessionID);
5502
5656
  setTimeout(async () => {
5503
5657
  try {
5504
5658
  await client.session.prompt_async({
@@ -5514,10 +5668,19 @@ async function executeCompact(sessionID, msg, autoCompactState, client, director
5514
5668
  const delay = RETRY_CONFIG.initialDelayMs * Math.pow(RETRY_CONFIG.backoffFactor, retryState.attempt - 1);
5515
5669
  const cappedDelay = Math.min(delay, RETRY_CONFIG.maxDelayMs);
5516
5670
  setTimeout(() => {
5517
- executeCompact(sessionID, msg, autoCompactState, client, directory);
5671
+ executeCompact(sessionID, msg, autoCompactState, client, directory, experimental);
5518
5672
  }, cappedDelay);
5519
5673
  return;
5520
5674
  }
5675
+ } else {
5676
+ await client.tui.showToast({
5677
+ body: {
5678
+ title: "Summarize Skipped",
5679
+ message: "Missing providerID or modelID. Skipping to revert...",
5680
+ variant: "warning",
5681
+ duration: 3000
5682
+ }
5683
+ }).catch(() => {});
5521
5684
  }
5522
5685
  }
5523
5686
  const fallbackState = getOrCreateFallbackState(autoCompactState, sessionID);
@@ -5551,10 +5714,19 @@ async function executeCompact(sessionID, msg, autoCompactState, client, director
5551
5714
  truncateState.truncateAttempt = 0;
5552
5715
  autoCompactState.compactionInProgress.delete(sessionID);
5553
5716
  setTimeout(() => {
5554
- executeCompact(sessionID, msg, autoCompactState, client, directory);
5717
+ executeCompact(sessionID, msg, autoCompactState, client, directory, experimental);
5555
5718
  }, 1000);
5556
5719
  return;
5557
5720
  } catch {}
5721
+ } else {
5722
+ await client.tui.showToast({
5723
+ body: {
5724
+ title: "Revert Skipped",
5725
+ message: "Could not find last message pair to revert.",
5726
+ variant: "warning",
5727
+ duration: 3000
5728
+ }
5729
+ }).catch(() => {});
5558
5730
  }
5559
5731
  }
5560
5732
  clearSessionState(autoCompactState, sessionID);
@@ -5576,11 +5748,13 @@ function createAutoCompactState() {
5576
5748
  retryStateBySession: new Map,
5577
5749
  fallbackStateBySession: new Map,
5578
5750
  truncateStateBySession: new Map,
5751
+ emptyContentAttemptBySession: new Map,
5579
5752
  compactionInProgress: new Set
5580
5753
  };
5581
5754
  }
5582
- function createAnthropicAutoCompactHook(ctx) {
5755
+ function createAnthropicAutoCompactHook(ctx, options) {
5583
5756
  const autoCompactState = createAutoCompactState();
5757
+ const experimental = options?.experimental;
5584
5758
  const eventHandler = async ({ event }) => {
5585
5759
  const props = event.properties;
5586
5760
  if (event.type === "session.deleted") {
@@ -5591,15 +5765,18 @@ function createAnthropicAutoCompactHook(ctx) {
5591
5765
  autoCompactState.retryStateBySession.delete(sessionInfo.id);
5592
5766
  autoCompactState.fallbackStateBySession.delete(sessionInfo.id);
5593
5767
  autoCompactState.truncateStateBySession.delete(sessionInfo.id);
5768
+ autoCompactState.emptyContentAttemptBySession.delete(sessionInfo.id);
5594
5769
  autoCompactState.compactionInProgress.delete(sessionInfo.id);
5595
5770
  }
5596
5771
  return;
5597
5772
  }
5598
5773
  if (event.type === "session.error") {
5599
5774
  const sessionID = props?.sessionID;
5775
+ log("[auto-compact] session.error received", { sessionID, error: props?.error });
5600
5776
  if (!sessionID)
5601
5777
  return;
5602
5778
  const parsed = parseAnthropicTokenLimitError(props?.error);
5779
+ log("[auto-compact] parsed result", { parsed, hasError: !!props?.error });
5603
5780
  if (parsed) {
5604
5781
  autoCompactState.pendingCompact.add(sessionID);
5605
5782
  autoCompactState.errorDataBySession.set(sessionID, parsed);
@@ -5618,7 +5795,7 @@ function createAnthropicAutoCompactHook(ctx) {
5618
5795
  }
5619
5796
  }).catch(() => {});
5620
5797
  setTimeout(() => {
5621
- executeCompact(sessionID, { providerID, modelID }, autoCompactState, ctx.client, ctx.directory);
5798
+ executeCompact(sessionID, { providerID, modelID }, autoCompactState, ctx.client, ctx.directory, experimental);
5622
5799
  }, 300);
5623
5800
  }
5624
5801
  return;
@@ -5627,7 +5804,9 @@ function createAnthropicAutoCompactHook(ctx) {
5627
5804
  const info = props?.info;
5628
5805
  const sessionID = info?.sessionID;
5629
5806
  if (sessionID && info?.role === "assistant" && info.error) {
5807
+ log("[auto-compact] message.updated with error", { sessionID, error: info.error });
5630
5808
  const parsed = parseAnthropicTokenLimitError(info.error);
5809
+ log("[auto-compact] message.updated parsed result", { parsed });
5631
5810
  if (parsed) {
5632
5811
  parsed.providerID = info.providerID;
5633
5812
  parsed.modelID = info.modelID;
@@ -5659,7 +5838,7 @@ function createAnthropicAutoCompactHook(ctx) {
5659
5838
  duration: 3000
5660
5839
  }
5661
5840
  }).catch(() => {});
5662
- await executeCompact(sessionID, { providerID, modelID }, autoCompactState, ctx.client, ctx.directory);
5841
+ await executeCompact(sessionID, { providerID, modelID }, autoCompactState, ctx.client, ctx.directory, experimental);
5663
5842
  }
5664
5843
  };
5665
5844
  return {
@@ -5933,8 +6112,8 @@ function createThinkModeHook() {
5933
6112
  };
5934
6113
  }
5935
6114
  // src/hooks/claude-code-hooks/config.ts
5936
- import { homedir as homedir4 } from "os";
5937
- import { join as join18 } from "path";
6115
+ import { homedir as homedir6 } from "os";
6116
+ import { join as join19 } from "path";
5938
6117
  import { existsSync as existsSync14 } from "fs";
5939
6118
  function normalizeHookMatcher(raw) {
5940
6119
  return {
@@ -5958,11 +6137,11 @@ function normalizeHooksConfig(raw) {
5958
6137
  return result;
5959
6138
  }
5960
6139
  function getClaudeSettingsPaths(customPath) {
5961
- const home = homedir4();
6140
+ const home = homedir6();
5962
6141
  const paths = [
5963
- join18(home, ".claude", "settings.json"),
5964
- join18(process.cwd(), ".claude", "settings.json"),
5965
- join18(process.cwd(), ".claude", "settings.local.json")
6142
+ join19(home, ".claude", "settings.json"),
6143
+ join19(process.cwd(), ".claude", "settings.json"),
6144
+ join19(process.cwd(), ".claude", "settings.local.json")
5966
6145
  ];
5967
6146
  if (customPath && existsSync14(customPath)) {
5968
6147
  paths.unshift(customPath);
@@ -6006,21 +6185,21 @@ async function loadClaudeHooksConfig(customSettingsPath) {
6006
6185
 
6007
6186
  // src/hooks/claude-code-hooks/config-loader.ts
6008
6187
  import { existsSync as existsSync15 } from "fs";
6009
- import { homedir as homedir5 } from "os";
6010
- import { join as join19 } from "path";
6011
- var USER_CONFIG_PATH = join19(homedir5(), ".config", "opencode", "opencode-cc-plugin.json");
6188
+ import { homedir as homedir7 } from "os";
6189
+ import { join as join20 } from "path";
6190
+ var USER_CONFIG_PATH = join20(homedir7(), ".config", "opencode", "opencode-cc-plugin.json");
6012
6191
  function getProjectConfigPath() {
6013
- return join19(process.cwd(), ".opencode", "opencode-cc-plugin.json");
6192
+ return join20(process.cwd(), ".opencode", "opencode-cc-plugin.json");
6014
6193
  }
6015
- async function loadConfigFromPath(path3) {
6016
- if (!existsSync15(path3)) {
6194
+ async function loadConfigFromPath(path4) {
6195
+ if (!existsSync15(path4)) {
6017
6196
  return null;
6018
6197
  }
6019
6198
  try {
6020
- const content = await Bun.file(path3).text();
6199
+ const content = await Bun.file(path4).text();
6021
6200
  return JSON.parse(content);
6022
6201
  } catch (error) {
6023
- log("Failed to load config", { path: path3, error });
6202
+ log("Failed to load config", { path: path4, error });
6024
6203
  return null;
6025
6204
  }
6026
6205
  }
@@ -6192,13 +6371,13 @@ async function executePreToolUseHooks(ctx, config, extendedConfig) {
6192
6371
  }
6193
6372
 
6194
6373
  // src/hooks/claude-code-hooks/transcript.ts
6195
- import { join as join20 } from "path";
6374
+ import { join as join21 } from "path";
6196
6375
  import { mkdirSync as mkdirSync6, appendFileSync as appendFileSync5, existsSync as existsSync16, writeFileSync as writeFileSync6, unlinkSync as unlinkSync5 } from "fs";
6197
- import { homedir as homedir6, tmpdir as tmpdir5 } from "os";
6376
+ import { homedir as homedir8, tmpdir as tmpdir5 } from "os";
6198
6377
  import { randomUUID } from "crypto";
6199
- var TRANSCRIPT_DIR = join20(homedir6(), ".claude", "transcripts");
6378
+ var TRANSCRIPT_DIR = join21(homedir8(), ".claude", "transcripts");
6200
6379
  function getTranscriptPath(sessionId) {
6201
- return join20(TRANSCRIPT_DIR, `${sessionId}.jsonl`);
6380
+ return join21(TRANSCRIPT_DIR, `${sessionId}.jsonl`);
6202
6381
  }
6203
6382
  function ensureTranscriptDir() {
6204
6383
  if (!existsSync16(TRANSCRIPT_DIR)) {
@@ -6207,10 +6386,10 @@ function ensureTranscriptDir() {
6207
6386
  }
6208
6387
  function appendTranscriptEntry(sessionId, entry) {
6209
6388
  ensureTranscriptDir();
6210
- const path3 = getTranscriptPath(sessionId);
6389
+ const path4 = getTranscriptPath(sessionId);
6211
6390
  const line = JSON.stringify(entry) + `
6212
6391
  `;
6213
- appendFileSync5(path3, line);
6392
+ appendFileSync5(path4, line);
6214
6393
  }
6215
6394
  function recordToolUse(sessionId, toolName, toolInput) {
6216
6395
  appendTranscriptEntry(sessionId, {
@@ -6288,7 +6467,7 @@ async function buildTranscriptFromSession(client, sessionId, directory, currentT
6288
6467
  }
6289
6468
  };
6290
6469
  entries.push(JSON.stringify(currentEntry));
6291
- const tempPath = join20(tmpdir5(), `opencode-transcript-${sessionId}-${randomUUID()}.jsonl`);
6470
+ const tempPath = join21(tmpdir5(), `opencode-transcript-${sessionId}-${randomUUID()}.jsonl`);
6292
6471
  writeFileSync6(tempPath, entries.join(`
6293
6472
  `) + `
6294
6473
  `);
@@ -6308,7 +6487,7 @@ async function buildTranscriptFromSession(client, sessionId, directory, currentT
6308
6487
  ]
6309
6488
  }
6310
6489
  };
6311
- const tempPath = join20(tmpdir5(), `opencode-transcript-${sessionId}-${randomUUID()}.jsonl`);
6490
+ const tempPath = join21(tmpdir5(), `opencode-transcript-${sessionId}-${randomUUID()}.jsonl`);
6312
6491
  writeFileSync6(tempPath, JSON.stringify(currentEntry) + `
6313
6492
  `);
6314
6493
  return tempPath;
@@ -6317,11 +6496,11 @@ async function buildTranscriptFromSession(client, sessionId, directory, currentT
6317
6496
  }
6318
6497
  }
6319
6498
  }
6320
- function deleteTempTranscript(path3) {
6321
- if (!path3)
6499
+ function deleteTempTranscript(path4) {
6500
+ if (!path4)
6322
6501
  return;
6323
6502
  try {
6324
- unlinkSync5(path3);
6503
+ unlinkSync5(path4);
6325
6504
  } catch {}
6326
6505
  }
6327
6506
 
@@ -6520,11 +6699,11 @@ ${USER_PROMPT_SUBMIT_TAG_CLOSE}`);
6520
6699
  }
6521
6700
 
6522
6701
  // src/hooks/claude-code-hooks/todo.ts
6523
- import { join as join21 } from "path";
6524
- import { homedir as homedir7 } from "os";
6525
- var TODO_DIR = join21(homedir7(), ".claude", "todos");
6702
+ import { join as join22 } from "path";
6703
+ import { homedir as homedir9 } from "os";
6704
+ var TODO_DIR = join22(homedir9(), ".claude", "todos");
6526
6705
  function getTodoPath(sessionId) {
6527
- return join21(TODO_DIR, `${sessionId}-agent-${sessionId}.json`);
6706
+ return join22(TODO_DIR, `${sessionId}-agent-${sessionId}.json`);
6528
6707
  }
6529
6708
 
6530
6709
  // src/hooks/claude-code-hooks/stop.ts
@@ -6857,7 +7036,7 @@ ${result.message}`;
6857
7036
  }
6858
7037
  // src/hooks/rules-injector/index.ts
6859
7038
  import { readFileSync as readFileSync10 } from "fs";
6860
- import { homedir as homedir8 } from "os";
7039
+ import { homedir as homedir10 } from "os";
6861
7040
  import { relative as relative3, resolve as resolve4 } from "path";
6862
7041
 
6863
7042
  // src/hooks/rules-injector/finder.ts
@@ -6867,12 +7046,12 @@ import {
6867
7046
  realpathSync,
6868
7047
  statSync as statSync2
6869
7048
  } from "fs";
6870
- import { dirname as dirname4, join as join23, relative } from "path";
7049
+ import { dirname as dirname4, join as join24, relative } from "path";
6871
7050
 
6872
7051
  // src/hooks/rules-injector/constants.ts
6873
- import { join as join22 } from "path";
6874
- var OPENCODE_STORAGE6 = join22(xdgData2 ?? "", "opencode", "storage");
6875
- var RULES_INJECTOR_STORAGE = join22(OPENCODE_STORAGE6, "rules-injector");
7052
+ import { join as join23 } from "path";
7053
+ var OPENCODE_STORAGE6 = join23(xdgData2 ?? "", "opencode", "storage");
7054
+ var RULES_INJECTOR_STORAGE = join23(OPENCODE_STORAGE6, "rules-injector");
6876
7055
  var PROJECT_MARKERS = [
6877
7056
  ".git",
6878
7057
  "pyproject.toml",
@@ -6899,7 +7078,7 @@ function findProjectRoot(startPath) {
6899
7078
  }
6900
7079
  while (true) {
6901
7080
  for (const marker of PROJECT_MARKERS) {
6902
- const markerPath = join23(current, marker);
7081
+ const markerPath = join24(current, marker);
6903
7082
  if (existsSync17(markerPath)) {
6904
7083
  return current;
6905
7084
  }
@@ -6917,7 +7096,7 @@ function findRuleFilesRecursive(dir, results) {
6917
7096
  try {
6918
7097
  const entries = readdirSync5(dir, { withFileTypes: true });
6919
7098
  for (const entry of entries) {
6920
- const fullPath = join23(dir, entry.name);
7099
+ const fullPath = join24(dir, entry.name);
6921
7100
  if (entry.isDirectory()) {
6922
7101
  findRuleFilesRecursive(fullPath, results);
6923
7102
  } else if (entry.isFile()) {
@@ -6943,7 +7122,7 @@ function findRuleFiles(projectRoot, homeDir, currentFile) {
6943
7122
  let distance = 0;
6944
7123
  while (true) {
6945
7124
  for (const [parent, subdir] of PROJECT_RULE_SUBDIRS) {
6946
- const ruleDir = join23(currentDir, parent, subdir);
7125
+ const ruleDir = join24(currentDir, parent, subdir);
6947
7126
  const files = [];
6948
7127
  findRuleFilesRecursive(ruleDir, files);
6949
7128
  for (const filePath of files) {
@@ -6967,7 +7146,7 @@ function findRuleFiles(projectRoot, homeDir, currentFile) {
6967
7146
  currentDir = parentDir;
6968
7147
  distance++;
6969
7148
  }
6970
- const userRuleDir = join23(homeDir, USER_RULE_DIR);
7149
+ const userRuleDir = join24(homeDir, USER_RULE_DIR);
6971
7150
  const userFiles = [];
6972
7151
  findRuleFilesRecursive(userRuleDir, userFiles);
6973
7152
  for (const filePath of userFiles) {
@@ -7162,9 +7341,9 @@ import {
7162
7341
  writeFileSync as writeFileSync7,
7163
7342
  unlinkSync as unlinkSync6
7164
7343
  } from "fs";
7165
- import { join as join24 } from "path";
7344
+ import { join as join25 } from "path";
7166
7345
  function getStoragePath3(sessionID) {
7167
- return join24(RULES_INJECTOR_STORAGE, `${sessionID}.json`);
7346
+ return join25(RULES_INJECTOR_STORAGE, `${sessionID}.json`);
7168
7347
  }
7169
7348
  function loadInjectedRules(sessionID) {
7170
7349
  const filePath = getStoragePath3(sessionID);
@@ -7225,7 +7404,7 @@ function createRulesInjectorHook(ctx) {
7225
7404
  return;
7226
7405
  const projectRoot = findProjectRoot(filePath);
7227
7406
  const cache2 = getSessionCache(input.sessionID);
7228
- const home = homedir8();
7407
+ const home = homedir10();
7229
7408
  const ruleFileCandidates = findRuleFiles(projectRoot, home, filePath);
7230
7409
  const toInject = [];
7231
7410
  for (const candidate of ruleFileCandidates) {
@@ -7296,33 +7475,33 @@ function createBackgroundNotificationHook(manager) {
7296
7475
  }
7297
7476
  // src/hooks/auto-update-checker/checker.ts
7298
7477
  import * as fs4 from "fs";
7299
- import * as path4 from "path";
7478
+ import * as path5 from "path";
7300
7479
  import { fileURLToPath } from "url";
7301
7480
 
7302
7481
  // src/hooks/auto-update-checker/constants.ts
7303
- import * as path3 from "path";
7304
- import * as os3 from "os";
7482
+ import * as path4 from "path";
7483
+ import * as os4 from "os";
7305
7484
  var PACKAGE_NAME = "oh-my-opencode";
7306
7485
  var NPM_REGISTRY_URL = `https://registry.npmjs.org/-/package/${PACKAGE_NAME}/dist-tags`;
7307
7486
  var NPM_FETCH_TIMEOUT = 5000;
7308
7487
  function getCacheDir2() {
7309
7488
  if (process.platform === "win32") {
7310
- return path3.join(process.env.LOCALAPPDATA ?? os3.homedir(), "opencode");
7489
+ return path4.join(process.env.LOCALAPPDATA ?? os4.homedir(), "opencode");
7311
7490
  }
7312
- return path3.join(os3.homedir(), ".cache", "opencode");
7491
+ return path4.join(os4.homedir(), ".cache", "opencode");
7313
7492
  }
7314
7493
  var CACHE_DIR = getCacheDir2();
7315
- var VERSION_FILE = path3.join(CACHE_DIR, "version");
7316
- var INSTALLED_PACKAGE_JSON = path3.join(CACHE_DIR, "node_modules", PACKAGE_NAME, "package.json");
7317
- function getUserConfigDir() {
7494
+ var VERSION_FILE = path4.join(CACHE_DIR, "version");
7495
+ var INSTALLED_PACKAGE_JSON = path4.join(CACHE_DIR, "node_modules", PACKAGE_NAME, "package.json");
7496
+ function getUserConfigDir2() {
7318
7497
  if (process.platform === "win32") {
7319
- return process.env.APPDATA ?? path3.join(os3.homedir(), "AppData", "Roaming");
7498
+ return process.env.APPDATA ?? path4.join(os4.homedir(), "AppData", "Roaming");
7320
7499
  }
7321
- return process.env.XDG_CONFIG_HOME ?? path3.join(os3.homedir(), ".config");
7500
+ return process.env.XDG_CONFIG_HOME ?? path4.join(os4.homedir(), ".config");
7322
7501
  }
7323
- var USER_CONFIG_DIR = getUserConfigDir();
7324
- var USER_OPENCODE_CONFIG = path3.join(USER_CONFIG_DIR, "opencode", "opencode.json");
7325
- var USER_OPENCODE_CONFIG_JSONC = path3.join(USER_CONFIG_DIR, "opencode", "opencode.jsonc");
7502
+ var USER_CONFIG_DIR = getUserConfigDir2();
7503
+ var USER_OPENCODE_CONFIG = path4.join(USER_CONFIG_DIR, "opencode", "opencode.json");
7504
+ var USER_OPENCODE_CONFIG_JSONC = path4.join(USER_CONFIG_DIR, "opencode", "opencode.jsonc");
7326
7505
 
7327
7506
  // src/hooks/auto-update-checker/checker.ts
7328
7507
  function isLocalDevMode(directory) {
@@ -7333,8 +7512,8 @@ function stripJsonComments(json) {
7333
7512
  }
7334
7513
  function getConfigPaths(directory) {
7335
7514
  return [
7336
- path4.join(directory, ".opencode", "opencode.json"),
7337
- path4.join(directory, ".opencode", "opencode.jsonc"),
7515
+ path5.join(directory, ".opencode", "opencode.json"),
7516
+ path5.join(directory, ".opencode", "opencode.jsonc"),
7338
7517
  USER_OPENCODE_CONFIG,
7339
7518
  USER_OPENCODE_CONFIG_JSONC
7340
7519
  ];
@@ -7365,9 +7544,9 @@ function getLocalDevPath(directory) {
7365
7544
  function findPackageJsonUp(startPath) {
7366
7545
  try {
7367
7546
  const stat = fs4.statSync(startPath);
7368
- let dir = stat.isDirectory() ? startPath : path4.dirname(startPath);
7547
+ let dir = stat.isDirectory() ? startPath : path5.dirname(startPath);
7369
7548
  for (let i = 0;i < 10; i++) {
7370
- const pkgPath = path4.join(dir, "package.json");
7549
+ const pkgPath = path5.join(dir, "package.json");
7371
7550
  if (fs4.existsSync(pkgPath)) {
7372
7551
  try {
7373
7552
  const content = fs4.readFileSync(pkgPath, "utf-8");
@@ -7376,7 +7555,7 @@ function findPackageJsonUp(startPath) {
7376
7555
  return pkgPath;
7377
7556
  } catch {}
7378
7557
  }
7379
- const parent = path4.dirname(dir);
7558
+ const parent = path5.dirname(dir);
7380
7559
  if (parent === dir)
7381
7560
  break;
7382
7561
  dir = parent;
@@ -7433,7 +7612,7 @@ function getCachedVersion() {
7433
7612
  }
7434
7613
  } catch {}
7435
7614
  try {
7436
- const currentDir = path4.dirname(fileURLToPath(import.meta.url));
7615
+ const currentDir = path5.dirname(fileURLToPath(import.meta.url));
7437
7616
  const pkgPath = findPackageJsonUp(currentDir);
7438
7617
  if (pkgPath) {
7439
7618
  const content = fs4.readFileSync(pkgPath, "utf-8");
@@ -7495,13 +7674,42 @@ async function checkForUpdate(directory) {
7495
7674
 
7496
7675
  // src/hooks/auto-update-checker/cache.ts
7497
7676
  import * as fs5 from "fs";
7498
- import * as path5 from "path";
7677
+ import * as path6 from "path";
7678
+ function stripTrailingCommas(json) {
7679
+ return json.replace(/,(\s*[}\]])/g, "$1");
7680
+ }
7681
+ function removeFromBunLock(packageName) {
7682
+ const lockPath = path6.join(CACHE_DIR, "bun.lock");
7683
+ if (!fs5.existsSync(lockPath))
7684
+ return false;
7685
+ try {
7686
+ const content = fs5.readFileSync(lockPath, "utf-8");
7687
+ const lock = JSON.parse(stripTrailingCommas(content));
7688
+ let modified = false;
7689
+ if (lock.workspaces?.[""]?.dependencies?.[packageName]) {
7690
+ delete lock.workspaces[""].dependencies[packageName];
7691
+ modified = true;
7692
+ }
7693
+ if (lock.packages?.[packageName]) {
7694
+ delete lock.packages[packageName];
7695
+ modified = true;
7696
+ }
7697
+ if (modified) {
7698
+ fs5.writeFileSync(lockPath, JSON.stringify(lock, null, 2));
7699
+ log(`[auto-update-checker] Removed from bun.lock: ${packageName}`);
7700
+ }
7701
+ return modified;
7702
+ } catch {
7703
+ return false;
7704
+ }
7705
+ }
7499
7706
  function invalidatePackage(packageName = PACKAGE_NAME) {
7500
7707
  try {
7501
- const pkgDir = path5.join(CACHE_DIR, "node_modules", packageName);
7502
- const pkgJsonPath = path5.join(CACHE_DIR, "package.json");
7708
+ const pkgDir = path6.join(CACHE_DIR, "node_modules", packageName);
7709
+ const pkgJsonPath = path6.join(CACHE_DIR, "package.json");
7503
7710
  let packageRemoved = false;
7504
7711
  let dependencyRemoved = false;
7712
+ let lockRemoved = false;
7505
7713
  if (fs5.existsSync(pkgDir)) {
7506
7714
  fs5.rmSync(pkgDir, { recursive: true, force: true });
7507
7715
  log(`[auto-update-checker] Package removed: ${pkgDir}`);
@@ -7517,7 +7725,8 @@ function invalidatePackage(packageName = PACKAGE_NAME) {
7517
7725
  dependencyRemoved = true;
7518
7726
  }
7519
7727
  }
7520
- if (!packageRemoved && !dependencyRemoved) {
7728
+ lockRemoved = removeFromBunLock(packageName);
7729
+ if (!packageRemoved && !dependencyRemoved && !lockRemoved) {
7521
7730
  log(`[auto-update-checker] Package not found, nothing to invalidate: ${packageName}`);
7522
7731
  return false;
7523
7732
  }
@@ -7530,7 +7739,27 @@ function invalidatePackage(packageName = PACKAGE_NAME) {
7530
7739
 
7531
7740
  // src/hooks/auto-update-checker/index.ts
7532
7741
  function createAutoUpdateCheckerHook(ctx, options = {}) {
7533
- const { showStartupToast = true } = options;
7742
+ const { showStartupToast = true, isSisyphusEnabled = false } = options;
7743
+ const getToastMessage = (isUpdate, latestVersion) => {
7744
+ if (isSisyphusEnabled) {
7745
+ return isUpdate ? `Sisyphus on steroids is steering OpenCode.
7746
+ v${latestVersion} available. Restart to apply.` : `Sisyphus on steroids is steering OpenCode.`;
7747
+ }
7748
+ return isUpdate ? `OpenCode is now on Steroids. oMoMoMoMo...
7749
+ v${latestVersion} available. Restart OpenCode to apply.` : `OpenCode is now on Steroids. oMoMoMoMo...`;
7750
+ };
7751
+ const showVersionToast = async (version) => {
7752
+ const displayVersion = version ?? "unknown";
7753
+ await ctx.client.tui.showToast({
7754
+ body: {
7755
+ title: `OhMyOpenCode ${displayVersion}`,
7756
+ message: getToastMessage(false),
7757
+ variant: "info",
7758
+ duration: 5000
7759
+ }
7760
+ }).catch(() => {});
7761
+ log(`[auto-update-checker] Startup toast shown: v${displayVersion}`);
7762
+ };
7534
7763
  let hasChecked = false;
7535
7764
  return {
7536
7765
  event: async ({ event }) => {
@@ -7548,21 +7777,21 @@ function createAutoUpdateCheckerHook(ctx, options = {}) {
7548
7777
  log("[auto-update-checker] Skipped: local development mode");
7549
7778
  if (showStartupToast) {
7550
7779
  const version = getLocalDevVersion(ctx.directory) ?? getCachedVersion();
7551
- await showVersionToast(ctx, version);
7780
+ await showVersionToast(version);
7552
7781
  }
7553
7782
  return;
7554
7783
  }
7555
7784
  if (result.isPinned) {
7556
7785
  log(`[auto-update-checker] Skipped: version pinned to ${result.currentVersion}`);
7557
7786
  if (showStartupToast) {
7558
- await showVersionToast(ctx, result.currentVersion);
7787
+ await showVersionToast(result.currentVersion);
7559
7788
  }
7560
7789
  return;
7561
7790
  }
7562
7791
  if (!result.needsUpdate) {
7563
7792
  log("[auto-update-checker] No update needed");
7564
7793
  if (showStartupToast) {
7565
- await showVersionToast(ctx, result.currentVersion);
7794
+ await showVersionToast(result.currentVersion);
7566
7795
  }
7567
7796
  return;
7568
7797
  }
@@ -7570,8 +7799,7 @@ function createAutoUpdateCheckerHook(ctx, options = {}) {
7570
7799
  await ctx.client.tui.showToast({
7571
7800
  body: {
7572
7801
  title: `OhMyOpenCode ${result.latestVersion}`,
7573
- message: `OpenCode is now on Steroids. oMoMoMoMo...
7574
- v${result.latestVersion} available. Restart OpenCode to apply.`,
7802
+ message: getToastMessage(true, result.latestVersion ?? undefined),
7575
7803
  variant: "info",
7576
7804
  duration: 8000
7577
7805
  }
@@ -7580,20 +7808,27 @@ v${result.latestVersion} available. Restart OpenCode to apply.`,
7580
7808
  } catch (err) {
7581
7809
  log("[auto-update-checker] Error during update check:", err);
7582
7810
  }
7811
+ await showConfigErrorsIfAny(ctx);
7583
7812
  }
7584
7813
  };
7585
7814
  }
7586
- async function showVersionToast(ctx, version) {
7587
- const displayVersion = version ?? "unknown";
7815
+ async function showConfigErrorsIfAny(ctx) {
7816
+ const errors = getConfigLoadErrors();
7817
+ if (errors.length === 0)
7818
+ return;
7819
+ const errorMessages = errors.map((e) => `${e.path}: ${e.error}`).join(`
7820
+ `);
7588
7821
  await ctx.client.tui.showToast({
7589
7822
  body: {
7590
- title: `OhMyOpenCode ${displayVersion}`,
7591
- message: "OpenCode is now on Steroids. oMoMoMoMo...",
7592
- variant: "info",
7593
- duration: 5000
7823
+ title: "Config Load Error",
7824
+ message: `Failed to load config:
7825
+ ${errorMessages}`,
7826
+ variant: "error",
7827
+ duration: 1e4
7594
7828
  }
7595
7829
  }).catch(() => {});
7596
- log(`[auto-update-checker] Startup toast shown: v${displayVersion}`);
7830
+ log(`[auto-update-checker] Config load errors shown: ${errors.length} error(s)`);
7831
+ clearConfigLoadErrors();
7597
7832
  }
7598
7833
  // src/hooks/agent-usage-reminder/storage.ts
7599
7834
  import {
@@ -7603,12 +7838,12 @@ import {
7603
7838
  writeFileSync as writeFileSync9,
7604
7839
  unlinkSync as unlinkSync7
7605
7840
  } from "fs";
7606
- import { join as join29 } from "path";
7841
+ import { join as join30 } from "path";
7607
7842
 
7608
7843
  // src/hooks/agent-usage-reminder/constants.ts
7609
- import { join as join28 } from "path";
7610
- var OPENCODE_STORAGE7 = join28(xdgData2 ?? "", "opencode", "storage");
7611
- var AGENT_USAGE_REMINDER_STORAGE = join28(OPENCODE_STORAGE7, "agent-usage-reminder");
7844
+ import { join as join29 } from "path";
7845
+ var OPENCODE_STORAGE7 = join29(xdgData2 ?? "", "opencode", "storage");
7846
+ var AGENT_USAGE_REMINDER_STORAGE = join29(OPENCODE_STORAGE7, "agent-usage-reminder");
7612
7847
  var TARGET_TOOLS = new Set([
7613
7848
  "grep",
7614
7849
  "safe_grep",
@@ -7653,7 +7888,7 @@ ALWAYS prefer: Multiple parallel background_task calls > Direct tool calls
7653
7888
 
7654
7889
  // src/hooks/agent-usage-reminder/storage.ts
7655
7890
  function getStoragePath4(sessionID) {
7656
- return join29(AGENT_USAGE_REMINDER_STORAGE, `${sessionID}.json`);
7891
+ return join30(AGENT_USAGE_REMINDER_STORAGE, `${sessionID}.json`);
7657
7892
  }
7658
7893
  function loadAgentUsageState(sessionID) {
7659
7894
  const filePath = getStoragePath4(sessionID);
@@ -7888,12 +8123,12 @@ import {
7888
8123
  writeFileSync as writeFileSync10,
7889
8124
  unlinkSync as unlinkSync8
7890
8125
  } from "fs";
7891
- import { join as join31 } from "path";
8126
+ import { join as join32 } from "path";
7892
8127
 
7893
8128
  // src/hooks/interactive-bash-session/constants.ts
7894
- import { join as join30 } from "path";
7895
- var OPENCODE_STORAGE8 = join30(xdgData2 ?? "", "opencode", "storage");
7896
- var INTERACTIVE_BASH_SESSION_STORAGE = join30(OPENCODE_STORAGE8, "interactive-bash-session");
8129
+ import { join as join31 } from "path";
8130
+ var OPENCODE_STORAGE8 = join31(xdgData2 ?? "", "opencode", "storage");
8131
+ var INTERACTIVE_BASH_SESSION_STORAGE = join31(OPENCODE_STORAGE8, "interactive-bash-session");
7897
8132
  var OMO_SESSION_PREFIX = "omo-";
7898
8133
  function buildSessionReminderMessage(sessions) {
7899
8134
  if (sessions.length === 0)
@@ -7905,7 +8140,7 @@ function buildSessionReminderMessage(sessions) {
7905
8140
 
7906
8141
  // src/hooks/interactive-bash-session/storage.ts
7907
8142
  function getStoragePath5(sessionID) {
7908
- return join31(INTERACTIVE_BASH_SESSION_STORAGE, `${sessionID}.json`);
8143
+ return join32(INTERACTIVE_BASH_SESSION_STORAGE, `${sessionID}.json`);
7909
8144
  }
7910
8145
  function loadInteractiveBashSessionState(sessionID) {
7911
8146
  const filePath = getStoragePath5(sessionID);
@@ -8115,7 +8350,7 @@ function createInteractiveBashSessionHook(_ctx) {
8115
8350
  };
8116
8351
  }
8117
8352
  // src/hooks/empty-message-sanitizer/index.ts
8118
- var PLACEHOLDER_TEXT2 = "[user interrupted]";
8353
+ var PLACEHOLDER_TEXT = "[user interrupted]";
8119
8354
  function hasTextContent(part) {
8120
8355
  if (part.type === "text") {
8121
8356
  const text = part.text;
@@ -8144,7 +8379,7 @@ function createEmptyMessageSanitizerHook() {
8144
8379
  if (part.type === "text") {
8145
8380
  const textPart = part;
8146
8381
  if (!textPart.text || !textPart.text.trim()) {
8147
- textPart.text = PLACEHOLDER_TEXT2;
8382
+ textPart.text = PLACEHOLDER_TEXT;
8148
8383
  textPart.synthetic = true;
8149
8384
  injected = true;
8150
8385
  break;
@@ -8158,7 +8393,7 @@ function createEmptyMessageSanitizerHook() {
8158
8393
  messageID: message.info.id,
8159
8394
  sessionID: message.info.sessionID ?? "",
8160
8395
  type: "text",
8161
- text: PLACEHOLDER_TEXT2,
8396
+ text: PLACEHOLDER_TEXT,
8162
8397
  synthetic: true
8163
8398
  };
8164
8399
  if (insertIndex === -1) {
@@ -8172,7 +8407,7 @@ function createEmptyMessageSanitizerHook() {
8172
8407
  if (part.type === "text") {
8173
8408
  const textPart = part;
8174
8409
  if (textPart.text !== undefined && textPart.text.trim() === "") {
8175
- textPart.text = PLACEHOLDER_TEXT2;
8410
+ textPart.text = PLACEHOLDER_TEXT;
8176
8411
  textPart.synthetic = true;
8177
8412
  }
8178
8413
  }
@@ -9882,8 +10117,8 @@ async function createGoogleAntigravityAuthPlugin({
9882
10117
  }
9883
10118
  // src/features/claude-code-command-loader/loader.ts
9884
10119
  import { existsSync as existsSync23, readdirSync as readdirSync6, readFileSync as readFileSync15 } from "fs";
9885
- import { homedir as homedir10 } from "os";
9886
- import { join as join32, basename } from "path";
10120
+ import { homedir as homedir12 } from "os";
10121
+ import { join as join33, basename } from "path";
9887
10122
  function loadCommandsFromDir(commandsDir, scope) {
9888
10123
  if (!existsSync23(commandsDir)) {
9889
10124
  return [];
@@ -9893,7 +10128,7 @@ function loadCommandsFromDir(commandsDir, scope) {
9893
10128
  for (const entry of entries) {
9894
10129
  if (!isMarkdownFile(entry))
9895
10130
  continue;
9896
- const commandPath = join32(commandsDir, entry.name);
10131
+ const commandPath = join33(commandsDir, entry.name);
9897
10132
  const commandName = basename(entry.name, ".md");
9898
10133
  try {
9899
10134
  const content = readFileSync15(commandPath, "utf-8");
@@ -9936,29 +10171,29 @@ function commandsToRecord(commands) {
9936
10171
  return result;
9937
10172
  }
9938
10173
  function loadUserCommands() {
9939
- const userCommandsDir = join32(homedir10(), ".claude", "commands");
10174
+ const userCommandsDir = join33(homedir12(), ".claude", "commands");
9940
10175
  const commands = loadCommandsFromDir(userCommandsDir, "user");
9941
10176
  return commandsToRecord(commands);
9942
10177
  }
9943
10178
  function loadProjectCommands() {
9944
- const projectCommandsDir = join32(process.cwd(), ".claude", "commands");
10179
+ const projectCommandsDir = join33(process.cwd(), ".claude", "commands");
9945
10180
  const commands = loadCommandsFromDir(projectCommandsDir, "project");
9946
10181
  return commandsToRecord(commands);
9947
10182
  }
9948
10183
  function loadOpencodeGlobalCommands() {
9949
- const opencodeCommandsDir = join32(homedir10(), ".config", "opencode", "command");
10184
+ const opencodeCommandsDir = join33(homedir12(), ".config", "opencode", "command");
9950
10185
  const commands = loadCommandsFromDir(opencodeCommandsDir, "opencode");
9951
10186
  return commandsToRecord(commands);
9952
10187
  }
9953
10188
  function loadOpencodeProjectCommands() {
9954
- const opencodeProjectDir = join32(process.cwd(), ".opencode", "command");
10189
+ const opencodeProjectDir = join33(process.cwd(), ".opencode", "command");
9955
10190
  const commands = loadCommandsFromDir(opencodeProjectDir, "opencode-project");
9956
10191
  return commandsToRecord(commands);
9957
10192
  }
9958
10193
  // src/features/claude-code-skill-loader/loader.ts
9959
10194
  import { existsSync as existsSync24, readdirSync as readdirSync7, readFileSync as readFileSync16 } from "fs";
9960
- import { homedir as homedir11 } from "os";
9961
- import { join as join33 } from "path";
10195
+ import { homedir as homedir13 } from "os";
10196
+ import { join as join34 } from "path";
9962
10197
  function loadSkillsFromDir(skillsDir, scope) {
9963
10198
  if (!existsSync24(skillsDir)) {
9964
10199
  return [];
@@ -9968,11 +10203,11 @@ function loadSkillsFromDir(skillsDir, scope) {
9968
10203
  for (const entry of entries) {
9969
10204
  if (entry.name.startsWith("."))
9970
10205
  continue;
9971
- const skillPath = join33(skillsDir, entry.name);
10206
+ const skillPath = join34(skillsDir, entry.name);
9972
10207
  if (!entry.isDirectory() && !entry.isSymbolicLink())
9973
10208
  continue;
9974
10209
  const resolvedPath = resolveSymlink(skillPath);
9975
- const skillMdPath = join33(resolvedPath, "SKILL.md");
10210
+ const skillMdPath = join34(resolvedPath, "SKILL.md");
9976
10211
  if (!existsSync24(skillMdPath))
9977
10212
  continue;
9978
10213
  try {
@@ -10010,7 +10245,7 @@ $ARGUMENTS
10010
10245
  return skills;
10011
10246
  }
10012
10247
  function loadUserSkillsAsCommands() {
10013
- const userSkillsDir = join33(homedir11(), ".claude", "skills");
10248
+ const userSkillsDir = join34(homedir13(), ".claude", "skills");
10014
10249
  const skills = loadSkillsFromDir(userSkillsDir, "user");
10015
10250
  return skills.reduce((acc, skill) => {
10016
10251
  acc[skill.name] = skill.definition;
@@ -10018,7 +10253,7 @@ function loadUserSkillsAsCommands() {
10018
10253
  }, {});
10019
10254
  }
10020
10255
  function loadProjectSkillsAsCommands() {
10021
- const projectSkillsDir = join33(process.cwd(), ".claude", "skills");
10256
+ const projectSkillsDir = join34(process.cwd(), ".claude", "skills");
10022
10257
  const skills = loadSkillsFromDir(projectSkillsDir, "project");
10023
10258
  return skills.reduce((acc, skill) => {
10024
10259
  acc[skill.name] = skill.definition;
@@ -10027,8 +10262,8 @@ function loadProjectSkillsAsCommands() {
10027
10262
  }
10028
10263
  // src/features/claude-code-agent-loader/loader.ts
10029
10264
  import { existsSync as existsSync25, readdirSync as readdirSync8, readFileSync as readFileSync17 } from "fs";
10030
- import { homedir as homedir12 } from "os";
10031
- import { join as join34, basename as basename2 } from "path";
10265
+ import { homedir as homedir14 } from "os";
10266
+ import { join as join35, basename as basename2 } from "path";
10032
10267
  function parseToolsConfig(toolsStr) {
10033
10268
  if (!toolsStr)
10034
10269
  return;
@@ -10050,7 +10285,7 @@ function loadAgentsFromDir(agentsDir, scope) {
10050
10285
  for (const entry of entries) {
10051
10286
  if (!isMarkdownFile(entry))
10052
10287
  continue;
10053
- const agentPath = join34(agentsDir, entry.name);
10288
+ const agentPath = join35(agentsDir, entry.name);
10054
10289
  const agentName = basename2(entry.name, ".md");
10055
10290
  try {
10056
10291
  const content = readFileSync17(agentPath, "utf-8");
@@ -10080,7 +10315,7 @@ function loadAgentsFromDir(agentsDir, scope) {
10080
10315
  return agents;
10081
10316
  }
10082
10317
  function loadUserAgents() {
10083
- const userAgentsDir = join34(homedir12(), ".claude", "agents");
10318
+ const userAgentsDir = join35(homedir14(), ".claude", "agents");
10084
10319
  const agents = loadAgentsFromDir(userAgentsDir, "user");
10085
10320
  const result = {};
10086
10321
  for (const agent of agents) {
@@ -10089,7 +10324,7 @@ function loadUserAgents() {
10089
10324
  return result;
10090
10325
  }
10091
10326
  function loadProjectAgents() {
10092
- const projectAgentsDir = join34(process.cwd(), ".claude", "agents");
10327
+ const projectAgentsDir = join35(process.cwd(), ".claude", "agents");
10093
10328
  const agents = loadAgentsFromDir(projectAgentsDir, "project");
10094
10329
  const result = {};
10095
10330
  for (const agent of agents) {
@@ -10099,8 +10334,8 @@ function loadProjectAgents() {
10099
10334
  }
10100
10335
  // src/features/claude-code-mcp-loader/loader.ts
10101
10336
  import { existsSync as existsSync26 } from "fs";
10102
- import { homedir as homedir13 } from "os";
10103
- import { join as join35 } from "path";
10337
+ import { homedir as homedir15 } from "os";
10338
+ import { join as join36 } from "path";
10104
10339
 
10105
10340
  // src/features/claude-code-mcp-loader/env-expander.ts
10106
10341
  function expandEnvVars(value) {
@@ -10166,12 +10401,12 @@ function transformMcpServer(name, server) {
10166
10401
 
10167
10402
  // src/features/claude-code-mcp-loader/loader.ts
10168
10403
  function getMcpConfigPaths() {
10169
- const home = homedir13();
10404
+ const home = homedir15();
10170
10405
  const cwd = process.cwd();
10171
10406
  return [
10172
- { path: join35(home, ".claude", ".mcp.json"), scope: "user" },
10173
- { path: join35(cwd, ".mcp.json"), scope: "project" },
10174
- { path: join35(cwd, ".claude", ".mcp.json"), scope: "local" }
10407
+ { path: join36(home, ".claude", ".mcp.json"), scope: "user" },
10408
+ { path: join36(cwd, ".mcp.json"), scope: "project" },
10409
+ { path: join36(cwd, ".claude", ".mcp.json"), scope: "local" }
10175
10410
  ];
10176
10411
  }
10177
10412
  async function loadMcpConfigFile(filePath) {
@@ -10190,13 +10425,13 @@ async function loadMcpConfigs() {
10190
10425
  const servers = {};
10191
10426
  const loadedServers = [];
10192
10427
  const paths = getMcpConfigPaths();
10193
- for (const { path: path6, scope } of paths) {
10194
- const config = await loadMcpConfigFile(path6);
10428
+ for (const { path: path7, scope } of paths) {
10429
+ const config = await loadMcpConfigFile(path7);
10195
10430
  if (!config?.mcpServers)
10196
10431
  continue;
10197
10432
  for (const [name, serverConfig] of Object.entries(config.mcpServers)) {
10198
10433
  if (serverConfig.disabled) {
10199
- log(`Skipping disabled MCP server "${name}"`, { path: path6 });
10434
+ log(`Skipping disabled MCP server "${name}"`, { path: path7 });
10200
10435
  continue;
10201
10436
  }
10202
10437
  try {
@@ -10207,7 +10442,7 @@ async function loadMcpConfigs() {
10207
10442
  loadedServers.splice(existingIndex, 1);
10208
10443
  }
10209
10444
  loadedServers.push({ name, scope, config: transformed });
10210
- log(`Loaded MCP server "${name}" from ${scope}`, { path: path6 });
10445
+ log(`Loaded MCP server "${name}" from ${scope}`, { path: path7 });
10211
10446
  } catch (error) {
10212
10447
  log(`Failed to transform MCP server "${name}"`, error);
10213
10448
  }
@@ -10322,6 +10557,10 @@ var BUILTIN_SERVERS = {
10322
10557
  command: ["astro-ls", "--stdio"],
10323
10558
  extensions: [".astro"]
10324
10559
  },
10560
+ "bash-ls": {
10561
+ command: ["bash-language-server", "start"],
10562
+ extensions: [".sh", ".bash", ".zsh", ".ksh"]
10563
+ },
10325
10564
  jdtls: {
10326
10565
  command: ["jdtls"],
10327
10566
  extensions: [".java"]
@@ -10404,13 +10643,13 @@ var EXT_TO_LANG = {
10404
10643
  };
10405
10644
  // src/tools/lsp/config.ts
10406
10645
  import { existsSync as existsSync27, readFileSync as readFileSync18 } from "fs";
10407
- import { join as join36 } from "path";
10408
- import { homedir as homedir14 } from "os";
10409
- function loadJsonFile(path6) {
10410
- if (!existsSync27(path6))
10646
+ import { join as join37 } from "path";
10647
+ import { homedir as homedir16 } from "os";
10648
+ function loadJsonFile(path7) {
10649
+ if (!existsSync27(path7))
10411
10650
  return null;
10412
10651
  try {
10413
- return JSON.parse(readFileSync18(path6, "utf-8"));
10652
+ return JSON.parse(readFileSync18(path7, "utf-8"));
10414
10653
  } catch {
10415
10654
  return null;
10416
10655
  }
@@ -10418,9 +10657,9 @@ function loadJsonFile(path6) {
10418
10657
  function getConfigPaths2() {
10419
10658
  const cwd = process.cwd();
10420
10659
  return {
10421
- project: join36(cwd, ".opencode", "oh-my-opencode.json"),
10422
- user: join36(homedir14(), ".config", "opencode", "oh-my-opencode.json"),
10423
- opencode: join36(homedir14(), ".config", "opencode", "opencode.json")
10660
+ project: join37(cwd, ".opencode", "oh-my-opencode.json"),
10661
+ user: join37(homedir16(), ".config", "opencode", "oh-my-opencode.json"),
10662
+ opencode: join37(homedir16(), ".config", "opencode", "opencode.json")
10424
10663
  };
10425
10664
  }
10426
10665
  function loadAllConfigs() {
@@ -10513,7 +10752,7 @@ function isServerInstalled(command) {
10513
10752
  const pathEnv = process.env.PATH || "";
10514
10753
  const paths = pathEnv.split(":");
10515
10754
  for (const p of paths) {
10516
- if (existsSync27(join36(p, cmd))) {
10755
+ if (existsSync27(join37(p, cmd))) {
10517
10756
  return true;
10518
10757
  }
10519
10758
  }
@@ -12122,10 +12361,10 @@ function mergeDefs(...defs) {
12122
12361
  function cloneDef(schema) {
12123
12362
  return mergeDefs(schema._zod.def);
12124
12363
  }
12125
- function getElementAtPath(obj, path6) {
12126
- if (!path6)
12364
+ function getElementAtPath(obj, path7) {
12365
+ if (!path7)
12127
12366
  return obj;
12128
- return path6.reduce((acc, key) => acc?.[key], obj);
12367
+ return path7.reduce((acc, key) => acc?.[key], obj);
12129
12368
  }
12130
12369
  function promiseAllObject(promisesObj) {
12131
12370
  const keys = Object.keys(promisesObj);
@@ -12484,11 +12723,11 @@ function aborted(x, startIndex = 0) {
12484
12723
  }
12485
12724
  return false;
12486
12725
  }
12487
- function prefixIssues(path6, issues) {
12726
+ function prefixIssues(path7, issues) {
12488
12727
  return issues.map((iss) => {
12489
12728
  var _a;
12490
12729
  (_a = iss).path ?? (_a.path = []);
12491
- iss.path.unshift(path6);
12730
+ iss.path.unshift(path7);
12492
12731
  return iss;
12493
12732
  });
12494
12733
  }
@@ -12656,7 +12895,7 @@ function treeifyError(error, _mapper) {
12656
12895
  return issue2.message;
12657
12896
  };
12658
12897
  const result = { errors: [] };
12659
- const processError = (error2, path6 = []) => {
12898
+ const processError = (error2, path7 = []) => {
12660
12899
  var _a, _b;
12661
12900
  for (const issue2 of error2.issues) {
12662
12901
  if (issue2.code === "invalid_union" && issue2.errors.length) {
@@ -12666,7 +12905,7 @@ function treeifyError(error, _mapper) {
12666
12905
  } else if (issue2.code === "invalid_element") {
12667
12906
  processError({ issues: issue2.issues }, issue2.path);
12668
12907
  } else {
12669
- const fullpath = [...path6, ...issue2.path];
12908
+ const fullpath = [...path7, ...issue2.path];
12670
12909
  if (fullpath.length === 0) {
12671
12910
  result.errors.push(mapper(issue2));
12672
12911
  continue;
@@ -12698,8 +12937,8 @@ function treeifyError(error, _mapper) {
12698
12937
  }
12699
12938
  function toDotPath(_path) {
12700
12939
  const segs = [];
12701
- const path6 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
12702
- for (const seg of path6) {
12940
+ const path7 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
12941
+ for (const seg of path7) {
12703
12942
  if (typeof seg === "number")
12704
12943
  segs.push(`[${seg}]`);
12705
12944
  else if (typeof seg === "symbol")
@@ -24042,14 +24281,14 @@ var lsp_code_action_resolve = tool({
24042
24281
  });
24043
24282
  // src/tools/ast-grep/constants.ts
24044
24283
  import { createRequire as createRequire4 } from "module";
24045
- import { dirname as dirname6, join as join38 } from "path";
24284
+ import { dirname as dirname6, join as join39 } from "path";
24046
24285
  import { existsSync as existsSync30, statSync as statSync4 } from "fs";
24047
24286
 
24048
24287
  // src/tools/ast-grep/downloader.ts
24049
24288
  var {spawn: spawn5 } = globalThis.Bun;
24050
24289
  import { existsSync as existsSync29, mkdirSync as mkdirSync10, chmodSync as chmodSync2, unlinkSync as unlinkSync9 } from "fs";
24051
- import { join as join37 } from "path";
24052
- import { homedir as homedir15 } from "os";
24290
+ import { join as join38 } from "path";
24291
+ import { homedir as homedir17 } from "os";
24053
24292
  import { createRequire as createRequire3 } from "module";
24054
24293
  var REPO2 = "ast-grep/ast-grep";
24055
24294
  var DEFAULT_VERSION = "0.40.0";
@@ -24074,18 +24313,18 @@ var PLATFORM_MAP2 = {
24074
24313
  function getCacheDir3() {
24075
24314
  if (process.platform === "win32") {
24076
24315
  const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
24077
- const base2 = localAppData || join37(homedir15(), "AppData", "Local");
24078
- return join37(base2, "oh-my-opencode", "bin");
24316
+ const base2 = localAppData || join38(homedir17(), "AppData", "Local");
24317
+ return join38(base2, "oh-my-opencode", "bin");
24079
24318
  }
24080
24319
  const xdgCache2 = process.env.XDG_CACHE_HOME;
24081
- const base = xdgCache2 || join37(homedir15(), ".cache");
24082
- return join37(base, "oh-my-opencode", "bin");
24320
+ const base = xdgCache2 || join38(homedir17(), ".cache");
24321
+ return join38(base, "oh-my-opencode", "bin");
24083
24322
  }
24084
24323
  function getBinaryName3() {
24085
24324
  return process.platform === "win32" ? "sg.exe" : "sg";
24086
24325
  }
24087
24326
  function getCachedBinaryPath2() {
24088
- const binaryPath = join37(getCacheDir3(), getBinaryName3());
24327
+ const binaryPath = join38(getCacheDir3(), getBinaryName3());
24089
24328
  return existsSync29(binaryPath) ? binaryPath : null;
24090
24329
  }
24091
24330
  async function extractZip2(archivePath, destDir) {
@@ -24112,12 +24351,12 @@ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
24112
24351
  }
24113
24352
  const cacheDir = getCacheDir3();
24114
24353
  const binaryName = getBinaryName3();
24115
- const binaryPath = join37(cacheDir, binaryName);
24354
+ const binaryPath = join38(cacheDir, binaryName);
24116
24355
  if (existsSync29(binaryPath)) {
24117
24356
  return binaryPath;
24118
24357
  }
24119
- const { arch, os: os4 } = platformInfo;
24120
- const assetName = `app-${arch}-${os4}.zip`;
24358
+ const { arch, os: os5 } = platformInfo;
24359
+ const assetName = `app-${arch}-${os5}.zip`;
24121
24360
  const downloadUrl = `https://github.com/${REPO2}/releases/download/${version2}/${assetName}`;
24122
24361
  console.log(`[oh-my-opencode] Downloading ast-grep binary...`);
24123
24362
  try {
@@ -24128,7 +24367,7 @@ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
24128
24367
  if (!response2.ok) {
24129
24368
  throw new Error(`HTTP ${response2.status}: ${response2.statusText}`);
24130
24369
  }
24131
- const archivePath = join37(cacheDir, assetName);
24370
+ const archivePath = join38(cacheDir, assetName);
24132
24371
  const arrayBuffer = await response2.arrayBuffer();
24133
24372
  await Bun.write(archivePath, arrayBuffer);
24134
24373
  await extractZip2(archivePath, cacheDir);
@@ -24186,7 +24425,7 @@ function findSgCliPathSync() {
24186
24425
  const require2 = createRequire4(import.meta.url);
24187
24426
  const cliPkgPath = require2.resolve("@ast-grep/cli/package.json");
24188
24427
  const cliDir = dirname6(cliPkgPath);
24189
- const sgPath = join38(cliDir, binaryName);
24428
+ const sgPath = join39(cliDir, binaryName);
24190
24429
  if (existsSync30(sgPath) && isValidBinary(sgPath)) {
24191
24430
  return sgPath;
24192
24431
  }
@@ -24198,7 +24437,7 @@ function findSgCliPathSync() {
24198
24437
  const pkgPath = require2.resolve(`${platformPkg}/package.json`);
24199
24438
  const pkgDir = dirname6(pkgPath);
24200
24439
  const astGrepName = process.platform === "win32" ? "ast-grep.exe" : "ast-grep";
24201
- const binaryPath = join38(pkgDir, astGrepName);
24440
+ const binaryPath = join39(pkgDir, astGrepName);
24202
24441
  if (existsSync30(binaryPath) && isValidBinary(binaryPath)) {
24203
24442
  return binaryPath;
24204
24443
  }
@@ -24206,9 +24445,9 @@ function findSgCliPathSync() {
24206
24445
  }
24207
24446
  if (process.platform === "darwin") {
24208
24447
  const homebrewPaths = ["/opt/homebrew/bin/sg", "/usr/local/bin/sg"];
24209
- for (const path6 of homebrewPaths) {
24210
- if (existsSync30(path6) && isValidBinary(path6)) {
24211
- return path6;
24448
+ for (const path7 of homebrewPaths) {
24449
+ if (existsSync30(path7) && isValidBinary(path7)) {
24450
+ return path7;
24212
24451
  }
24213
24452
  }
24214
24453
  }
@@ -24226,8 +24465,8 @@ function getSgCliPath() {
24226
24465
  }
24227
24466
  return "sg";
24228
24467
  }
24229
- function setSgCliPath(path6) {
24230
- resolvedCliPath2 = path6;
24468
+ function setSgCliPath(path7) {
24469
+ resolvedCliPath2 = path7;
24231
24470
  }
24232
24471
  var SG_CLI_PATH = getSgCliPath();
24233
24472
  var CLI_LANGUAGES = [
@@ -24574,19 +24813,19 @@ var {spawn: spawn7 } = globalThis.Bun;
24574
24813
 
24575
24814
  // src/tools/grep/constants.ts
24576
24815
  import { existsSync as existsSync33 } from "fs";
24577
- import { join as join40, dirname as dirname7 } from "path";
24816
+ import { join as join41, dirname as dirname7 } from "path";
24578
24817
  import { spawnSync } from "child_process";
24579
24818
 
24580
24819
  // src/tools/grep/downloader.ts
24581
24820
  import { existsSync as existsSync32, mkdirSync as mkdirSync11, chmodSync as chmodSync3, unlinkSync as unlinkSync10, readdirSync as readdirSync9 } from "fs";
24582
- import { join as join39 } from "path";
24821
+ import { join as join40 } from "path";
24583
24822
  function getInstallDir() {
24584
24823
  const homeDir = process.env.HOME || process.env.USERPROFILE || ".";
24585
- return join39(homeDir, ".cache", "oh-my-opencode", "bin");
24824
+ return join40(homeDir, ".cache", "oh-my-opencode", "bin");
24586
24825
  }
24587
24826
  function getRgPath() {
24588
24827
  const isWindows2 = process.platform === "win32";
24589
- return join39(getInstallDir(), isWindows2 ? "rg.exe" : "rg");
24828
+ return join40(getInstallDir(), isWindows2 ? "rg.exe" : "rg");
24590
24829
  }
24591
24830
  function getInstalledRipgrepPath() {
24592
24831
  const rgPath = getRgPath();
@@ -24613,10 +24852,10 @@ function getOpenCodeBundledRg() {
24613
24852
  const isWindows2 = process.platform === "win32";
24614
24853
  const rgName = isWindows2 ? "rg.exe" : "rg";
24615
24854
  const candidates = [
24616
- join40(execDir, rgName),
24617
- join40(execDir, "bin", rgName),
24618
- join40(execDir, "..", "bin", rgName),
24619
- join40(execDir, "..", "libexec", rgName)
24855
+ join41(execDir, rgName),
24856
+ join41(execDir, "bin", rgName),
24857
+ join41(execDir, "..", "bin", rgName),
24858
+ join41(execDir, "..", "libexec", rgName)
24620
24859
  ];
24621
24860
  for (const candidate of candidates) {
24622
24861
  if (existsSync33(candidate)) {
@@ -25023,8 +25262,8 @@ var glob = tool({
25023
25262
  });
25024
25263
  // src/tools/slashcommand/tools.ts
25025
25264
  import { existsSync as existsSync34, readdirSync as readdirSync10, readFileSync as readFileSync21 } from "fs";
25026
- import { homedir as homedir16 } from "os";
25027
- import { join as join41, basename as basename3, dirname as dirname8 } from "path";
25265
+ import { homedir as homedir18 } from "os";
25266
+ import { join as join42, basename as basename3, dirname as dirname8 } from "path";
25028
25267
  function discoverCommandsFromDir(commandsDir, scope) {
25029
25268
  if (!existsSync34(commandsDir)) {
25030
25269
  return [];
@@ -25034,7 +25273,7 @@ function discoverCommandsFromDir(commandsDir, scope) {
25034
25273
  for (const entry of entries) {
25035
25274
  if (!isMarkdownFile(entry))
25036
25275
  continue;
25037
- const commandPath = join41(commandsDir, entry.name);
25276
+ const commandPath = join42(commandsDir, entry.name);
25038
25277
  const commandName = basename3(entry.name, ".md");
25039
25278
  try {
25040
25279
  const content = readFileSync21(commandPath, "utf-8");
@@ -25062,10 +25301,10 @@ function discoverCommandsFromDir(commandsDir, scope) {
25062
25301
  return commands;
25063
25302
  }
25064
25303
  function discoverCommandsSync() {
25065
- const userCommandsDir = join41(homedir16(), ".claude", "commands");
25066
- const projectCommandsDir = join41(process.cwd(), ".claude", "commands");
25067
- const opencodeGlobalDir = join41(homedir16(), ".config", "opencode", "command");
25068
- const opencodeProjectDir = join41(process.cwd(), ".opencode", "command");
25304
+ const userCommandsDir = join42(homedir18(), ".claude", "commands");
25305
+ const projectCommandsDir = join42(process.cwd(), ".claude", "commands");
25306
+ const opencodeGlobalDir = join42(homedir18(), ".config", "opencode", "command");
25307
+ const opencodeProjectDir = join42(process.cwd(), ".opencode", "command");
25069
25308
  const userCommands = discoverCommandsFromDir(userCommandsDir, "user");
25070
25309
  const opencodeGlobalCommands = discoverCommandsFromDir(opencodeGlobalDir, "opencode");
25071
25310
  const projectCommands = discoverCommandsFromDir(projectCommandsDir, "project");
@@ -25198,8 +25437,8 @@ var SkillFrontmatterSchema = exports_external.object({
25198
25437
  });
25199
25438
  // src/tools/skill/tools.ts
25200
25439
  import { existsSync as existsSync35, readdirSync as readdirSync11, readFileSync as readFileSync22 } from "fs";
25201
- import { homedir as homedir17 } from "os";
25202
- import { join as join42, basename as basename4 } from "path";
25440
+ import { homedir as homedir19 } from "os";
25441
+ import { join as join43, basename as basename4 } from "path";
25203
25442
  function parseSkillFrontmatter(data) {
25204
25443
  return {
25205
25444
  name: typeof data.name === "string" ? data.name : "",
@@ -25218,10 +25457,10 @@ function discoverSkillsFromDir(skillsDir, scope) {
25218
25457
  for (const entry of entries) {
25219
25458
  if (entry.name.startsWith("."))
25220
25459
  continue;
25221
- const skillPath = join42(skillsDir, entry.name);
25460
+ const skillPath = join43(skillsDir, entry.name);
25222
25461
  if (entry.isDirectory() || entry.isSymbolicLink()) {
25223
25462
  const resolvedPath = resolveSymlink(skillPath);
25224
- const skillMdPath = join42(resolvedPath, "SKILL.md");
25463
+ const skillMdPath = join43(resolvedPath, "SKILL.md");
25225
25464
  if (!existsSync35(skillMdPath))
25226
25465
  continue;
25227
25466
  try {
@@ -25240,8 +25479,8 @@ function discoverSkillsFromDir(skillsDir, scope) {
25240
25479
  return skills;
25241
25480
  }
25242
25481
  function discoverSkillsSync() {
25243
- const userSkillsDir = join42(homedir17(), ".claude", "skills");
25244
- const projectSkillsDir = join42(process.cwd(), ".claude", "skills");
25482
+ const userSkillsDir = join43(homedir19(), ".claude", "skills");
25483
+ const projectSkillsDir = join43(process.cwd(), ".claude", "skills");
25245
25484
  const userSkills = discoverSkillsFromDir(userSkillsDir, "user");
25246
25485
  const projectSkills = discoverSkillsFromDir(projectSkillsDir, "project");
25247
25486
  return [...projectSkills, ...userSkills];
@@ -25251,7 +25490,7 @@ var skillListForDescription = availableSkills.map((s) => `- ${s.name}: ${s.descr
25251
25490
  `);
25252
25491
  async function parseSkillMd(skillPath) {
25253
25492
  const resolvedPath = resolveSymlink(skillPath);
25254
- const skillMdPath = join42(resolvedPath, "SKILL.md");
25493
+ const skillMdPath = join43(resolvedPath, "SKILL.md");
25255
25494
  if (!existsSync35(skillMdPath)) {
25256
25495
  return null;
25257
25496
  }
@@ -25267,9 +25506,9 @@ async function parseSkillMd(skillPath) {
25267
25506
  allowedTools: frontmatter2["allowed-tools"],
25268
25507
  metadata: frontmatter2.metadata
25269
25508
  };
25270
- const referencesDir = join42(resolvedPath, "references");
25271
- const scriptsDir = join42(resolvedPath, "scripts");
25272
- const assetsDir = join42(resolvedPath, "assets");
25509
+ const referencesDir = join43(resolvedPath, "references");
25510
+ const scriptsDir = join43(resolvedPath, "scripts");
25511
+ const assetsDir = join43(resolvedPath, "assets");
25273
25512
  const references = existsSync35(referencesDir) ? readdirSync11(referencesDir).filter((f) => !f.startsWith(".")) : [];
25274
25513
  const scripts = existsSync35(scriptsDir) ? readdirSync11(scriptsDir).filter((f) => !f.startsWith(".") && !f.startsWith("__")) : [];
25275
25514
  const assets = existsSync35(assetsDir) ? readdirSync11(assetsDir).filter((f) => !f.startsWith(".")) : [];
@@ -25296,7 +25535,7 @@ async function discoverSkillsFromDirAsync(skillsDir) {
25296
25535
  for (const entry of entries) {
25297
25536
  if (entry.name.startsWith("."))
25298
25537
  continue;
25299
- const skillPath = join42(skillsDir, entry.name);
25538
+ const skillPath = join43(skillsDir, entry.name);
25300
25539
  if (entry.isDirectory() || entry.isSymbolicLink()) {
25301
25540
  const skillInfo = await parseSkillMd(skillPath);
25302
25541
  if (skillInfo) {
@@ -25307,8 +25546,8 @@ async function discoverSkillsFromDirAsync(skillsDir) {
25307
25546
  return skills;
25308
25547
  }
25309
25548
  async function discoverSkills() {
25310
- const userSkillsDir = join42(homedir17(), ".claude", "skills");
25311
- const projectSkillsDir = join42(process.cwd(), ".claude", "skills");
25549
+ const userSkillsDir = join43(homedir19(), ".claude", "skills");
25550
+ const projectSkillsDir = join43(process.cwd(), ".claude", "skills");
25312
25551
  const userSkills = await discoverSkillsFromDirAsync(userSkillsDir);
25313
25552
  const projectSkills = await discoverSkillsFromDirAsync(projectSkillsDir);
25314
25553
  return [...projectSkills, ...userSkills];
@@ -25337,7 +25576,7 @@ async function loadSkillWithReferences(skill, includeRefs) {
25337
25576
  const referencesLoaded = [];
25338
25577
  if (includeRefs && skill.references.length > 0) {
25339
25578
  for (const ref of skill.references) {
25340
- const refPath = join42(skill.path, "references", ref);
25579
+ const refPath = join43(skill.path, "references", ref);
25341
25580
  try {
25342
25581
  let content = readFileSync22(refPath, "utf-8");
25343
25582
  content = await resolveCommandsInText(content);
@@ -25461,12 +25700,12 @@ async function findTmuxPath() {
25461
25700
  return null;
25462
25701
  }
25463
25702
  const stdout = await new Response(proc.stdout).text();
25464
- const path6 = stdout.trim().split(`
25703
+ const path7 = stdout.trim().split(`
25465
25704
  `)[0];
25466
- if (!path6) {
25705
+ if (!path7) {
25467
25706
  return null;
25468
25707
  }
25469
- const verifyProc = spawn9([path6, "-V"], {
25708
+ const verifyProc = spawn9([path7, "-V"], {
25470
25709
  stdout: "pipe",
25471
25710
  stderr: "pipe"
25472
25711
  });
@@ -25474,7 +25713,7 @@ async function findTmuxPath() {
25474
25713
  if (verifyExitCode !== 0) {
25475
25714
  return null;
25476
25715
  }
25477
- return path6;
25716
+ return path7;
25478
25717
  } catch {
25479
25718
  return null;
25480
25719
  }
@@ -25487,9 +25726,9 @@ async function getTmuxPath() {
25487
25726
  return initPromise3;
25488
25727
  }
25489
25728
  initPromise3 = (async () => {
25490
- const path6 = await findTmuxPath();
25491
- tmuxPath = path6;
25492
- return path6;
25729
+ const path7 = await findTmuxPath();
25730
+ tmuxPath = path7;
25731
+ return path7;
25493
25732
  })();
25494
25733
  return initPromise3;
25495
25734
  }
@@ -25809,7 +26048,7 @@ function createBackgroundCancel(manager, client2) {
25809
26048
  return `\u274C Invalid arguments: Either provide a taskId or set all=true to cancel all running tasks.`;
25810
26049
  }
25811
26050
  if (cancelAll) {
25812
- const tasks = manager.getTasksByParentSession(toolContext.sessionID);
26051
+ const tasks = manager.getAllDescendantTasks(toolContext.sessionID);
25813
26052
  const runningTasks = tasks.filter((t) => t.status === "running");
25814
26053
  if (runningTasks.length === 0) {
25815
26054
  return `\u2705 No running background tasks to cancel.`;
@@ -26104,15 +26343,15 @@ var builtinTools = {
26104
26343
  };
26105
26344
  // src/features/background-agent/manager.ts
26106
26345
  import { existsSync as existsSync36, readdirSync as readdirSync12 } from "fs";
26107
- import { join as join43 } from "path";
26346
+ import { join as join44 } from "path";
26108
26347
  function getMessageDir4(sessionID) {
26109
26348
  if (!existsSync36(MESSAGE_STORAGE))
26110
26349
  return null;
26111
- const directPath = join43(MESSAGE_STORAGE, sessionID);
26350
+ const directPath = join44(MESSAGE_STORAGE, sessionID);
26112
26351
  if (existsSync36(directPath))
26113
26352
  return directPath;
26114
26353
  for (const dir of readdirSync12(MESSAGE_STORAGE)) {
26115
- const sessionPath = join43(MESSAGE_STORAGE, dir, sessionID);
26354
+ const sessionPath = join44(MESSAGE_STORAGE, dir, sessionID);
26116
26355
  if (existsSync36(sessionPath))
26117
26356
  return sessionPath;
26118
26357
  }
@@ -26204,6 +26443,16 @@ class BackgroundManager {
26204
26443
  }
26205
26444
  return result;
26206
26445
  }
26446
+ getAllDescendantTasks(sessionID) {
26447
+ const result = [];
26448
+ const directChildren = this.getTasksByParentSession(sessionID);
26449
+ for (const child of directChildren) {
26450
+ result.push(child);
26451
+ const descendants = this.getAllDescendantTasks(child.sessionID);
26452
+ result.push(...descendants);
26453
+ }
26454
+ return result;
26455
+ }
26207
26456
  findBySession(sessionID) {
26208
26457
  for (const task of this.tasks.values()) {
26209
26458
  if (task.sessionID === sessionID) {
@@ -26495,7 +26744,7 @@ var AgentPermissionSchema = exports_external.object({
26495
26744
  external_directory: PermissionValue.optional()
26496
26745
  });
26497
26746
  var BuiltinAgentNameSchema = exports_external.enum([
26498
- "OmO",
26747
+ "Sisyphus",
26499
26748
  "oracle",
26500
26749
  "librarian",
26501
26750
  "explore",
@@ -26506,8 +26755,8 @@ var BuiltinAgentNameSchema = exports_external.enum([
26506
26755
  var OverridableAgentNameSchema = exports_external.enum([
26507
26756
  "build",
26508
26757
  "plan",
26509
- "OmO",
26510
- "OmO-Plan",
26758
+ "Sisyphus",
26759
+ "Planner-Sisyphus",
26511
26760
  "oracle",
26512
26761
  "librarian",
26513
26762
  "explore",
@@ -26553,8 +26802,8 @@ var AgentOverrideConfigSchema = exports_external.object({
26553
26802
  var AgentOverridesSchema = exports_external.object({
26554
26803
  build: AgentOverrideConfigSchema.optional(),
26555
26804
  plan: AgentOverrideConfigSchema.optional(),
26556
- OmO: AgentOverrideConfigSchema.optional(),
26557
- "OmO-Plan": AgentOverrideConfigSchema.optional(),
26805
+ Sisyphus: AgentOverrideConfigSchema.optional(),
26806
+ "Planner-Sisyphus": AgentOverrideConfigSchema.optional(),
26558
26807
  oracle: AgentOverrideConfigSchema.optional(),
26559
26808
  librarian: AgentOverrideConfigSchema.optional(),
26560
26809
  explore: AgentOverrideConfigSchema.optional(),
@@ -26569,9 +26818,14 @@ var ClaudeCodeConfigSchema = exports_external.object({
26569
26818
  agents: exports_external.boolean().optional(),
26570
26819
  hooks: exports_external.boolean().optional()
26571
26820
  });
26572
- var OmoAgentConfigSchema = exports_external.object({
26821
+ var SisyphusAgentConfigSchema = exports_external.object({
26573
26822
  disabled: exports_external.boolean().optional()
26574
26823
  });
26824
+ var ExperimentalConfigSchema = exports_external.object({
26825
+ aggressive_truncation: exports_external.boolean().optional(),
26826
+ empty_message_recovery: exports_external.boolean().optional(),
26827
+ auto_resume: exports_external.boolean().optional()
26828
+ });
26575
26829
  var OhMyOpenCodeConfigSchema = exports_external.object({
26576
26830
  $schema: exports_external.string().optional(),
26577
26831
  disabled_mcps: exports_external.array(McpNameSchema).optional(),
@@ -26580,7 +26834,8 @@ var OhMyOpenCodeConfigSchema = exports_external.object({
26580
26834
  agents: AgentOverridesSchema.optional(),
26581
26835
  claude_code: ClaudeCodeConfigSchema.optional(),
26582
26836
  google_auth: exports_external.boolean().optional(),
26583
- omo_agent: OmoAgentConfigSchema.optional()
26837
+ sisyphus_agent: SisyphusAgentConfigSchema.optional(),
26838
+ experimental: ExperimentalConfigSchema.optional()
26584
26839
  });
26585
26840
  // src/agents/plan-prompt.ts
26586
26841
  var PLAN_SYSTEM_PROMPT = `<system-reminder>
@@ -26655,16 +26910,14 @@ var PLAN_PERMISSION = {
26655
26910
 
26656
26911
  // src/index.ts
26657
26912
  import * as fs6 from "fs";
26658
- import * as path6 from "path";
26659
- import * as os4 from "os";
26660
- function getUserConfigDir2() {
26661
- if (process.platform === "win32") {
26662
- return process.env.APPDATA || path6.join(os4.homedir(), "AppData", "Roaming");
26663
- }
26664
- return process.env.XDG_CONFIG_HOME || path6.join(os4.homedir(), ".config");
26665
- }
26913
+ import * as path7 from "path";
26666
26914
  var AGENT_NAME_MAP = {
26667
- omo: "OmO",
26915
+ omo: "Sisyphus",
26916
+ OmO: "Sisyphus",
26917
+ "OmO-Plan": "Planner-Sisyphus",
26918
+ "omo-plan": "Planner-Sisyphus",
26919
+ sisyphus: "Sisyphus",
26920
+ "planner-sisyphus": "Planner-Sisyphus",
26668
26921
  build: "build",
26669
26922
  oracle: "oracle",
26670
26923
  librarian: "librarian",
@@ -26673,32 +26926,63 @@ var AGENT_NAME_MAP = {
26673
26926
  "document-writer": "document-writer",
26674
26927
  "multimodal-looker": "multimodal-looker"
26675
26928
  };
26676
- function normalizeAgentNames(agents) {
26677
- const normalized = {};
26929
+ function migrateAgentNames(agents) {
26930
+ const migrated = {};
26931
+ let changed = false;
26678
26932
  for (const [key, value] of Object.entries(agents)) {
26679
- const normalizedKey = AGENT_NAME_MAP[key.toLowerCase()] ?? key;
26680
- normalized[normalizedKey] = value;
26933
+ const newKey = AGENT_NAME_MAP[key.toLowerCase()] ?? AGENT_NAME_MAP[key] ?? key;
26934
+ if (newKey !== key) {
26935
+ changed = true;
26936
+ }
26937
+ migrated[newKey] = value;
26681
26938
  }
26682
- return normalized;
26939
+ return { migrated, changed };
26940
+ }
26941
+ function migrateConfigFile(configPath, rawConfig) {
26942
+ let needsWrite = false;
26943
+ if (rawConfig.agents && typeof rawConfig.agents === "object") {
26944
+ const { migrated, changed } = migrateAgentNames(rawConfig.agents);
26945
+ if (changed) {
26946
+ rawConfig.agents = migrated;
26947
+ needsWrite = true;
26948
+ }
26949
+ }
26950
+ if (rawConfig.omo_agent) {
26951
+ rawConfig.sisyphus_agent = rawConfig.omo_agent;
26952
+ delete rawConfig.omo_agent;
26953
+ needsWrite = true;
26954
+ }
26955
+ if (needsWrite) {
26956
+ try {
26957
+ fs6.writeFileSync(configPath, JSON.stringify(rawConfig, null, 2) + `
26958
+ `, "utf-8");
26959
+ log(`Migrated config file: ${configPath} (OmO \u2192 Sisyphus)`);
26960
+ } catch (err) {
26961
+ log(`Failed to write migrated config to ${configPath}:`, err);
26962
+ }
26963
+ }
26964
+ return needsWrite;
26683
26965
  }
26684
26966
  function loadConfigFromPath2(configPath) {
26685
26967
  try {
26686
26968
  if (fs6.existsSync(configPath)) {
26687
26969
  const content = fs6.readFileSync(configPath, "utf-8");
26688
26970
  const rawConfig = JSON.parse(content);
26689
- if (rawConfig.agents && typeof rawConfig.agents === "object") {
26690
- rawConfig.agents = normalizeAgentNames(rawConfig.agents);
26691
- }
26971
+ migrateConfigFile(configPath, rawConfig);
26692
26972
  const result = OhMyOpenCodeConfigSchema.safeParse(rawConfig);
26693
26973
  if (!result.success) {
26974
+ const errorMsg = result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join(", ");
26694
26975
  log(`Config validation error in ${configPath}:`, result.error.issues);
26976
+ addConfigLoadError({ path: configPath, error: `Validation error: ${errorMsg}` });
26695
26977
  return null;
26696
26978
  }
26697
26979
  log(`Config loaded from ${configPath}`, { agents: result.data.agents });
26698
26980
  return result.data;
26699
26981
  }
26700
26982
  } catch (err) {
26983
+ const errorMsg = err instanceof Error ? err.message : String(err);
26701
26984
  log(`Error loading config from ${configPath}:`, err);
26985
+ addConfigLoadError({ path: configPath, error: errorMsg });
26702
26986
  }
26703
26987
  return null;
26704
26988
  }
@@ -26729,8 +27013,8 @@ function mergeConfigs(base, override) {
26729
27013
  };
26730
27014
  }
26731
27015
  function loadPluginConfig(directory) {
26732
- const userConfigPath = path6.join(getUserConfigDir2(), "opencode", "oh-my-opencode.json");
26733
- const projectConfigPath = path6.join(directory, ".opencode", "oh-my-opencode.json");
27016
+ const userConfigPath = path7.join(getUserConfigDir(), "opencode", "oh-my-opencode.json");
27017
+ const projectConfigPath = path7.join(directory, ".opencode", "oh-my-opencode.json");
26734
27018
  let config3 = loadConfigFromPath2(userConfigPath) ?? {};
26735
27019
  const projectConfig = loadConfigFromPath2(projectConfigPath);
26736
27020
  if (projectConfig) {
@@ -26751,7 +27035,7 @@ var OhMyOpenCodePlugin = async (ctx) => {
26751
27035
  const isHookEnabled = (hookName) => !disabledHooks.has(hookName);
26752
27036
  const todoContinuationEnforcer = isHookEnabled("todo-continuation-enforcer") ? createTodoContinuationEnforcer(ctx) : null;
26753
27037
  const contextWindowMonitor = isHookEnabled("context-window-monitor") ? createContextWindowMonitorHook(ctx) : null;
26754
- const sessionRecovery = isHookEnabled("session-recovery") ? createSessionRecoveryHook(ctx) : null;
27038
+ const sessionRecovery = isHookEnabled("session-recovery") ? createSessionRecoveryHook(ctx, { experimental: pluginConfig.experimental }) : null;
26755
27039
  const sessionNotification = isHookEnabled("session-notification") ? createSessionNotification(ctx) : null;
26756
27040
  if (sessionRecovery && todoContinuationEnforcer) {
26757
27041
  sessionRecovery.setOnAbortCallback(todoContinuationEnforcer.markRecovering);
@@ -26766,10 +27050,11 @@ var OhMyOpenCodePlugin = async (ctx) => {
26766
27050
  const claudeCodeHooks = createClaudeCodeHooksHook(ctx, {
26767
27051
  disabledHooks: pluginConfig.claude_code?.hooks ?? true ? undefined : true
26768
27052
  });
26769
- const anthropicAutoCompact = isHookEnabled("anthropic-auto-compact") ? createAnthropicAutoCompactHook(ctx) : null;
27053
+ const anthropicAutoCompact = isHookEnabled("anthropic-auto-compact") ? createAnthropicAutoCompactHook(ctx, { experimental: pluginConfig.experimental }) : null;
26770
27054
  const rulesInjector = isHookEnabled("rules-injector") ? createRulesInjectorHook(ctx) : null;
26771
27055
  const autoUpdateChecker = isHookEnabled("auto-update-checker") ? createAutoUpdateCheckerHook(ctx, {
26772
- showStartupToast: isHookEnabled("startup-toast")
27056
+ showStartupToast: isHookEnabled("startup-toast"),
27057
+ isSisyphusEnabled: pluginConfig.sisyphus_agent?.disabled !== true
26773
27058
  }) : null;
26774
27059
  const keywordDetector = isHookEnabled("keyword-detector") ? createKeywordDetectorHook() : null;
26775
27060
  const agentUsageReminder = isHookEnabled("agent-usage-reminder") ? createAgentUsageReminderHook(ctx) : null;
@@ -26803,22 +27088,22 @@ var OhMyOpenCodePlugin = async (ctx) => {
26803
27088
  const builtinAgents = createBuiltinAgents(pluginConfig.disabled_agents, pluginConfig.agents, ctx.directory, config3.model);
26804
27089
  const userAgents = pluginConfig.claude_code?.agents ?? true ? loadUserAgents() : {};
26805
27090
  const projectAgents = pluginConfig.claude_code?.agents ?? true ? loadProjectAgents() : {};
26806
- const isOmoEnabled = pluginConfig.omo_agent?.disabled !== true;
26807
- if (isOmoEnabled && builtinAgents.OmO) {
27091
+ const isSisyphusEnabled = pluginConfig.sisyphus_agent?.disabled !== true;
27092
+ if (isSisyphusEnabled && builtinAgents.Sisyphus) {
26808
27093
  const { name: _planName, ...planConfigWithoutName } = config3.agent?.plan ?? {};
26809
- const omoPlanOverride = pluginConfig.agents?.["OmO-Plan"];
26810
- const omoPlanBase = {
27094
+ const plannerSisyphusOverride = pluginConfig.agents?.["Planner-Sisyphus"];
27095
+ const plannerSisyphusBase = {
26811
27096
  ...planConfigWithoutName,
26812
27097
  prompt: PLAN_SYSTEM_PROMPT,
26813
27098
  permission: PLAN_PERMISSION,
26814
27099
  description: `${config3.agent?.plan?.description ?? "Plan agent"} (OhMyOpenCode version)`,
26815
27100
  color: config3.agent?.plan?.color ?? "#6495ED"
26816
27101
  };
26817
- const omoPlanConfig = omoPlanOverride ? { ...omoPlanBase, ...omoPlanOverride } : omoPlanBase;
27102
+ const plannerSisyphusConfig = plannerSisyphusOverride ? { ...plannerSisyphusBase, ...plannerSisyphusOverride } : plannerSisyphusBase;
26818
27103
  config3.agent = {
26819
- OmO: builtinAgents.OmO,
26820
- "OmO-Plan": omoPlanConfig,
26821
- ...Object.fromEntries(Object.entries(builtinAgents).filter(([k]) => k !== "OmO")),
27104
+ Sisyphus: builtinAgents.Sisyphus,
27105
+ "Planner-Sisyphus": plannerSisyphusConfig,
27106
+ ...Object.fromEntries(Object.entries(builtinAgents).filter(([k]) => k !== "Sisyphus")),
26822
27107
  ...userAgents,
26823
27108
  ...projectAgents,
26824
27109
  ...config3.agent,