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/README.ja.md +59 -25
- package/README.ko.md +61 -27
- package/README.md +59 -26
- package/README.zh-cn.md +867 -0
- package/dist/agents/{omo.d.ts → sisyphus.d.ts} +1 -1
- package/dist/agents/types.d.ts +1 -1
- package/dist/config/index.d.ts +2 -2
- package/dist/config/schema.d.ts +33 -22
- package/dist/features/background-agent/manager.d.ts +1 -0
- package/dist/features/background-agent/manager.test.d.ts +1 -0
- package/dist/hooks/anthropic-auto-compact/executor.d.ts +2 -1
- package/dist/hooks/anthropic-auto-compact/index.d.ts +5 -1
- package/dist/hooks/anthropic-auto-compact/storage.d.ts +12 -0
- package/dist/hooks/anthropic-auto-compact/types.d.ts +5 -2
- package/dist/hooks/auto-update-checker/types.d.ts +1 -0
- package/dist/hooks/index.d.ts +2 -3
- package/dist/hooks/session-recovery/index.d.ts +5 -1
- package/dist/hooks/session-recovery/types.d.ts +15 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +759 -474
- package/dist/shared/config-errors.d.ts +7 -0
- package/dist/shared/config-path.d.ts +14 -0
- package/dist/shared/index.d.ts +2 -0
- package/package.json +1 -1
- package/dist/hooks/grep-output-truncator.d.ts +0 -12
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 = (
|
|
226
|
-
const segs =
|
|
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/
|
|
1478
|
-
var
|
|
1479
|
-
You are
|
|
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
|
-
**
|
|
1482
|
-
|
|
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
|
|
1916
|
-
description: "
|
|
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:
|
|
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
|
-
|
|
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 === "
|
|
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 === "
|
|
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
|
|
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
|
|
3246
|
+
import { join as join5 } from "path";
|
|
3220
3247
|
|
|
3221
3248
|
// src/features/hook-message-injector/constants.ts
|
|
3222
|
-
import { join as
|
|
3223
|
-
import { homedir as
|
|
3224
|
-
var xdgData = process.env.XDG_DATA_HOME ||
|
|
3225
|
-
var OPENCODE_STORAGE =
|
|
3226
|
-
var MESSAGE_STORAGE =
|
|
3227
|
-
var PART_STORAGE =
|
|
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(
|
|
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 =
|
|
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 =
|
|
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(
|
|
3323
|
-
const partDir =
|
|
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(
|
|
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 =
|
|
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 =
|
|
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
|
-
},
|
|
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
|
|
3863
|
+
import { join as join8 } from "path";
|
|
3831
3864
|
|
|
3832
3865
|
// src/hooks/session-recovery/constants.ts
|
|
3833
|
-
import { join as
|
|
3866
|
+
import { join as join7 } from "path";
|
|
3834
3867
|
|
|
3835
3868
|
// node_modules/xdg-basedir/index.js
|
|
3836
|
-
import
|
|
3837
|
-
import
|
|
3838
|
-
var homeDirectory =
|
|
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 ?
|
|
3841
|
-
var xdgConfig = env.XDG_CONFIG_HOME || (homeDirectory ?
|
|
3842
|
-
var xdgState = env.XDG_STATE_HOME || (homeDirectory ?
|
|
3843
|
-
var xdgCache = env.XDG_CACHE_HOME || (homeDirectory ?
|
|
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 =
|
|
3856
|
-
var MESSAGE_STORAGE2 =
|
|
3857
|
-
var PART_STORAGE2 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
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 =
|
|
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(
|
|
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 =
|
|
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(
|
|
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 =
|
|
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(
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
4373
|
-
|
|
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
|
|
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
|
|
4405
|
-
import { homedir as
|
|
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 =
|
|
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 ||
|
|
4427
|
-
return
|
|
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 =
|
|
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 =
|
|
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:
|
|
4488
|
-
const assetName = `comment-checker_v${version}_${
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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((
|
|
4601
|
-
debugLog2("background init complete:",
|
|
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
|
|
4597
|
+
import { join as join11 } from "path";
|
|
4651
4598
|
var DEBUG3 = process.env.COMMENT_CHECKER_DEBUG === "1";
|
|
4652
|
-
var DEBUG_FILE3 =
|
|
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((
|
|
4677
|
-
debugLog3("CLI path resolved:",
|
|
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
|
|
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
|
|
4757
|
+
import { join as join13 } from "path";
|
|
4809
4758
|
|
|
4810
4759
|
// src/hooks/directory-agents-injector/constants.ts
|
|
4811
|
-
import { join as
|
|
4812
|
-
var OPENCODE_STORAGE3 =
|
|
4813
|
-
var AGENTS_INJECTOR_STORAGE =
|
|
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
|
|
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 =
|
|
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:
|
|
4856
|
+
for (const { path: path4, content } of toInject) {
|
|
4908
4857
|
output.output += `
|
|
4909
4858
|
|
|
4910
|
-
[Directory Context: ${
|
|
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
|
|
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
|
|
4898
|
+
import { join as join16 } from "path";
|
|
4950
4899
|
|
|
4951
4900
|
// src/hooks/directory-readme-injector/constants.ts
|
|
4952
|
-
import { join as
|
|
4953
|
-
var OPENCODE_STORAGE4 =
|
|
4954
|
-
var README_INJECTOR_STORAGE =
|
|
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
|
|
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 =
|
|
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:
|
|
4997
|
+
for (const { path: path4, content } of toInject) {
|
|
5049
4998
|
output.output += `
|
|
5050
4999
|
|
|
5051
|
-
[Project README: ${
|
|
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:
|
|
5251
|
-
minOutputSizeToTruncate:
|
|
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 {
|
|
5257
|
-
|
|
5258
|
-
var
|
|
5259
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
|
5937
|
-
import { join as
|
|
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 =
|
|
6140
|
+
const home = homedir6();
|
|
5962
6141
|
const paths = [
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
|
|
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
|
|
6010
|
-
import { join as
|
|
6011
|
-
var USER_CONFIG_PATH =
|
|
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
|
|
6192
|
+
return join20(process.cwd(), ".opencode", "opencode-cc-plugin.json");
|
|
6014
6193
|
}
|
|
6015
|
-
async function loadConfigFromPath(
|
|
6016
|
-
if (!existsSync15(
|
|
6194
|
+
async function loadConfigFromPath(path4) {
|
|
6195
|
+
if (!existsSync15(path4)) {
|
|
6017
6196
|
return null;
|
|
6018
6197
|
}
|
|
6019
6198
|
try {
|
|
6020
|
-
const content = await Bun.file(
|
|
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:
|
|
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
|
|
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
|
|
6376
|
+
import { homedir as homedir8, tmpdir as tmpdir5 } from "os";
|
|
6198
6377
|
import { randomUUID } from "crypto";
|
|
6199
|
-
var TRANSCRIPT_DIR =
|
|
6378
|
+
var TRANSCRIPT_DIR = join21(homedir8(), ".claude", "transcripts");
|
|
6200
6379
|
function getTranscriptPath(sessionId) {
|
|
6201
|
-
return
|
|
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
|
|
6389
|
+
const path4 = getTranscriptPath(sessionId);
|
|
6211
6390
|
const line = JSON.stringify(entry) + `
|
|
6212
6391
|
`;
|
|
6213
|
-
appendFileSync5(
|
|
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 =
|
|
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 =
|
|
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(
|
|
6321
|
-
if (!
|
|
6499
|
+
function deleteTempTranscript(path4) {
|
|
6500
|
+
if (!path4)
|
|
6322
6501
|
return;
|
|
6323
6502
|
try {
|
|
6324
|
-
unlinkSync5(
|
|
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
|
|
6524
|
-
import { homedir as
|
|
6525
|
-
var TODO_DIR =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
6874
|
-
var OPENCODE_STORAGE6 =
|
|
6875
|
-
var RULES_INJECTOR_STORAGE =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
7344
|
+
import { join as join25 } from "path";
|
|
7166
7345
|
function getStoragePath3(sessionID) {
|
|
7167
|
-
return
|
|
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 =
|
|
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
|
|
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
|
|
7304
|
-
import * as
|
|
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
|
|
7489
|
+
return path4.join(process.env.LOCALAPPDATA ?? os4.homedir(), "opencode");
|
|
7311
7490
|
}
|
|
7312
|
-
return
|
|
7491
|
+
return path4.join(os4.homedir(), ".cache", "opencode");
|
|
7313
7492
|
}
|
|
7314
7493
|
var CACHE_DIR = getCacheDir2();
|
|
7315
|
-
var VERSION_FILE =
|
|
7316
|
-
var INSTALLED_PACKAGE_JSON =
|
|
7317
|
-
function
|
|
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 ??
|
|
7498
|
+
return process.env.APPDATA ?? path4.join(os4.homedir(), "AppData", "Roaming");
|
|
7320
7499
|
}
|
|
7321
|
-
return process.env.XDG_CONFIG_HOME ??
|
|
7500
|
+
return process.env.XDG_CONFIG_HOME ?? path4.join(os4.homedir(), ".config");
|
|
7322
7501
|
}
|
|
7323
|
-
var USER_CONFIG_DIR =
|
|
7324
|
-
var USER_OPENCODE_CONFIG =
|
|
7325
|
-
var USER_OPENCODE_CONFIG_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
|
-
|
|
7337
|
-
|
|
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 :
|
|
7547
|
+
let dir = stat.isDirectory() ? startPath : path5.dirname(startPath);
|
|
7369
7548
|
for (let i = 0;i < 10; i++) {
|
|
7370
|
-
const pkgPath =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
7502
|
-
const pkgJsonPath =
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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:
|
|
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
|
|
7587
|
-
const
|
|
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:
|
|
7591
|
-
message:
|
|
7592
|
-
|
|
7593
|
-
|
|
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]
|
|
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
|
|
7841
|
+
import { join as join30 } from "path";
|
|
7607
7842
|
|
|
7608
7843
|
// src/hooks/agent-usage-reminder/constants.ts
|
|
7609
|
-
import { join as
|
|
7610
|
-
var OPENCODE_STORAGE7 =
|
|
7611
|
-
var AGENT_USAGE_REMINDER_STORAGE =
|
|
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
|
|
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
|
|
8126
|
+
import { join as join32 } from "path";
|
|
7892
8127
|
|
|
7893
8128
|
// src/hooks/interactive-bash-session/constants.ts
|
|
7894
|
-
import { join as
|
|
7895
|
-
var OPENCODE_STORAGE8 =
|
|
7896
|
-
var INTERACTIVE_BASH_SESSION_STORAGE =
|
|
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
|
|
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
|
|
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 =
|
|
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:
|
|
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 =
|
|
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
|
|
9886
|
-
import { join as
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
9961
|
-
import { join as
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
10031
|
-
import { join as
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
10103
|
-
import { join as
|
|
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 =
|
|
10404
|
+
const home = homedir15();
|
|
10170
10405
|
const cwd = process.cwd();
|
|
10171
10406
|
return [
|
|
10172
|
-
{ path:
|
|
10173
|
-
{ path:
|
|
10174
|
-
{ path:
|
|
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:
|
|
10194
|
-
const config = await loadMcpConfigFile(
|
|
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:
|
|
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:
|
|
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
|
|
10408
|
-
import { homedir as
|
|
10409
|
-
function loadJsonFile(
|
|
10410
|
-
if (!existsSync27(
|
|
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(
|
|
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:
|
|
10422
|
-
user:
|
|
10423
|
-
opencode:
|
|
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(
|
|
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,
|
|
12126
|
-
if (!
|
|
12364
|
+
function getElementAtPath(obj, path7) {
|
|
12365
|
+
if (!path7)
|
|
12127
12366
|
return obj;
|
|
12128
|
-
return
|
|
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(
|
|
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(
|
|
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,
|
|
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 = [...
|
|
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
|
|
12702
|
-
for (const seg of
|
|
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
|
|
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
|
|
24052
|
-
import { homedir as
|
|
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 ||
|
|
24078
|
-
return
|
|
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 ||
|
|
24082
|
-
return
|
|
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 =
|
|
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 =
|
|
24354
|
+
const binaryPath = join38(cacheDir, binaryName);
|
|
24116
24355
|
if (existsSync29(binaryPath)) {
|
|
24117
24356
|
return binaryPath;
|
|
24118
24357
|
}
|
|
24119
|
-
const { arch, os:
|
|
24120
|
-
const assetName = `app-${arch}-${
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
24210
|
-
if (existsSync30(
|
|
24211
|
-
return
|
|
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(
|
|
24230
|
-
resolvedCliPath2 =
|
|
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
|
|
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
|
|
24821
|
+
import { join as join40 } from "path";
|
|
24583
24822
|
function getInstallDir() {
|
|
24584
24823
|
const homeDir = process.env.HOME || process.env.USERPROFILE || ".";
|
|
24585
|
-
return
|
|
24824
|
+
return join40(homeDir, ".cache", "oh-my-opencode", "bin");
|
|
24586
24825
|
}
|
|
24587
24826
|
function getRgPath() {
|
|
24588
24827
|
const isWindows2 = process.platform === "win32";
|
|
24589
|
-
return
|
|
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
|
-
|
|
24617
|
-
|
|
24618
|
-
|
|
24619
|
-
|
|
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
|
|
25027
|
-
import { join as
|
|
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 =
|
|
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 =
|
|
25066
|
-
const projectCommandsDir =
|
|
25067
|
-
const opencodeGlobalDir =
|
|
25068
|
-
const opencodeProjectDir =
|
|
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
|
|
25202
|
-
import { join as
|
|
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 =
|
|
25460
|
+
const skillPath = join43(skillsDir, entry.name);
|
|
25222
25461
|
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
25223
25462
|
const resolvedPath = resolveSymlink(skillPath);
|
|
25224
|
-
const skillMdPath =
|
|
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 =
|
|
25244
|
-
const projectSkillsDir =
|
|
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 =
|
|
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 =
|
|
25271
|
-
const scriptsDir =
|
|
25272
|
-
const assetsDir =
|
|
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 =
|
|
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 =
|
|
25311
|
-
const projectSkillsDir =
|
|
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 =
|
|
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
|
|
25703
|
+
const path7 = stdout.trim().split(`
|
|
25465
25704
|
`)[0];
|
|
25466
|
-
if (!
|
|
25705
|
+
if (!path7) {
|
|
25467
25706
|
return null;
|
|
25468
25707
|
}
|
|
25469
|
-
const verifyProc = spawn9([
|
|
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
|
|
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
|
|
25491
|
-
tmuxPath =
|
|
25492
|
-
return
|
|
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.
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
-
"
|
|
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
|
-
"
|
|
26510
|
-
"
|
|
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
|
-
|
|
26557
|
-
"
|
|
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
|
|
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
|
-
|
|
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
|
|
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: "
|
|
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
|
|
26677
|
-
const
|
|
26929
|
+
function migrateAgentNames(agents) {
|
|
26930
|
+
const migrated = {};
|
|
26931
|
+
let changed = false;
|
|
26678
26932
|
for (const [key, value] of Object.entries(agents)) {
|
|
26679
|
-
const
|
|
26680
|
-
|
|
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
|
|
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
|
-
|
|
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 =
|
|
26733
|
-
const projectConfigPath =
|
|
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
|
|
26807
|
-
if (
|
|
27091
|
+
const isSisyphusEnabled = pluginConfig.sisyphus_agent?.disabled !== true;
|
|
27092
|
+
if (isSisyphusEnabled && builtinAgents.Sisyphus) {
|
|
26808
27093
|
const { name: _planName, ...planConfigWithoutName } = config3.agent?.plan ?? {};
|
|
26809
|
-
const
|
|
26810
|
-
const
|
|
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
|
|
27102
|
+
const plannerSisyphusConfig = plannerSisyphusOverride ? { ...plannerSisyphusBase, ...plannerSisyphusOverride } : plannerSisyphusBase;
|
|
26818
27103
|
config3.agent = {
|
|
26819
|
-
|
|
26820
|
-
"
|
|
26821
|
-
...Object.fromEntries(Object.entries(builtinAgents).filter(([k]) => k !== "
|
|
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,
|