oh-my-opencode 2.5.2 → 2.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +49 -15
- package/README.ko.md +48 -14
- package/README.md +49 -15
- package/README.zh-cn.md +49 -15
- package/dist/agents/build-prompt.d.ts +31 -0
- package/dist/cli/index.js +1823 -1
- package/dist/cli/run/completion.d.ts +2 -0
- package/dist/cli/run/completion.test.d.ts +1 -0
- package/dist/cli/run/events.d.ts +11 -0
- package/dist/cli/run/events.test.d.ts +1 -0
- package/dist/cli/run/index.d.ts +2 -0
- package/dist/cli/run/runner.d.ts +2 -0
- package/dist/cli/run/types.d.ts +71 -0
- package/dist/config/schema.d.ts +103 -2
- package/dist/features/background-agent/types.d.ts +8 -0
- package/dist/hooks/anthropic-auto-compact/executor.test.d.ts +1 -0
- package/dist/index.js +1126 -510
- package/dist/shared/data-path.d.ts +15 -0
- package/dist/shared/index.d.ts +1 -0
- package/dist/tools/index.d.ts +52 -0
- package/dist/tools/session-manager/constants.d.ts +11 -0
- package/dist/tools/session-manager/index.d.ts +3 -0
- package/dist/tools/session-manager/storage.d.ts +8 -0
- package/dist/tools/session-manager/storage.test.d.ts +1 -0
- package/dist/tools/session-manager/tools.d.ts +52 -0
- package/dist/tools/session-manager/tools.test.d.ts +1 -0
- package/dist/tools/session-manager/types.d.ts +71 -0
- package/dist/tools/session-manager/utils.d.ts +11 -0
- package/dist/tools/session-manager/utils.test.d.ts +1 -0
- package/package.json +2 -1
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 = (path5, { windows } = {}) => {
|
|
226
|
+
const segs = path5.split(windows ? /[\\/]/ : "/");
|
|
227
227
|
const last = segs[segs.length - 1];
|
|
228
228
|
if (last === "") {
|
|
229
229
|
return segs[segs.length - 2];
|
|
@@ -3216,7 +3216,7 @@ async function getContextWindowUsage(ctx, sessionID) {
|
|
|
3216
3216
|
return null;
|
|
3217
3217
|
const lastAssistant = assistantMessages[assistantMessages.length - 1];
|
|
3218
3218
|
const lastTokens = lastAssistant.tokens;
|
|
3219
|
-
const usedTokens = (lastTokens?.input ?? 0) + (lastTokens?.cache?.read ?? 0);
|
|
3219
|
+
const usedTokens = (lastTokens?.input ?? 0) + (lastTokens?.cache?.read ?? 0) + (lastTokens?.output ?? 0);
|
|
3220
3220
|
const remainingTokens = ANTHROPIC_ACTUAL_LIMIT - usedTokens;
|
|
3221
3221
|
return {
|
|
3222
3222
|
usedTokens,
|
|
@@ -3269,6 +3269,18 @@ function getUserConfigDir() {
|
|
|
3269
3269
|
}
|
|
3270
3270
|
return process.env.XDG_CONFIG_HOME || path2.join(os2.homedir(), ".config");
|
|
3271
3271
|
}
|
|
3272
|
+
// src/shared/data-path.ts
|
|
3273
|
+
import * as path3 from "path";
|
|
3274
|
+
import * as os3 from "os";
|
|
3275
|
+
function getDataDir() {
|
|
3276
|
+
if (process.platform === "win32") {
|
|
3277
|
+
return process.env.LOCALAPPDATA ?? path3.join(os3.homedir(), "AppData", "Local");
|
|
3278
|
+
}
|
|
3279
|
+
return process.env.XDG_DATA_HOME ?? path3.join(os3.homedir(), ".local", "share");
|
|
3280
|
+
}
|
|
3281
|
+
function getOpenCodeStorageDir() {
|
|
3282
|
+
return path3.join(getDataDir(), "opencode", "storage");
|
|
3283
|
+
}
|
|
3272
3284
|
// src/shared/config-errors.ts
|
|
3273
3285
|
var configLoadErrors = [];
|
|
3274
3286
|
function getConfigLoadErrors() {
|
|
@@ -3356,7 +3368,7 @@ function createBuiltinAgents(disabledAgents = [], agentOverrides = {}, directory
|
|
|
3356
3368
|
}
|
|
3357
3369
|
// src/hooks/todo-continuation-enforcer.ts
|
|
3358
3370
|
import { existsSync as existsSync5, readdirSync as readdirSync2 } from "fs";
|
|
3359
|
-
import { join as
|
|
3371
|
+
import { join as join7 } from "path";
|
|
3360
3372
|
|
|
3361
3373
|
// src/features/claude-code-session-state/state.ts
|
|
3362
3374
|
var subagentSessions = new Set;
|
|
@@ -3369,15 +3381,13 @@ function getMainSessionID() {
|
|
|
3369
3381
|
}
|
|
3370
3382
|
// src/features/hook-message-injector/injector.ts
|
|
3371
3383
|
import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync2, readdirSync, writeFileSync } from "fs";
|
|
3372
|
-
import { join as
|
|
3384
|
+
import { join as join6 } from "path";
|
|
3373
3385
|
|
|
3374
3386
|
// src/features/hook-message-injector/constants.ts
|
|
3375
|
-
import { join as
|
|
3376
|
-
|
|
3377
|
-
var
|
|
3378
|
-
var
|
|
3379
|
-
var MESSAGE_STORAGE = join4(OPENCODE_STORAGE, "message");
|
|
3380
|
-
var PART_STORAGE = join4(OPENCODE_STORAGE, "part");
|
|
3387
|
+
import { join as join5 } from "path";
|
|
3388
|
+
var OPENCODE_STORAGE = getOpenCodeStorageDir();
|
|
3389
|
+
var MESSAGE_STORAGE = join5(OPENCODE_STORAGE, "message");
|
|
3390
|
+
var PART_STORAGE = join5(OPENCODE_STORAGE, "part");
|
|
3381
3391
|
|
|
3382
3392
|
// src/features/hook-message-injector/injector.ts
|
|
3383
3393
|
function findNearestMessageWithFields(messageDir) {
|
|
@@ -3385,7 +3395,7 @@ function findNearestMessageWithFields(messageDir) {
|
|
|
3385
3395
|
const files = readdirSync(messageDir).filter((f) => f.endsWith(".json")).sort().reverse();
|
|
3386
3396
|
for (const file of files) {
|
|
3387
3397
|
try {
|
|
3388
|
-
const content = readFileSync2(
|
|
3398
|
+
const content = readFileSync2(join6(messageDir, file), "utf-8");
|
|
3389
3399
|
const msg = JSON.parse(content);
|
|
3390
3400
|
if (msg.agent && msg.model?.providerID && msg.model?.modelID) {
|
|
3391
3401
|
return msg;
|
|
@@ -3413,12 +3423,12 @@ function getOrCreateMessageDir(sessionID) {
|
|
|
3413
3423
|
if (!existsSync4(MESSAGE_STORAGE)) {
|
|
3414
3424
|
mkdirSync(MESSAGE_STORAGE, { recursive: true });
|
|
3415
3425
|
}
|
|
3416
|
-
const directPath =
|
|
3426
|
+
const directPath = join6(MESSAGE_STORAGE, sessionID);
|
|
3417
3427
|
if (existsSync4(directPath)) {
|
|
3418
3428
|
return directPath;
|
|
3419
3429
|
}
|
|
3420
3430
|
for (const dir of readdirSync(MESSAGE_STORAGE)) {
|
|
3421
|
-
const sessionPath =
|
|
3431
|
+
const sessionPath = join6(MESSAGE_STORAGE, dir, sessionID);
|
|
3422
3432
|
if (existsSync4(sessionPath)) {
|
|
3423
3433
|
return sessionPath;
|
|
3424
3434
|
}
|
|
@@ -3472,12 +3482,12 @@ function injectHookMessage(sessionID, hookContent, originalMessage) {
|
|
|
3472
3482
|
sessionID
|
|
3473
3483
|
};
|
|
3474
3484
|
try {
|
|
3475
|
-
writeFileSync(
|
|
3476
|
-
const partDir =
|
|
3485
|
+
writeFileSync(join6(messageDir, `${messageID}.json`), JSON.stringify(messageMeta, null, 2));
|
|
3486
|
+
const partDir = join6(PART_STORAGE, messageID);
|
|
3477
3487
|
if (!existsSync4(partDir)) {
|
|
3478
3488
|
mkdirSync(partDir, { recursive: true });
|
|
3479
3489
|
}
|
|
3480
|
-
writeFileSync(
|
|
3490
|
+
writeFileSync(join6(partDir, `${partID}.json`), JSON.stringify(textPart, null, 2));
|
|
3481
3491
|
return true;
|
|
3482
3492
|
} catch {
|
|
3483
3493
|
return false;
|
|
@@ -3512,11 +3522,11 @@ Incomplete tasks remain in your todo list. Continue working on the next pending
|
|
|
3512
3522
|
function getMessageDir(sessionID) {
|
|
3513
3523
|
if (!existsSync5(MESSAGE_STORAGE))
|
|
3514
3524
|
return null;
|
|
3515
|
-
const directPath =
|
|
3525
|
+
const directPath = join7(MESSAGE_STORAGE, sessionID);
|
|
3516
3526
|
if (existsSync5(directPath))
|
|
3517
3527
|
return directPath;
|
|
3518
3528
|
for (const dir of readdirSync2(MESSAGE_STORAGE)) {
|
|
3519
|
-
const sessionPath =
|
|
3529
|
+
const sessionPath = join7(MESSAGE_STORAGE, dir, sessionID);
|
|
3520
3530
|
if (existsSync5(sessionPath))
|
|
3521
3531
|
return sessionPath;
|
|
3522
3532
|
}
|
|
@@ -3653,6 +3663,24 @@ function createTodoContinuationEnforcer(ctx, options = {}) {
|
|
|
3653
3663
|
errorSessions.delete(sessionID);
|
|
3654
3664
|
return;
|
|
3655
3665
|
}
|
|
3666
|
+
let freshTodos = [];
|
|
3667
|
+
try {
|
|
3668
|
+
log(`[${HOOK_NAME}] Re-verifying todos after countdown`, { sessionID });
|
|
3669
|
+
const response = await ctx.client.session.todo({
|
|
3670
|
+
path: { id: sessionID }
|
|
3671
|
+
});
|
|
3672
|
+
freshTodos = response.data ?? response;
|
|
3673
|
+
log(`[${HOOK_NAME}] Fresh todo count`, { sessionID, todosCount: freshTodos?.length ?? 0 });
|
|
3674
|
+
} catch (err) {
|
|
3675
|
+
log(`[${HOOK_NAME}] Failed to re-verify todos`, { sessionID, error: String(err) });
|
|
3676
|
+
return;
|
|
3677
|
+
}
|
|
3678
|
+
const freshIncomplete = freshTodos.filter((t) => t.status !== "completed" && t.status !== "cancelled");
|
|
3679
|
+
if (freshIncomplete.length === 0) {
|
|
3680
|
+
log(`[${HOOK_NAME}] Abort: no incomplete todos after countdown`, { sessionID, total: freshTodos.length });
|
|
3681
|
+
return;
|
|
3682
|
+
}
|
|
3683
|
+
log(`[${HOOK_NAME}] Confirmed incomplete todos, proceeding with injection`, { sessionID, incomplete: freshIncomplete.length, total: freshTodos.length });
|
|
3656
3684
|
remindedSessions.add(sessionID);
|
|
3657
3685
|
try {
|
|
3658
3686
|
const messageDir = getMessageDir(sessionID);
|
|
@@ -3673,7 +3701,7 @@ function createTodoContinuationEnforcer(ctx, options = {}) {
|
|
|
3673
3701
|
type: "text",
|
|
3674
3702
|
text: `${CONTINUATION_PROMPT}
|
|
3675
3703
|
|
|
3676
|
-
[Status: ${
|
|
3704
|
+
[Status: ${freshTodos.length - freshIncomplete.length}/${freshTodos.length} completed, ${freshIncomplete.length} remaining]`
|
|
3677
3705
|
}
|
|
3678
3706
|
]
|
|
3679
3707
|
},
|
|
@@ -3723,7 +3751,8 @@ function createTodoContinuationEnforcer(ctx, options = {}) {
|
|
|
3723
3751
|
}
|
|
3724
3752
|
if (sessionID && role === "assistant" && finish) {
|
|
3725
3753
|
remindedSessions.delete(sessionID);
|
|
3726
|
-
|
|
3754
|
+
preemptivelyInjectedSessions.delete(sessionID);
|
|
3755
|
+
log(`[${HOOK_NAME}] Cleared reminded/preemptive state on assistant finish`, { sessionID });
|
|
3727
3756
|
const isTerminalFinish = finish && !["tool-calls", "unknown"].includes(finish);
|
|
3728
3757
|
if (isTerminalFinish && isNonInteractive()) {
|
|
3729
3758
|
log(`[${HOOK_NAME}] Terminal finish in non-interactive mode`, { sessionID, finish });
|
|
@@ -4090,24 +4119,24 @@ function createSessionNotification(ctx, config = {}) {
|
|
|
4090
4119
|
}
|
|
4091
4120
|
// src/hooks/session-recovery/storage.ts
|
|
4092
4121
|
import { existsSync as existsSync6, mkdirSync as mkdirSync2, readdirSync as readdirSync3, readFileSync as readFileSync3, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
4093
|
-
import { join as
|
|
4122
|
+
import { join as join9 } from "path";
|
|
4094
4123
|
|
|
4095
4124
|
// src/hooks/session-recovery/constants.ts
|
|
4096
|
-
import { join as
|
|
4125
|
+
import { join as join8 } from "path";
|
|
4097
4126
|
|
|
4098
4127
|
// node_modules/xdg-basedir/index.js
|
|
4099
|
-
import
|
|
4100
|
-
import
|
|
4101
|
-
var homeDirectory =
|
|
4128
|
+
import os4 from "os";
|
|
4129
|
+
import path4 from "path";
|
|
4130
|
+
var homeDirectory = os4.homedir();
|
|
4102
4131
|
var { env } = process;
|
|
4103
|
-
var
|
|
4104
|
-
var xdgConfig = env.XDG_CONFIG_HOME || (homeDirectory ?
|
|
4105
|
-
var xdgState = env.XDG_STATE_HOME || (homeDirectory ?
|
|
4106
|
-
var xdgCache = env.XDG_CACHE_HOME || (homeDirectory ?
|
|
4132
|
+
var xdgData = env.XDG_DATA_HOME || (homeDirectory ? path4.join(homeDirectory, ".local", "share") : undefined);
|
|
4133
|
+
var xdgConfig = env.XDG_CONFIG_HOME || (homeDirectory ? path4.join(homeDirectory, ".config") : undefined);
|
|
4134
|
+
var xdgState = env.XDG_STATE_HOME || (homeDirectory ? path4.join(homeDirectory, ".local", "state") : undefined);
|
|
4135
|
+
var xdgCache = env.XDG_CACHE_HOME || (homeDirectory ? path4.join(homeDirectory, ".cache") : undefined);
|
|
4107
4136
|
var xdgRuntime = env.XDG_RUNTIME_DIR || undefined;
|
|
4108
4137
|
var xdgDataDirectories = (env.XDG_DATA_DIRS || "/usr/local/share/:/usr/share/").split(":");
|
|
4109
|
-
if (
|
|
4110
|
-
xdgDataDirectories.unshift(
|
|
4138
|
+
if (xdgData) {
|
|
4139
|
+
xdgDataDirectories.unshift(xdgData);
|
|
4111
4140
|
}
|
|
4112
4141
|
var xdgConfigDirectories = (env.XDG_CONFIG_DIRS || "/etc/xdg").split(":");
|
|
4113
4142
|
if (xdgConfig) {
|
|
@@ -4115,9 +4144,9 @@ if (xdgConfig) {
|
|
|
4115
4144
|
}
|
|
4116
4145
|
|
|
4117
4146
|
// src/hooks/session-recovery/constants.ts
|
|
4118
|
-
var OPENCODE_STORAGE2 =
|
|
4119
|
-
var MESSAGE_STORAGE2 =
|
|
4120
|
-
var PART_STORAGE2 =
|
|
4147
|
+
var OPENCODE_STORAGE2 = join8(xdgData ?? "", "opencode", "storage");
|
|
4148
|
+
var MESSAGE_STORAGE2 = join8(OPENCODE_STORAGE2, "message");
|
|
4149
|
+
var PART_STORAGE2 = join8(OPENCODE_STORAGE2, "part");
|
|
4121
4150
|
var THINKING_TYPES = new Set(["thinking", "redacted_thinking", "reasoning"]);
|
|
4122
4151
|
var META_TYPES = new Set(["step-start", "step-finish"]);
|
|
4123
4152
|
var CONTENT_TYPES = new Set(["text", "tool", "tool_use", "tool_result"]);
|
|
@@ -4131,12 +4160,12 @@ function generatePartId2() {
|
|
|
4131
4160
|
function getMessageDir2(sessionID) {
|
|
4132
4161
|
if (!existsSync6(MESSAGE_STORAGE2))
|
|
4133
4162
|
return "";
|
|
4134
|
-
const directPath =
|
|
4163
|
+
const directPath = join9(MESSAGE_STORAGE2, sessionID);
|
|
4135
4164
|
if (existsSync6(directPath)) {
|
|
4136
4165
|
return directPath;
|
|
4137
4166
|
}
|
|
4138
4167
|
for (const dir of readdirSync3(MESSAGE_STORAGE2)) {
|
|
4139
|
-
const sessionPath =
|
|
4168
|
+
const sessionPath = join9(MESSAGE_STORAGE2, dir, sessionID);
|
|
4140
4169
|
if (existsSync6(sessionPath)) {
|
|
4141
4170
|
return sessionPath;
|
|
4142
4171
|
}
|
|
@@ -4152,7 +4181,7 @@ function readMessages(sessionID) {
|
|
|
4152
4181
|
if (!file.endsWith(".json"))
|
|
4153
4182
|
continue;
|
|
4154
4183
|
try {
|
|
4155
|
-
const content = readFileSync3(
|
|
4184
|
+
const content = readFileSync3(join9(messageDir, file), "utf-8");
|
|
4156
4185
|
messages.push(JSON.parse(content));
|
|
4157
4186
|
} catch {
|
|
4158
4187
|
continue;
|
|
@@ -4167,7 +4196,7 @@ function readMessages(sessionID) {
|
|
|
4167
4196
|
});
|
|
4168
4197
|
}
|
|
4169
4198
|
function readParts(messageID) {
|
|
4170
|
-
const partDir =
|
|
4199
|
+
const partDir = join9(PART_STORAGE2, messageID);
|
|
4171
4200
|
if (!existsSync6(partDir))
|
|
4172
4201
|
return [];
|
|
4173
4202
|
const parts = [];
|
|
@@ -4175,7 +4204,7 @@ function readParts(messageID) {
|
|
|
4175
4204
|
if (!file.endsWith(".json"))
|
|
4176
4205
|
continue;
|
|
4177
4206
|
try {
|
|
4178
|
-
const content = readFileSync3(
|
|
4207
|
+
const content = readFileSync3(join9(partDir, file), "utf-8");
|
|
4179
4208
|
parts.push(JSON.parse(content));
|
|
4180
4209
|
} catch {
|
|
4181
4210
|
continue;
|
|
@@ -4205,7 +4234,7 @@ function messageHasContent(messageID) {
|
|
|
4205
4234
|
return parts.some(hasContent);
|
|
4206
4235
|
}
|
|
4207
4236
|
function injectTextPart(sessionID, messageID, text) {
|
|
4208
|
-
const partDir =
|
|
4237
|
+
const partDir = join9(PART_STORAGE2, messageID);
|
|
4209
4238
|
if (!existsSync6(partDir)) {
|
|
4210
4239
|
mkdirSync2(partDir, { recursive: true });
|
|
4211
4240
|
}
|
|
@@ -4219,7 +4248,7 @@ function injectTextPart(sessionID, messageID, text) {
|
|
|
4219
4248
|
synthetic: true
|
|
4220
4249
|
};
|
|
4221
4250
|
try {
|
|
4222
|
-
writeFileSync2(
|
|
4251
|
+
writeFileSync2(join9(partDir, `${partId}.json`), JSON.stringify(part, null, 2));
|
|
4223
4252
|
return true;
|
|
4224
4253
|
} catch {
|
|
4225
4254
|
return false;
|
|
@@ -4282,7 +4311,7 @@ function findMessagesWithOrphanThinking(sessionID) {
|
|
|
4282
4311
|
return result;
|
|
4283
4312
|
}
|
|
4284
4313
|
function prependThinkingPart(sessionID, messageID) {
|
|
4285
|
-
const partDir =
|
|
4314
|
+
const partDir = join9(PART_STORAGE2, messageID);
|
|
4286
4315
|
if (!existsSync6(partDir)) {
|
|
4287
4316
|
mkdirSync2(partDir, { recursive: true });
|
|
4288
4317
|
}
|
|
@@ -4296,14 +4325,14 @@ function prependThinkingPart(sessionID, messageID) {
|
|
|
4296
4325
|
synthetic: true
|
|
4297
4326
|
};
|
|
4298
4327
|
try {
|
|
4299
|
-
writeFileSync2(
|
|
4328
|
+
writeFileSync2(join9(partDir, `${partId}.json`), JSON.stringify(part, null, 2));
|
|
4300
4329
|
return true;
|
|
4301
4330
|
} catch {
|
|
4302
4331
|
return false;
|
|
4303
4332
|
}
|
|
4304
4333
|
}
|
|
4305
4334
|
function stripThinkingParts(messageID) {
|
|
4306
|
-
const partDir =
|
|
4335
|
+
const partDir = join9(PART_STORAGE2, messageID);
|
|
4307
4336
|
if (!existsSync6(partDir))
|
|
4308
4337
|
return false;
|
|
4309
4338
|
let anyRemoved = false;
|
|
@@ -4311,7 +4340,7 @@ function stripThinkingParts(messageID) {
|
|
|
4311
4340
|
if (!file.endsWith(".json"))
|
|
4312
4341
|
continue;
|
|
4313
4342
|
try {
|
|
4314
|
-
const filePath =
|
|
4343
|
+
const filePath = join9(partDir, file);
|
|
4315
4344
|
const content = readFileSync3(filePath, "utf-8");
|
|
4316
4345
|
const part = JSON.parse(content);
|
|
4317
4346
|
if (THINKING_TYPES.has(part.type)) {
|
|
@@ -4325,7 +4354,7 @@ function stripThinkingParts(messageID) {
|
|
|
4325
4354
|
return anyRemoved;
|
|
4326
4355
|
}
|
|
4327
4356
|
function replaceEmptyTextParts(messageID, replacementText) {
|
|
4328
|
-
const partDir =
|
|
4357
|
+
const partDir = join9(PART_STORAGE2, messageID);
|
|
4329
4358
|
if (!existsSync6(partDir))
|
|
4330
4359
|
return false;
|
|
4331
4360
|
let anyReplaced = false;
|
|
@@ -4333,7 +4362,7 @@ function replaceEmptyTextParts(messageID, replacementText) {
|
|
|
4333
4362
|
if (!file.endsWith(".json"))
|
|
4334
4363
|
continue;
|
|
4335
4364
|
try {
|
|
4336
|
-
const filePath =
|
|
4365
|
+
const filePath = join9(partDir, file);
|
|
4337
4366
|
const content = readFileSync3(filePath, "utf-8");
|
|
4338
4367
|
const part = JSON.parse(content);
|
|
4339
4368
|
if (part.type === "text") {
|
|
@@ -4610,7 +4639,7 @@ function createSessionRecoveryHook(ctx, options) {
|
|
|
4610
4639
|
// src/hooks/comment-checker/cli.ts
|
|
4611
4640
|
var {spawn: spawn3 } = globalThis.Bun;
|
|
4612
4641
|
import { createRequire as createRequire2 } from "module";
|
|
4613
|
-
import { dirname, join as
|
|
4642
|
+
import { dirname, join as join11 } from "path";
|
|
4614
4643
|
import { existsSync as existsSync8 } from "fs";
|
|
4615
4644
|
import * as fs3 from "fs";
|
|
4616
4645
|
import { tmpdir as tmpdir3 } from "os";
|
|
@@ -4618,11 +4647,11 @@ import { tmpdir as tmpdir3 } from "os";
|
|
|
4618
4647
|
// src/hooks/comment-checker/downloader.ts
|
|
4619
4648
|
var {spawn: spawn2 } = globalThis.Bun;
|
|
4620
4649
|
import { existsSync as existsSync7, mkdirSync as mkdirSync3, chmodSync, unlinkSync as unlinkSync2, appendFileSync as appendFileSync2 } from "fs";
|
|
4621
|
-
import { join as
|
|
4650
|
+
import { join as join10 } from "path";
|
|
4622
4651
|
import { homedir as homedir4, tmpdir as tmpdir2 } from "os";
|
|
4623
4652
|
import { createRequire } from "module";
|
|
4624
4653
|
var DEBUG = process.env.COMMENT_CHECKER_DEBUG === "1";
|
|
4625
|
-
var DEBUG_FILE =
|
|
4654
|
+
var DEBUG_FILE = join10(tmpdir2(), "comment-checker-debug.log");
|
|
4626
4655
|
function debugLog(...args) {
|
|
4627
4656
|
if (DEBUG) {
|
|
4628
4657
|
const msg = `[${new Date().toISOString()}] [comment-checker:downloader] ${args.map((a) => typeof a === "object" ? JSON.stringify(a, null, 2) : String(a)).join(" ")}
|
|
@@ -4640,14 +4669,14 @@ var PLATFORM_MAP = {
|
|
|
4640
4669
|
};
|
|
4641
4670
|
function getCacheDir() {
|
|
4642
4671
|
const xdgCache2 = process.env.XDG_CACHE_HOME;
|
|
4643
|
-
const base = xdgCache2 ||
|
|
4644
|
-
return
|
|
4672
|
+
const base = xdgCache2 || join10(homedir4(), ".cache");
|
|
4673
|
+
return join10(base, "oh-my-opencode", "bin");
|
|
4645
4674
|
}
|
|
4646
4675
|
function getBinaryName() {
|
|
4647
4676
|
return process.platform === "win32" ? "comment-checker.exe" : "comment-checker";
|
|
4648
4677
|
}
|
|
4649
4678
|
function getCachedBinaryPath() {
|
|
4650
|
-
const binaryPath =
|
|
4679
|
+
const binaryPath = join10(getCacheDir(), getBinaryName());
|
|
4651
4680
|
return existsSync7(binaryPath) ? binaryPath : null;
|
|
4652
4681
|
}
|
|
4653
4682
|
function getPackageVersion() {
|
|
@@ -4695,14 +4724,14 @@ async function downloadCommentChecker() {
|
|
|
4695
4724
|
}
|
|
4696
4725
|
const cacheDir = getCacheDir();
|
|
4697
4726
|
const binaryName = getBinaryName();
|
|
4698
|
-
const binaryPath =
|
|
4727
|
+
const binaryPath = join10(cacheDir, binaryName);
|
|
4699
4728
|
if (existsSync7(binaryPath)) {
|
|
4700
4729
|
debugLog("Binary already cached at:", binaryPath);
|
|
4701
4730
|
return binaryPath;
|
|
4702
4731
|
}
|
|
4703
4732
|
const version = getPackageVersion();
|
|
4704
|
-
const { os:
|
|
4705
|
-
const assetName = `comment-checker_v${version}_${
|
|
4733
|
+
const { os: os5, arch, ext } = platformInfo;
|
|
4734
|
+
const assetName = `comment-checker_v${version}_${os5}_${arch}.${ext}`;
|
|
4706
4735
|
const downloadUrl = `https://github.com/${REPO}/releases/download/v${version}/${assetName}`;
|
|
4707
4736
|
debugLog(`Downloading from: ${downloadUrl}`);
|
|
4708
4737
|
console.log(`[oh-my-opencode] Downloading comment-checker binary...`);
|
|
@@ -4714,7 +4743,7 @@ async function downloadCommentChecker() {
|
|
|
4714
4743
|
if (!response.ok) {
|
|
4715
4744
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
4716
4745
|
}
|
|
4717
|
-
const archivePath =
|
|
4746
|
+
const archivePath = join10(cacheDir, assetName);
|
|
4718
4747
|
const arrayBuffer = await response.arrayBuffer();
|
|
4719
4748
|
await Bun.write(archivePath, arrayBuffer);
|
|
4720
4749
|
debugLog(`Downloaded archive to: ${archivePath}`);
|
|
@@ -4750,7 +4779,7 @@ async function ensureCommentCheckerBinary() {
|
|
|
4750
4779
|
|
|
4751
4780
|
// src/hooks/comment-checker/cli.ts
|
|
4752
4781
|
var DEBUG2 = process.env.COMMENT_CHECKER_DEBUG === "1";
|
|
4753
|
-
var DEBUG_FILE2 =
|
|
4782
|
+
var DEBUG_FILE2 = join11(tmpdir3(), "comment-checker-debug.log");
|
|
4754
4783
|
function debugLog2(...args) {
|
|
4755
4784
|
if (DEBUG2) {
|
|
4756
4785
|
const msg = `[${new Date().toISOString()}] [comment-checker:cli] ${args.map((a) => typeof a === "object" ? JSON.stringify(a, null, 2) : String(a)).join(" ")}
|
|
@@ -4767,7 +4796,7 @@ function findCommentCheckerPathSync() {
|
|
|
4767
4796
|
const require2 = createRequire2(import.meta.url);
|
|
4768
4797
|
const cliPkgPath = require2.resolve("@code-yeongyu/comment-checker/package.json");
|
|
4769
4798
|
const cliDir = dirname(cliPkgPath);
|
|
4770
|
-
const binaryPath =
|
|
4799
|
+
const binaryPath = join11(cliDir, "bin", binaryName);
|
|
4771
4800
|
if (existsSync8(binaryPath)) {
|
|
4772
4801
|
debugLog2("found binary in main package:", binaryPath);
|
|
4773
4802
|
return binaryPath;
|
|
@@ -4814,8 +4843,8 @@ async function getCommentCheckerPath() {
|
|
|
4814
4843
|
function startBackgroundInit() {
|
|
4815
4844
|
if (!initPromise) {
|
|
4816
4845
|
initPromise = getCommentCheckerPath();
|
|
4817
|
-
initPromise.then((
|
|
4818
|
-
debugLog2("background init complete:",
|
|
4846
|
+
initPromise.then((path5) => {
|
|
4847
|
+
debugLog2("background init complete:", path5 || "no binary");
|
|
4819
4848
|
}).catch((err) => {
|
|
4820
4849
|
debugLog2("background init error:", err);
|
|
4821
4850
|
});
|
|
@@ -4864,9 +4893,9 @@ async function runCommentChecker(input, cliPath) {
|
|
|
4864
4893
|
import * as fs4 from "fs";
|
|
4865
4894
|
import { existsSync as existsSync9 } from "fs";
|
|
4866
4895
|
import { tmpdir as tmpdir4 } from "os";
|
|
4867
|
-
import { join as
|
|
4896
|
+
import { join as join12 } from "path";
|
|
4868
4897
|
var DEBUG3 = process.env.COMMENT_CHECKER_DEBUG === "1";
|
|
4869
|
-
var DEBUG_FILE3 =
|
|
4898
|
+
var DEBUG_FILE3 = join12(tmpdir4(), "comment-checker-debug.log");
|
|
4870
4899
|
function debugLog3(...args) {
|
|
4871
4900
|
if (DEBUG3) {
|
|
4872
4901
|
const msg = `[${new Date().toISOString()}] [comment-checker:hook] ${args.map((a) => typeof a === "object" ? JSON.stringify(a, null, 2) : String(a)).join(" ")}
|
|
@@ -4890,8 +4919,8 @@ function createCommentCheckerHooks() {
|
|
|
4890
4919
|
debugLog3("createCommentCheckerHooks called");
|
|
4891
4920
|
startBackgroundInit();
|
|
4892
4921
|
cliPathPromise = getCommentCheckerPath();
|
|
4893
|
-
cliPathPromise.then((
|
|
4894
|
-
debugLog3("CLI path resolved:",
|
|
4922
|
+
cliPathPromise.then((path5) => {
|
|
4923
|
+
debugLog3("CLI path resolved:", path5 || "disabled (no binary)");
|
|
4895
4924
|
}).catch((err) => {
|
|
4896
4925
|
debugLog3("CLI path resolution error:", err);
|
|
4897
4926
|
});
|
|
@@ -4998,7 +5027,7 @@ var TRUNCATABLE_TOOLS = [
|
|
|
4998
5027
|
];
|
|
4999
5028
|
function createToolOutputTruncatorHook(ctx, options) {
|
|
5000
5029
|
const truncator = createDynamicTruncator(ctx);
|
|
5001
|
-
const truncateAll = options?.experimental?.truncate_all_tool_outputs ??
|
|
5030
|
+
const truncateAll = options?.experimental?.truncate_all_tool_outputs ?? true;
|
|
5002
5031
|
const toolExecuteAfter = async (input, output) => {
|
|
5003
5032
|
if (!truncateAll && !TRUNCATABLE_TOOLS.includes(input.tool))
|
|
5004
5033
|
return;
|
|
@@ -5015,7 +5044,7 @@ function createToolOutputTruncatorHook(ctx, options) {
|
|
|
5015
5044
|
}
|
|
5016
5045
|
// src/hooks/directory-agents-injector/index.ts
|
|
5017
5046
|
import { existsSync as existsSync11, readFileSync as readFileSync5 } from "fs";
|
|
5018
|
-
import { dirname as dirname2, join as
|
|
5047
|
+
import { dirname as dirname2, join as join15, resolve as resolve2 } from "path";
|
|
5019
5048
|
|
|
5020
5049
|
// src/hooks/directory-agents-injector/storage.ts
|
|
5021
5050
|
import {
|
|
@@ -5025,17 +5054,17 @@ import {
|
|
|
5025
5054
|
writeFileSync as writeFileSync3,
|
|
5026
5055
|
unlinkSync as unlinkSync3
|
|
5027
5056
|
} from "fs";
|
|
5028
|
-
import { join as
|
|
5057
|
+
import { join as join14 } from "path";
|
|
5029
5058
|
|
|
5030
5059
|
// src/hooks/directory-agents-injector/constants.ts
|
|
5031
|
-
import { join as
|
|
5032
|
-
var OPENCODE_STORAGE3 =
|
|
5033
|
-
var AGENTS_INJECTOR_STORAGE =
|
|
5060
|
+
import { join as join13 } from "path";
|
|
5061
|
+
var OPENCODE_STORAGE3 = join13(xdgData ?? "", "opencode", "storage");
|
|
5062
|
+
var AGENTS_INJECTOR_STORAGE = join13(OPENCODE_STORAGE3, "directory-agents");
|
|
5034
5063
|
var AGENTS_FILENAME = "AGENTS.md";
|
|
5035
5064
|
|
|
5036
5065
|
// src/hooks/directory-agents-injector/storage.ts
|
|
5037
5066
|
function getStoragePath(sessionID) {
|
|
5038
|
-
return
|
|
5067
|
+
return join14(AGENTS_INJECTOR_STORAGE, `${sessionID}.json`);
|
|
5039
5068
|
}
|
|
5040
5069
|
function loadInjectedPaths(sessionID) {
|
|
5041
5070
|
const filePath = getStoragePath(sessionID);
|
|
@@ -5071,24 +5100,25 @@ function clearInjectedPaths(sessionID) {
|
|
|
5071
5100
|
function createDirectoryAgentsInjectorHook(ctx) {
|
|
5072
5101
|
const sessionCaches = new Map;
|
|
5073
5102
|
const pendingBatchReads = new Map;
|
|
5103
|
+
const truncator = createDynamicTruncator(ctx);
|
|
5074
5104
|
function getSessionCache(sessionID) {
|
|
5075
5105
|
if (!sessionCaches.has(sessionID)) {
|
|
5076
5106
|
sessionCaches.set(sessionID, loadInjectedPaths(sessionID));
|
|
5077
5107
|
}
|
|
5078
5108
|
return sessionCaches.get(sessionID);
|
|
5079
5109
|
}
|
|
5080
|
-
function resolveFilePath2(
|
|
5081
|
-
if (!
|
|
5110
|
+
function resolveFilePath2(path5) {
|
|
5111
|
+
if (!path5)
|
|
5082
5112
|
return null;
|
|
5083
|
-
if (
|
|
5084
|
-
return
|
|
5085
|
-
return resolve2(ctx.directory,
|
|
5113
|
+
if (path5.startsWith("/"))
|
|
5114
|
+
return path5;
|
|
5115
|
+
return resolve2(ctx.directory, path5);
|
|
5086
5116
|
}
|
|
5087
5117
|
function findAgentsMdUp(startDir) {
|
|
5088
5118
|
const found = [];
|
|
5089
5119
|
let current = startDir;
|
|
5090
5120
|
while (true) {
|
|
5091
|
-
const agentsPath =
|
|
5121
|
+
const agentsPath = join15(current, AGENTS_FILENAME);
|
|
5092
5122
|
if (existsSync11(agentsPath)) {
|
|
5093
5123
|
found.push(agentsPath);
|
|
5094
5124
|
}
|
|
@@ -5103,7 +5133,7 @@ function createDirectoryAgentsInjectorHook(ctx) {
|
|
|
5103
5133
|
}
|
|
5104
5134
|
return found.reverse();
|
|
5105
5135
|
}
|
|
5106
|
-
function processFilePathForInjection(filePath, sessionID, output) {
|
|
5136
|
+
async function processFilePathForInjection(filePath, sessionID, output) {
|
|
5107
5137
|
const resolved = resolveFilePath2(filePath);
|
|
5108
5138
|
if (!resolved)
|
|
5109
5139
|
return;
|
|
@@ -5116,10 +5146,14 @@ function createDirectoryAgentsInjectorHook(ctx) {
|
|
|
5116
5146
|
continue;
|
|
5117
5147
|
try {
|
|
5118
5148
|
const content = readFileSync5(agentsPath, "utf-8");
|
|
5149
|
+
const { result, truncated } = await truncator.truncate(sessionID, content);
|
|
5150
|
+
const truncationNotice = truncated ? `
|
|
5151
|
+
|
|
5152
|
+
[Note: Content was truncated to save context window space. For full context, please read the file directly: ${agentsPath}]` : "";
|
|
5119
5153
|
output.output += `
|
|
5120
5154
|
|
|
5121
5155
|
[Directory Context: ${agentsPath}]
|
|
5122
|
-
${
|
|
5156
|
+
${result}${truncationNotice}`;
|
|
5123
5157
|
cache.add(agentsDir);
|
|
5124
5158
|
} catch {}
|
|
5125
5159
|
}
|
|
@@ -5144,14 +5178,14 @@ ${content}`;
|
|
|
5144
5178
|
const toolExecuteAfter = async (input, output) => {
|
|
5145
5179
|
const toolName = input.tool.toLowerCase();
|
|
5146
5180
|
if (toolName === "read") {
|
|
5147
|
-
processFilePathForInjection(output.title, input.sessionID, output);
|
|
5181
|
+
await processFilePathForInjection(output.title, input.sessionID, output);
|
|
5148
5182
|
return;
|
|
5149
5183
|
}
|
|
5150
5184
|
if (toolName === "batch") {
|
|
5151
5185
|
const filePaths = pendingBatchReads.get(input.callID);
|
|
5152
5186
|
if (filePaths) {
|
|
5153
5187
|
for (const filePath of filePaths) {
|
|
5154
|
-
processFilePathForInjection(filePath, input.sessionID, output);
|
|
5188
|
+
await processFilePathForInjection(filePath, input.sessionID, output);
|
|
5155
5189
|
}
|
|
5156
5190
|
pendingBatchReads.delete(input.callID);
|
|
5157
5191
|
}
|
|
@@ -5182,7 +5216,7 @@ ${content}`;
|
|
|
5182
5216
|
}
|
|
5183
5217
|
// src/hooks/directory-readme-injector/index.ts
|
|
5184
5218
|
import { existsSync as existsSync13, readFileSync as readFileSync7 } from "fs";
|
|
5185
|
-
import { dirname as dirname3, join as
|
|
5219
|
+
import { dirname as dirname3, join as join18, resolve as resolve3 } from "path";
|
|
5186
5220
|
|
|
5187
5221
|
// src/hooks/directory-readme-injector/storage.ts
|
|
5188
5222
|
import {
|
|
@@ -5192,17 +5226,17 @@ import {
|
|
|
5192
5226
|
writeFileSync as writeFileSync4,
|
|
5193
5227
|
unlinkSync as unlinkSync4
|
|
5194
5228
|
} from "fs";
|
|
5195
|
-
import { join as
|
|
5229
|
+
import { join as join17 } from "path";
|
|
5196
5230
|
|
|
5197
5231
|
// src/hooks/directory-readme-injector/constants.ts
|
|
5198
|
-
import { join as
|
|
5199
|
-
var OPENCODE_STORAGE4 =
|
|
5200
|
-
var README_INJECTOR_STORAGE =
|
|
5232
|
+
import { join as join16 } from "path";
|
|
5233
|
+
var OPENCODE_STORAGE4 = join16(xdgData ?? "", "opencode", "storage");
|
|
5234
|
+
var README_INJECTOR_STORAGE = join16(OPENCODE_STORAGE4, "directory-readme");
|
|
5201
5235
|
var README_FILENAME = "README.md";
|
|
5202
5236
|
|
|
5203
5237
|
// src/hooks/directory-readme-injector/storage.ts
|
|
5204
5238
|
function getStoragePath2(sessionID) {
|
|
5205
|
-
return
|
|
5239
|
+
return join17(README_INJECTOR_STORAGE, `${sessionID}.json`);
|
|
5206
5240
|
}
|
|
5207
5241
|
function loadInjectedPaths2(sessionID) {
|
|
5208
5242
|
const filePath = getStoragePath2(sessionID);
|
|
@@ -5238,24 +5272,25 @@ function clearInjectedPaths2(sessionID) {
|
|
|
5238
5272
|
function createDirectoryReadmeInjectorHook(ctx) {
|
|
5239
5273
|
const sessionCaches = new Map;
|
|
5240
5274
|
const pendingBatchReads = new Map;
|
|
5275
|
+
const truncator = createDynamicTruncator(ctx);
|
|
5241
5276
|
function getSessionCache(sessionID) {
|
|
5242
5277
|
if (!sessionCaches.has(sessionID)) {
|
|
5243
5278
|
sessionCaches.set(sessionID, loadInjectedPaths2(sessionID));
|
|
5244
5279
|
}
|
|
5245
5280
|
return sessionCaches.get(sessionID);
|
|
5246
5281
|
}
|
|
5247
|
-
function resolveFilePath2(
|
|
5248
|
-
if (!
|
|
5282
|
+
function resolveFilePath2(path5) {
|
|
5283
|
+
if (!path5)
|
|
5249
5284
|
return null;
|
|
5250
|
-
if (
|
|
5251
|
-
return
|
|
5252
|
-
return resolve3(ctx.directory,
|
|
5285
|
+
if (path5.startsWith("/"))
|
|
5286
|
+
return path5;
|
|
5287
|
+
return resolve3(ctx.directory, path5);
|
|
5253
5288
|
}
|
|
5254
5289
|
function findReadmeMdUp(startDir) {
|
|
5255
5290
|
const found = [];
|
|
5256
5291
|
let current = startDir;
|
|
5257
5292
|
while (true) {
|
|
5258
|
-
const readmePath =
|
|
5293
|
+
const readmePath = join18(current, README_FILENAME);
|
|
5259
5294
|
if (existsSync13(readmePath)) {
|
|
5260
5295
|
found.push(readmePath);
|
|
5261
5296
|
}
|
|
@@ -5270,7 +5305,7 @@ function createDirectoryReadmeInjectorHook(ctx) {
|
|
|
5270
5305
|
}
|
|
5271
5306
|
return found.reverse();
|
|
5272
5307
|
}
|
|
5273
|
-
function processFilePathForInjection(filePath, sessionID, output) {
|
|
5308
|
+
async function processFilePathForInjection(filePath, sessionID, output) {
|
|
5274
5309
|
const resolved = resolveFilePath2(filePath);
|
|
5275
5310
|
if (!resolved)
|
|
5276
5311
|
return;
|
|
@@ -5283,10 +5318,14 @@ function createDirectoryReadmeInjectorHook(ctx) {
|
|
|
5283
5318
|
continue;
|
|
5284
5319
|
try {
|
|
5285
5320
|
const content = readFileSync7(readmePath, "utf-8");
|
|
5321
|
+
const { result, truncated } = await truncator.truncate(sessionID, content);
|
|
5322
|
+
const truncationNotice = truncated ? `
|
|
5323
|
+
|
|
5324
|
+
[Note: Content was truncated to save context window space. For full context, please read the file directly: ${readmePath}]` : "";
|
|
5286
5325
|
output.output += `
|
|
5287
5326
|
|
|
5288
5327
|
[Project README: ${readmePath}]
|
|
5289
|
-
${
|
|
5328
|
+
${result}${truncationNotice}`;
|
|
5290
5329
|
cache.add(readmeDir);
|
|
5291
5330
|
} catch {}
|
|
5292
5331
|
}
|
|
@@ -5311,14 +5350,14 @@ ${content}`;
|
|
|
5311
5350
|
const toolExecuteAfter = async (input, output) => {
|
|
5312
5351
|
const toolName = input.tool.toLowerCase();
|
|
5313
5352
|
if (toolName === "read") {
|
|
5314
|
-
processFilePathForInjection(output.title, input.sessionID, output);
|
|
5353
|
+
await processFilePathForInjection(output.title, input.sessionID, output);
|
|
5315
5354
|
return;
|
|
5316
5355
|
}
|
|
5317
5356
|
if (toolName === "batch") {
|
|
5318
5357
|
const filePaths = pendingBatchReads.get(input.callID);
|
|
5319
5358
|
if (filePaths) {
|
|
5320
5359
|
for (const filePath of filePaths) {
|
|
5321
|
-
processFilePathForInjection(filePath, input.sessionID, output);
|
|
5360
|
+
await processFilePathForInjection(filePath, input.sessionID, output);
|
|
5322
5361
|
}
|
|
5323
5362
|
pendingBatchReads.delete(input.callID);
|
|
5324
5363
|
}
|
|
@@ -5553,26 +5592,26 @@ var TRUNCATE_CONFIG = {
|
|
|
5553
5592
|
// src/hooks/anthropic-auto-compact/storage.ts
|
|
5554
5593
|
import { existsSync as existsSync14, readdirSync as readdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
|
|
5555
5594
|
import { homedir as homedir5 } from "os";
|
|
5556
|
-
import { join as
|
|
5557
|
-
var OPENCODE_STORAGE5 =
|
|
5595
|
+
import { join as join19 } from "path";
|
|
5596
|
+
var OPENCODE_STORAGE5 = join19(xdgData ?? "", "opencode", "storage");
|
|
5558
5597
|
if (process.platform === "darwin" && !existsSync14(OPENCODE_STORAGE5)) {
|
|
5559
|
-
const localShare =
|
|
5598
|
+
const localShare = join19(homedir5(), ".local", "share", "opencode", "storage");
|
|
5560
5599
|
if (existsSync14(localShare)) {
|
|
5561
5600
|
OPENCODE_STORAGE5 = localShare;
|
|
5562
5601
|
}
|
|
5563
5602
|
}
|
|
5564
|
-
var MESSAGE_STORAGE3 =
|
|
5565
|
-
var PART_STORAGE3 =
|
|
5603
|
+
var MESSAGE_STORAGE3 = join19(OPENCODE_STORAGE5, "message");
|
|
5604
|
+
var PART_STORAGE3 = join19(OPENCODE_STORAGE5, "part");
|
|
5566
5605
|
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.]";
|
|
5567
5606
|
function getMessageDir3(sessionID) {
|
|
5568
5607
|
if (!existsSync14(MESSAGE_STORAGE3))
|
|
5569
5608
|
return "";
|
|
5570
|
-
const directPath =
|
|
5609
|
+
const directPath = join19(MESSAGE_STORAGE3, sessionID);
|
|
5571
5610
|
if (existsSync14(directPath)) {
|
|
5572
5611
|
return directPath;
|
|
5573
5612
|
}
|
|
5574
5613
|
for (const dir of readdirSync4(MESSAGE_STORAGE3)) {
|
|
5575
|
-
const sessionPath =
|
|
5614
|
+
const sessionPath = join19(MESSAGE_STORAGE3, dir, sessionID);
|
|
5576
5615
|
if (existsSync14(sessionPath)) {
|
|
5577
5616
|
return sessionPath;
|
|
5578
5617
|
}
|
|
@@ -5596,14 +5635,14 @@ function findToolResultsBySize(sessionID) {
|
|
|
5596
5635
|
const messageIds = getMessageIds(sessionID);
|
|
5597
5636
|
const results = [];
|
|
5598
5637
|
for (const messageID of messageIds) {
|
|
5599
|
-
const partDir =
|
|
5638
|
+
const partDir = join19(PART_STORAGE3, messageID);
|
|
5600
5639
|
if (!existsSync14(partDir))
|
|
5601
5640
|
continue;
|
|
5602
5641
|
for (const file of readdirSync4(partDir)) {
|
|
5603
5642
|
if (!file.endsWith(".json"))
|
|
5604
5643
|
continue;
|
|
5605
5644
|
try {
|
|
5606
|
-
const partPath =
|
|
5645
|
+
const partPath = join19(partDir, file);
|
|
5607
5646
|
const content = readFileSync8(partPath, "utf-8");
|
|
5608
5647
|
const part = JSON.parse(content);
|
|
5609
5648
|
if (part.type === "tool" && part.state?.output && !part.truncated) {
|
|
@@ -5864,244 +5903,250 @@ async function fixEmptyMessages(sessionID, autoCompactState, client, messageInde
|
|
|
5864
5903
|
}
|
|
5865
5904
|
async function executeCompact(sessionID, msg, autoCompactState, client, directory, experimental) {
|
|
5866
5905
|
if (autoCompactState.compactionInProgress.has(sessionID)) {
|
|
5906
|
+
await client.tui.showToast({
|
|
5907
|
+
body: {
|
|
5908
|
+
title: "Compact In Progress",
|
|
5909
|
+
message: "Recovery already running. Please wait or start new session if stuck.",
|
|
5910
|
+
variant: "warning",
|
|
5911
|
+
duration: 5000
|
|
5912
|
+
}
|
|
5913
|
+
}).catch(() => {});
|
|
5867
5914
|
return;
|
|
5868
5915
|
}
|
|
5869
5916
|
autoCompactState.compactionInProgress.add(sessionID);
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
}
|
|
5905
|
-
} else {
|
|
5906
|
-
await client.tui.showToast({
|
|
5907
|
-
body: {
|
|
5908
|
-
title: "Truncation Skipped",
|
|
5909
|
-
message: "No tool outputs found to truncate.",
|
|
5910
|
-
variant: "warning",
|
|
5911
|
-
duration: 3000
|
|
5917
|
+
try {
|
|
5918
|
+
const errorData = autoCompactState.errorDataBySession.get(sessionID);
|
|
5919
|
+
const truncateState = getOrCreateTruncateState(autoCompactState, sessionID);
|
|
5920
|
+
if (experimental?.aggressive_truncation && errorData?.currentTokens && errorData?.maxTokens && errorData.currentTokens > errorData.maxTokens && truncateState.truncateAttempt < TRUNCATE_CONFIG.maxTruncateAttempts) {
|
|
5921
|
+
log("[auto-compact] aggressive truncation triggered (experimental)", {
|
|
5922
|
+
currentTokens: errorData.currentTokens,
|
|
5923
|
+
maxTokens: errorData.maxTokens,
|
|
5924
|
+
targetRatio: TRUNCATE_CONFIG.targetTokenRatio
|
|
5925
|
+
});
|
|
5926
|
+
const aggressiveResult = truncateUntilTargetTokens(sessionID, errorData.currentTokens, errorData.maxTokens, TRUNCATE_CONFIG.targetTokenRatio, TRUNCATE_CONFIG.charsPerToken);
|
|
5927
|
+
if (aggressiveResult.truncatedCount > 0) {
|
|
5928
|
+
truncateState.truncateAttempt += aggressiveResult.truncatedCount;
|
|
5929
|
+
const toolNames = aggressiveResult.truncatedTools.map((t) => t.toolName).join(", ");
|
|
5930
|
+
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...`;
|
|
5931
|
+
await client.tui.showToast({
|
|
5932
|
+
body: {
|
|
5933
|
+
title: aggressiveResult.sufficient ? "Aggressive Truncation" : "Partial Truncation",
|
|
5934
|
+
message: `${statusMsg}: ${toolNames}`,
|
|
5935
|
+
variant: "warning",
|
|
5936
|
+
duration: 4000
|
|
5937
|
+
}
|
|
5938
|
+
}).catch(() => {});
|
|
5939
|
+
log("[auto-compact] aggressive truncation completed", aggressiveResult);
|
|
5940
|
+
if (aggressiveResult.sufficient) {
|
|
5941
|
+
setTimeout(async () => {
|
|
5942
|
+
try {
|
|
5943
|
+
await client.session.prompt_async({
|
|
5944
|
+
path: { sessionID },
|
|
5945
|
+
body: { parts: [{ type: "text", text: "Continue" }] },
|
|
5946
|
+
query: { directory }
|
|
5947
|
+
});
|
|
5948
|
+
} catch {}
|
|
5949
|
+
}, 500);
|
|
5950
|
+
return;
|
|
5912
5951
|
}
|
|
5913
|
-
}
|
|
5914
|
-
}
|
|
5915
|
-
}
|
|
5916
|
-
let skipSummarize = false;
|
|
5917
|
-
if (truncateState.truncateAttempt < TRUNCATE_CONFIG.maxTruncateAttempts) {
|
|
5918
|
-
const largest = findLargestToolResult(sessionID);
|
|
5919
|
-
if (largest && largest.outputSize >= TRUNCATE_CONFIG.minOutputSizeToTruncate) {
|
|
5920
|
-
const result = truncateToolResult(largest.partPath);
|
|
5921
|
-
if (result.success) {
|
|
5922
|
-
truncateState.truncateAttempt++;
|
|
5923
|
-
truncateState.lastTruncatedPartId = largest.partId;
|
|
5952
|
+
} else {
|
|
5924
5953
|
await client.tui.showToast({
|
|
5925
5954
|
body: {
|
|
5926
|
-
title: "
|
|
5927
|
-
message:
|
|
5955
|
+
title: "Truncation Skipped",
|
|
5956
|
+
message: "No tool outputs found to truncate.",
|
|
5928
5957
|
variant: "warning",
|
|
5929
5958
|
duration: 3000
|
|
5930
5959
|
}
|
|
5931
5960
|
}).catch(() => {});
|
|
5932
|
-
autoCompactState.compactionInProgress.delete(sessionID);
|
|
5933
|
-
setTimeout(async () => {
|
|
5934
|
-
try {
|
|
5935
|
-
await client.session.prompt_async({
|
|
5936
|
-
path: { sessionID },
|
|
5937
|
-
body: { parts: [{ type: "text", text: "Continue" }] },
|
|
5938
|
-
query: { directory }
|
|
5939
|
-
});
|
|
5940
|
-
} catch {}
|
|
5941
|
-
}, 500);
|
|
5942
|
-
return;
|
|
5943
5961
|
}
|
|
5944
|
-
} else if (errorData?.currentTokens && errorData?.maxTokens && errorData.currentTokens > errorData.maxTokens) {
|
|
5945
|
-
skipSummarize = true;
|
|
5946
|
-
await client.tui.showToast({
|
|
5947
|
-
body: {
|
|
5948
|
-
title: "Summarize Skipped",
|
|
5949
|
-
message: `Over token limit (${errorData.currentTokens}/${errorData.maxTokens}) with nothing to truncate. Going to revert...`,
|
|
5950
|
-
variant: "warning",
|
|
5951
|
-
duration: 3000
|
|
5952
|
-
}
|
|
5953
|
-
}).catch(() => {});
|
|
5954
|
-
} else if (!errorData?.currentTokens) {
|
|
5955
|
-
await client.tui.showToast({
|
|
5956
|
-
body: {
|
|
5957
|
-
title: "Truncation Skipped",
|
|
5958
|
-
message: "No large tool outputs found.",
|
|
5959
|
-
variant: "warning",
|
|
5960
|
-
duration: 3000
|
|
5961
|
-
}
|
|
5962
|
-
}).catch(() => {});
|
|
5963
5962
|
}
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
|
|
5974
|
-
|
|
5975
|
-
|
|
5976
|
-
|
|
5977
|
-
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
|
|
5983
|
-
|
|
5963
|
+
let skipSummarize = false;
|
|
5964
|
+
if (truncateState.truncateAttempt < TRUNCATE_CONFIG.maxTruncateAttempts) {
|
|
5965
|
+
const largest = findLargestToolResult(sessionID);
|
|
5966
|
+
if (largest && largest.outputSize >= TRUNCATE_CONFIG.minOutputSizeToTruncate) {
|
|
5967
|
+
const result = truncateToolResult(largest.partPath);
|
|
5968
|
+
if (result.success) {
|
|
5969
|
+
truncateState.truncateAttempt++;
|
|
5970
|
+
truncateState.lastTruncatedPartId = largest.partId;
|
|
5971
|
+
await client.tui.showToast({
|
|
5972
|
+
body: {
|
|
5973
|
+
title: "Truncating Large Output",
|
|
5974
|
+
message: `Truncated ${result.toolName} (${formatBytes(result.originalSize ?? 0)}). Retrying...`,
|
|
5975
|
+
variant: "warning",
|
|
5976
|
+
duration: 3000
|
|
5977
|
+
}
|
|
5978
|
+
}).catch(() => {});
|
|
5979
|
+
setTimeout(async () => {
|
|
5980
|
+
try {
|
|
5981
|
+
await client.session.prompt_async({
|
|
5982
|
+
path: { sessionID },
|
|
5983
|
+
body: { parts: [{ type: "text", text: "Continue" }] },
|
|
5984
|
+
query: { directory }
|
|
5985
|
+
});
|
|
5986
|
+
} catch {}
|
|
5987
|
+
}, 500);
|
|
5988
|
+
return;
|
|
5984
5989
|
}
|
|
5985
|
-
}
|
|
5986
|
-
|
|
5987
|
-
return;
|
|
5988
|
-
}
|
|
5989
|
-
}
|
|
5990
|
-
if (Date.now() - retryState.lastAttemptTime > 300000) {
|
|
5991
|
-
retryState.attempt = 0;
|
|
5992
|
-
autoCompactState.fallbackStateBySession.delete(sessionID);
|
|
5993
|
-
autoCompactState.truncateStateBySession.delete(sessionID);
|
|
5994
|
-
}
|
|
5995
|
-
if (!skipSummarize && retryState.attempt < RETRY_CONFIG.maxAttempts) {
|
|
5996
|
-
retryState.attempt++;
|
|
5997
|
-
retryState.lastAttemptTime = Date.now();
|
|
5998
|
-
const providerID = msg.providerID;
|
|
5999
|
-
const modelID = msg.modelID;
|
|
6000
|
-
if (providerID && modelID) {
|
|
6001
|
-
try {
|
|
5990
|
+
} else if (errorData?.currentTokens && errorData?.maxTokens && errorData.currentTokens > errorData.maxTokens) {
|
|
5991
|
+
skipSummarize = true;
|
|
6002
5992
|
await client.tui.showToast({
|
|
6003
5993
|
body: {
|
|
6004
|
-
title: "
|
|
6005
|
-
message: `
|
|
5994
|
+
title: "Summarize Skipped",
|
|
5995
|
+
message: `Over token limit (${errorData.currentTokens}/${errorData.maxTokens}) with nothing to truncate. Going to revert...`,
|
|
5996
|
+
variant: "warning",
|
|
5997
|
+
duration: 3000
|
|
5998
|
+
}
|
|
5999
|
+
}).catch(() => {});
|
|
6000
|
+
} else if (!errorData?.currentTokens) {
|
|
6001
|
+
await client.tui.showToast({
|
|
6002
|
+
body: {
|
|
6003
|
+
title: "Truncation Skipped",
|
|
6004
|
+
message: "No large tool outputs found.",
|
|
6006
6005
|
variant: "warning",
|
|
6007
6006
|
duration: 3000
|
|
6008
6007
|
}
|
|
6009
6008
|
}).catch(() => {});
|
|
6010
|
-
await client.session.summarize({
|
|
6011
|
-
path: { id: sessionID },
|
|
6012
|
-
body: { providerID, modelID },
|
|
6013
|
-
query: { directory }
|
|
6014
|
-
});
|
|
6015
|
-
autoCompactState.compactionInProgress.delete(sessionID);
|
|
6016
|
-
setTimeout(async () => {
|
|
6017
|
-
try {
|
|
6018
|
-
await client.session.prompt_async({
|
|
6019
|
-
path: { sessionID },
|
|
6020
|
-
body: { parts: [{ type: "text", text: "Continue" }] },
|
|
6021
|
-
query: { directory }
|
|
6022
|
-
});
|
|
6023
|
-
} catch {}
|
|
6024
|
-
}, 500);
|
|
6025
|
-
return;
|
|
6026
|
-
} catch {
|
|
6027
|
-
autoCompactState.compactionInProgress.delete(sessionID);
|
|
6028
|
-
const delay = RETRY_CONFIG.initialDelayMs * Math.pow(RETRY_CONFIG.backoffFactor, retryState.attempt - 1);
|
|
6029
|
-
const cappedDelay = Math.min(delay, RETRY_CONFIG.maxDelayMs);
|
|
6030
|
-
setTimeout(() => {
|
|
6031
|
-
executeCompact(sessionID, msg, autoCompactState, client, directory, experimental);
|
|
6032
|
-
}, cappedDelay);
|
|
6033
|
-
return;
|
|
6034
6009
|
}
|
|
6035
|
-
}
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
|
|
6039
|
-
|
|
6040
|
-
|
|
6041
|
-
|
|
6010
|
+
}
|
|
6011
|
+
const retryState = getOrCreateRetryState(autoCompactState, sessionID);
|
|
6012
|
+
if (errorData?.errorType?.includes("non-empty content")) {
|
|
6013
|
+
const attempt = getOrCreateEmptyContentAttempt(autoCompactState, sessionID);
|
|
6014
|
+
if (attempt < 3) {
|
|
6015
|
+
const fixed = await fixEmptyMessages(sessionID, autoCompactState, client, errorData.messageIndex);
|
|
6016
|
+
if (fixed) {
|
|
6017
|
+
setTimeout(() => {
|
|
6018
|
+
executeCompact(sessionID, msg, autoCompactState, client, directory, experimental);
|
|
6019
|
+
}, 500);
|
|
6020
|
+
return;
|
|
6042
6021
|
}
|
|
6043
|
-
}
|
|
6022
|
+
} else {
|
|
6023
|
+
await client.tui.showToast({
|
|
6024
|
+
body: {
|
|
6025
|
+
title: "Recovery Failed",
|
|
6026
|
+
message: "Max recovery attempts (3) reached for empty content error. Please start a new session.",
|
|
6027
|
+
variant: "error",
|
|
6028
|
+
duration: 1e4
|
|
6029
|
+
}
|
|
6030
|
+
}).catch(() => {});
|
|
6031
|
+
return;
|
|
6032
|
+
}
|
|
6044
6033
|
}
|
|
6045
|
-
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
|
|
6034
|
+
if (Date.now() - retryState.lastAttemptTime > 300000) {
|
|
6035
|
+
retryState.attempt = 0;
|
|
6036
|
+
autoCompactState.fallbackStateBySession.delete(sessionID);
|
|
6037
|
+
autoCompactState.truncateStateBySession.delete(sessionID);
|
|
6038
|
+
}
|
|
6039
|
+
if (!skipSummarize && retryState.attempt < RETRY_CONFIG.maxAttempts) {
|
|
6040
|
+
retryState.attempt++;
|
|
6041
|
+
retryState.lastAttemptTime = Date.now();
|
|
6042
|
+
const providerID = msg.providerID;
|
|
6043
|
+
const modelID = msg.modelID;
|
|
6044
|
+
if (providerID && modelID) {
|
|
6045
|
+
try {
|
|
6046
|
+
await client.tui.showToast({
|
|
6047
|
+
body: {
|
|
6048
|
+
title: "Auto Compact",
|
|
6049
|
+
message: `Summarizing session (attempt ${retryState.attempt}/${RETRY_CONFIG.maxAttempts})...`,
|
|
6050
|
+
variant: "warning",
|
|
6051
|
+
duration: 3000
|
|
6052
|
+
}
|
|
6053
|
+
}).catch(() => {});
|
|
6054
|
+
await client.session.summarize({
|
|
6055
|
+
path: { id: sessionID },
|
|
6056
|
+
body: { providerID, modelID },
|
|
6057
|
+
query: { directory }
|
|
6058
|
+
});
|
|
6059
|
+
setTimeout(async () => {
|
|
6060
|
+
try {
|
|
6061
|
+
await client.session.prompt_async({
|
|
6062
|
+
path: { sessionID },
|
|
6063
|
+
body: { parts: [{ type: "text", text: "Continue" }] },
|
|
6064
|
+
query: { directory }
|
|
6065
|
+
});
|
|
6066
|
+
} catch {}
|
|
6067
|
+
}, 500);
|
|
6068
|
+
return;
|
|
6069
|
+
} catch {
|
|
6070
|
+
const delay = RETRY_CONFIG.initialDelayMs * Math.pow(RETRY_CONFIG.backoffFactor, retryState.attempt - 1);
|
|
6071
|
+
const cappedDelay = Math.min(delay, RETRY_CONFIG.maxDelayMs);
|
|
6072
|
+
setTimeout(() => {
|
|
6073
|
+
executeCompact(sessionID, msg, autoCompactState, client, directory, experimental);
|
|
6074
|
+
}, cappedDelay);
|
|
6075
|
+
return;
|
|
6076
|
+
}
|
|
6077
|
+
} else {
|
|
6051
6078
|
await client.tui.showToast({
|
|
6052
6079
|
body: {
|
|
6053
|
-
title: "
|
|
6054
|
-
message: "
|
|
6080
|
+
title: "Summarize Skipped",
|
|
6081
|
+
message: "Missing providerID or modelID. Skipping to revert...",
|
|
6055
6082
|
variant: "warning",
|
|
6056
6083
|
duration: 3000
|
|
6057
6084
|
}
|
|
6058
6085
|
}).catch(() => {});
|
|
6059
|
-
|
|
6086
|
+
}
|
|
6087
|
+
}
|
|
6088
|
+
const fallbackState = getOrCreateFallbackState(autoCompactState, sessionID);
|
|
6089
|
+
if (fallbackState.revertAttempt < FALLBACK_CONFIG.maxRevertAttempts) {
|
|
6090
|
+
const pair = await getLastMessagePair(sessionID, client, directory);
|
|
6091
|
+
if (pair) {
|
|
6092
|
+
try {
|
|
6093
|
+
await client.tui.showToast({
|
|
6094
|
+
body: {
|
|
6095
|
+
title: "Emergency Recovery",
|
|
6096
|
+
message: "Removing last message pair...",
|
|
6097
|
+
variant: "warning",
|
|
6098
|
+
duration: 3000
|
|
6099
|
+
}
|
|
6100
|
+
}).catch(() => {});
|
|
6101
|
+
if (pair.assistantMessageID) {
|
|
6102
|
+
await client.session.revert({
|
|
6103
|
+
path: { id: sessionID },
|
|
6104
|
+
body: { messageID: pair.assistantMessageID },
|
|
6105
|
+
query: { directory }
|
|
6106
|
+
});
|
|
6107
|
+
}
|
|
6060
6108
|
await client.session.revert({
|
|
6061
6109
|
path: { id: sessionID },
|
|
6062
|
-
body: { messageID: pair.
|
|
6110
|
+
body: { messageID: pair.userMessageID },
|
|
6063
6111
|
query: { directory }
|
|
6064
6112
|
});
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
message: "Could not find last message pair to revert.",
|
|
6090
|
-
variant: "warning",
|
|
6091
|
-
duration: 3000
|
|
6092
|
-
}
|
|
6093
|
-
}).catch(() => {});
|
|
6113
|
+
fallbackState.revertAttempt++;
|
|
6114
|
+
fallbackState.lastRevertedMessageID = pair.userMessageID;
|
|
6115
|
+
clearSessionState(autoCompactState, sessionID);
|
|
6116
|
+
setTimeout(async () => {
|
|
6117
|
+
try {
|
|
6118
|
+
await client.session.prompt_async({
|
|
6119
|
+
path: { sessionID },
|
|
6120
|
+
body: { parts: [{ type: "text", text: "Continue" }] },
|
|
6121
|
+
query: { directory }
|
|
6122
|
+
});
|
|
6123
|
+
} catch {}
|
|
6124
|
+
}, 500);
|
|
6125
|
+
return;
|
|
6126
|
+
} catch {}
|
|
6127
|
+
} else {
|
|
6128
|
+
await client.tui.showToast({
|
|
6129
|
+
body: {
|
|
6130
|
+
title: "Revert Skipped",
|
|
6131
|
+
message: "Could not find last message pair to revert.",
|
|
6132
|
+
variant: "warning",
|
|
6133
|
+
duration: 3000
|
|
6134
|
+
}
|
|
6135
|
+
}).catch(() => {});
|
|
6136
|
+
}
|
|
6094
6137
|
}
|
|
6138
|
+
clearSessionState(autoCompactState, sessionID);
|
|
6139
|
+
await client.tui.showToast({
|
|
6140
|
+
body: {
|
|
6141
|
+
title: "Auto Compact Failed",
|
|
6142
|
+
message: "All recovery attempts failed. Please start a new session.",
|
|
6143
|
+
variant: "error",
|
|
6144
|
+
duration: 5000
|
|
6145
|
+
}
|
|
6146
|
+
}).catch(() => {});
|
|
6147
|
+
} finally {
|
|
6148
|
+
autoCompactState.compactionInProgress.delete(sessionID);
|
|
6095
6149
|
}
|
|
6096
|
-
clearSessionState(autoCompactState, sessionID);
|
|
6097
|
-
await client.tui.showToast({
|
|
6098
|
-
body: {
|
|
6099
|
-
title: "Auto Compact Failed",
|
|
6100
|
-
message: "All recovery attempts failed. Please start a new session.",
|
|
6101
|
-
variant: "error",
|
|
6102
|
-
duration: 5000
|
|
6103
|
-
}
|
|
6104
|
-
}).catch(() => {});
|
|
6105
6150
|
}
|
|
6106
6151
|
|
|
6107
6152
|
// src/hooks/anthropic-auto-compact/index.ts
|
|
@@ -6211,7 +6256,7 @@ function createAnthropicAutoCompactHook(ctx, options) {
|
|
|
6211
6256
|
}
|
|
6212
6257
|
// src/hooks/preemptive-compaction/index.ts
|
|
6213
6258
|
import { existsSync as existsSync15, readdirSync as readdirSync5 } from "fs";
|
|
6214
|
-
import { join as
|
|
6259
|
+
import { join as join20 } from "path";
|
|
6215
6260
|
|
|
6216
6261
|
// src/hooks/preemptive-compaction/constants.ts
|
|
6217
6262
|
var DEFAULT_THRESHOLD = 0.85;
|
|
@@ -6227,11 +6272,11 @@ function isSupportedModel(modelID) {
|
|
|
6227
6272
|
function getMessageDir4(sessionID) {
|
|
6228
6273
|
if (!existsSync15(MESSAGE_STORAGE))
|
|
6229
6274
|
return null;
|
|
6230
|
-
const directPath =
|
|
6275
|
+
const directPath = join20(MESSAGE_STORAGE, sessionID);
|
|
6231
6276
|
if (existsSync15(directPath))
|
|
6232
6277
|
return directPath;
|
|
6233
6278
|
for (const dir of readdirSync5(MESSAGE_STORAGE)) {
|
|
6234
|
-
const sessionPath =
|
|
6279
|
+
const sessionPath = join20(MESSAGE_STORAGE, dir, sessionID);
|
|
6235
6280
|
if (existsSync15(sessionPath))
|
|
6236
6281
|
return sessionPath;
|
|
6237
6282
|
}
|
|
@@ -6717,7 +6762,7 @@ function createThinkModeHook() {
|
|
|
6717
6762
|
}
|
|
6718
6763
|
// src/hooks/claude-code-hooks/config.ts
|
|
6719
6764
|
import { homedir as homedir6 } from "os";
|
|
6720
|
-
import { join as
|
|
6765
|
+
import { join as join21 } from "path";
|
|
6721
6766
|
import { existsSync as existsSync16 } from "fs";
|
|
6722
6767
|
function normalizeHookMatcher(raw) {
|
|
6723
6768
|
return {
|
|
@@ -6744,9 +6789,9 @@ function normalizeHooksConfig(raw) {
|
|
|
6744
6789
|
function getClaudeSettingsPaths(customPath) {
|
|
6745
6790
|
const home = homedir6();
|
|
6746
6791
|
const paths = [
|
|
6747
|
-
|
|
6748
|
-
|
|
6749
|
-
|
|
6792
|
+
join21(home, ".claude", "settings.json"),
|
|
6793
|
+
join21(process.cwd(), ".claude", "settings.json"),
|
|
6794
|
+
join21(process.cwd(), ".claude", "settings.local.json")
|
|
6750
6795
|
];
|
|
6751
6796
|
if (customPath && existsSync16(customPath)) {
|
|
6752
6797
|
paths.unshift(customPath);
|
|
@@ -6792,20 +6837,20 @@ async function loadClaudeHooksConfig(customSettingsPath) {
|
|
|
6792
6837
|
// src/hooks/claude-code-hooks/config-loader.ts
|
|
6793
6838
|
import { existsSync as existsSync17 } from "fs";
|
|
6794
6839
|
import { homedir as homedir7 } from "os";
|
|
6795
|
-
import { join as
|
|
6796
|
-
var USER_CONFIG_PATH =
|
|
6840
|
+
import { join as join22 } from "path";
|
|
6841
|
+
var USER_CONFIG_PATH = join22(homedir7(), ".config", "opencode", "opencode-cc-plugin.json");
|
|
6797
6842
|
function getProjectConfigPath() {
|
|
6798
|
-
return
|
|
6843
|
+
return join22(process.cwd(), ".opencode", "opencode-cc-plugin.json");
|
|
6799
6844
|
}
|
|
6800
|
-
async function loadConfigFromPath(
|
|
6801
|
-
if (!existsSync17(
|
|
6845
|
+
async function loadConfigFromPath(path5) {
|
|
6846
|
+
if (!existsSync17(path5)) {
|
|
6802
6847
|
return null;
|
|
6803
6848
|
}
|
|
6804
6849
|
try {
|
|
6805
|
-
const content = await Bun.file(
|
|
6850
|
+
const content = await Bun.file(path5).text();
|
|
6806
6851
|
return JSON.parse(content);
|
|
6807
6852
|
} catch (error) {
|
|
6808
|
-
log("Failed to load config", { path:
|
|
6853
|
+
log("Failed to load config", { path: path5, error });
|
|
6809
6854
|
return null;
|
|
6810
6855
|
}
|
|
6811
6856
|
}
|
|
@@ -6978,13 +7023,13 @@ async function executePreToolUseHooks(ctx, config, extendedConfig) {
|
|
|
6978
7023
|
}
|
|
6979
7024
|
|
|
6980
7025
|
// src/hooks/claude-code-hooks/transcript.ts
|
|
6981
|
-
import { join as
|
|
7026
|
+
import { join as join23 } from "path";
|
|
6982
7027
|
import { mkdirSync as mkdirSync6, appendFileSync as appendFileSync5, existsSync as existsSync18, writeFileSync as writeFileSync6, unlinkSync as unlinkSync5 } from "fs";
|
|
6983
7028
|
import { homedir as homedir8, tmpdir as tmpdir5 } from "os";
|
|
6984
7029
|
import { randomUUID } from "crypto";
|
|
6985
|
-
var TRANSCRIPT_DIR =
|
|
7030
|
+
var TRANSCRIPT_DIR = join23(homedir8(), ".claude", "transcripts");
|
|
6986
7031
|
function getTranscriptPath(sessionId) {
|
|
6987
|
-
return
|
|
7032
|
+
return join23(TRANSCRIPT_DIR, `${sessionId}.jsonl`);
|
|
6988
7033
|
}
|
|
6989
7034
|
function ensureTranscriptDir() {
|
|
6990
7035
|
if (!existsSync18(TRANSCRIPT_DIR)) {
|
|
@@ -6993,10 +7038,10 @@ function ensureTranscriptDir() {
|
|
|
6993
7038
|
}
|
|
6994
7039
|
function appendTranscriptEntry(sessionId, entry) {
|
|
6995
7040
|
ensureTranscriptDir();
|
|
6996
|
-
const
|
|
7041
|
+
const path5 = getTranscriptPath(sessionId);
|
|
6997
7042
|
const line = JSON.stringify(entry) + `
|
|
6998
7043
|
`;
|
|
6999
|
-
appendFileSync5(
|
|
7044
|
+
appendFileSync5(path5, line);
|
|
7000
7045
|
}
|
|
7001
7046
|
function recordToolUse(sessionId, toolName, toolInput) {
|
|
7002
7047
|
appendTranscriptEntry(sessionId, {
|
|
@@ -7074,7 +7119,7 @@ async function buildTranscriptFromSession(client, sessionId, directory, currentT
|
|
|
7074
7119
|
}
|
|
7075
7120
|
};
|
|
7076
7121
|
entries.push(JSON.stringify(currentEntry));
|
|
7077
|
-
const tempPath =
|
|
7122
|
+
const tempPath = join23(tmpdir5(), `opencode-transcript-${sessionId}-${randomUUID()}.jsonl`);
|
|
7078
7123
|
writeFileSync6(tempPath, entries.join(`
|
|
7079
7124
|
`) + `
|
|
7080
7125
|
`);
|
|
@@ -7094,7 +7139,7 @@ async function buildTranscriptFromSession(client, sessionId, directory, currentT
|
|
|
7094
7139
|
]
|
|
7095
7140
|
}
|
|
7096
7141
|
};
|
|
7097
|
-
const tempPath =
|
|
7142
|
+
const tempPath = join23(tmpdir5(), `opencode-transcript-${sessionId}-${randomUUID()}.jsonl`);
|
|
7098
7143
|
writeFileSync6(tempPath, JSON.stringify(currentEntry) + `
|
|
7099
7144
|
`);
|
|
7100
7145
|
return tempPath;
|
|
@@ -7103,11 +7148,11 @@ async function buildTranscriptFromSession(client, sessionId, directory, currentT
|
|
|
7103
7148
|
}
|
|
7104
7149
|
}
|
|
7105
7150
|
}
|
|
7106
|
-
function deleteTempTranscript(
|
|
7107
|
-
if (!
|
|
7151
|
+
function deleteTempTranscript(path5) {
|
|
7152
|
+
if (!path5)
|
|
7108
7153
|
return;
|
|
7109
7154
|
try {
|
|
7110
|
-
unlinkSync5(
|
|
7155
|
+
unlinkSync5(path5);
|
|
7111
7156
|
} catch {}
|
|
7112
7157
|
}
|
|
7113
7158
|
|
|
@@ -7306,11 +7351,11 @@ ${USER_PROMPT_SUBMIT_TAG_CLOSE}`);
|
|
|
7306
7351
|
}
|
|
7307
7352
|
|
|
7308
7353
|
// src/hooks/claude-code-hooks/todo.ts
|
|
7309
|
-
import { join as
|
|
7354
|
+
import { join as join24 } from "path";
|
|
7310
7355
|
import { homedir as homedir9 } from "os";
|
|
7311
|
-
var TODO_DIR =
|
|
7356
|
+
var TODO_DIR = join24(homedir9(), ".claude", "todos");
|
|
7312
7357
|
function getTodoPath(sessionId) {
|
|
7313
|
-
return
|
|
7358
|
+
return join24(TODO_DIR, `${sessionId}-agent-${sessionId}.json`);
|
|
7314
7359
|
}
|
|
7315
7360
|
|
|
7316
7361
|
// src/hooks/claude-code-hooks/stop.ts
|
|
@@ -7745,12 +7790,12 @@ import {
|
|
|
7745
7790
|
realpathSync,
|
|
7746
7791
|
statSync as statSync2
|
|
7747
7792
|
} from "fs";
|
|
7748
|
-
import { dirname as dirname4, join as
|
|
7793
|
+
import { dirname as dirname4, join as join26, relative } from "path";
|
|
7749
7794
|
|
|
7750
7795
|
// src/hooks/rules-injector/constants.ts
|
|
7751
|
-
import { join as
|
|
7752
|
-
var OPENCODE_STORAGE6 =
|
|
7753
|
-
var RULES_INJECTOR_STORAGE =
|
|
7796
|
+
import { join as join25 } from "path";
|
|
7797
|
+
var OPENCODE_STORAGE6 = join25(xdgData ?? "", "opencode", "storage");
|
|
7798
|
+
var RULES_INJECTOR_STORAGE = join25(OPENCODE_STORAGE6, "rules-injector");
|
|
7754
7799
|
var PROJECT_MARKERS = [
|
|
7755
7800
|
".git",
|
|
7756
7801
|
"pyproject.toml",
|
|
@@ -7777,7 +7822,7 @@ function findProjectRoot(startPath) {
|
|
|
7777
7822
|
}
|
|
7778
7823
|
while (true) {
|
|
7779
7824
|
for (const marker of PROJECT_MARKERS) {
|
|
7780
|
-
const markerPath =
|
|
7825
|
+
const markerPath = join26(current, marker);
|
|
7781
7826
|
if (existsSync19(markerPath)) {
|
|
7782
7827
|
return current;
|
|
7783
7828
|
}
|
|
@@ -7795,7 +7840,7 @@ function findRuleFilesRecursive(dir, results) {
|
|
|
7795
7840
|
try {
|
|
7796
7841
|
const entries = readdirSync6(dir, { withFileTypes: true });
|
|
7797
7842
|
for (const entry of entries) {
|
|
7798
|
-
const fullPath =
|
|
7843
|
+
const fullPath = join26(dir, entry.name);
|
|
7799
7844
|
if (entry.isDirectory()) {
|
|
7800
7845
|
findRuleFilesRecursive(fullPath, results);
|
|
7801
7846
|
} else if (entry.isFile()) {
|
|
@@ -7821,7 +7866,7 @@ function findRuleFiles(projectRoot, homeDir, currentFile) {
|
|
|
7821
7866
|
let distance = 0;
|
|
7822
7867
|
while (true) {
|
|
7823
7868
|
for (const [parent, subdir] of PROJECT_RULE_SUBDIRS) {
|
|
7824
|
-
const ruleDir =
|
|
7869
|
+
const ruleDir = join26(currentDir, parent, subdir);
|
|
7825
7870
|
const files = [];
|
|
7826
7871
|
findRuleFilesRecursive(ruleDir, files);
|
|
7827
7872
|
for (const filePath of files) {
|
|
@@ -7845,7 +7890,7 @@ function findRuleFiles(projectRoot, homeDir, currentFile) {
|
|
|
7845
7890
|
currentDir = parentDir;
|
|
7846
7891
|
distance++;
|
|
7847
7892
|
}
|
|
7848
|
-
const userRuleDir =
|
|
7893
|
+
const userRuleDir = join26(homeDir, USER_RULE_DIR);
|
|
7849
7894
|
const userFiles = [];
|
|
7850
7895
|
findRuleFilesRecursive(userRuleDir, userFiles);
|
|
7851
7896
|
for (const filePath of userFiles) {
|
|
@@ -8040,9 +8085,9 @@ import {
|
|
|
8040
8085
|
writeFileSync as writeFileSync7,
|
|
8041
8086
|
unlinkSync as unlinkSync6
|
|
8042
8087
|
} from "fs";
|
|
8043
|
-
import { join as
|
|
8088
|
+
import { join as join27 } from "path";
|
|
8044
8089
|
function getStoragePath3(sessionID) {
|
|
8045
|
-
return
|
|
8090
|
+
return join27(RULES_INJECTOR_STORAGE, `${sessionID}.json`);
|
|
8046
8091
|
}
|
|
8047
8092
|
function loadInjectedRules(sessionID) {
|
|
8048
8093
|
const filePath = getStoragePath3(sessionID);
|
|
@@ -8083,20 +8128,21 @@ var TRACKED_TOOLS = ["read", "write", "edit", "multiedit"];
|
|
|
8083
8128
|
function createRulesInjectorHook(ctx) {
|
|
8084
8129
|
const sessionCaches = new Map;
|
|
8085
8130
|
const pendingBatchFiles = new Map;
|
|
8131
|
+
const truncator = createDynamicTruncator(ctx);
|
|
8086
8132
|
function getSessionCache(sessionID) {
|
|
8087
8133
|
if (!sessionCaches.has(sessionID)) {
|
|
8088
8134
|
sessionCaches.set(sessionID, loadInjectedRules(sessionID));
|
|
8089
8135
|
}
|
|
8090
8136
|
return sessionCaches.get(sessionID);
|
|
8091
8137
|
}
|
|
8092
|
-
function resolveFilePath2(
|
|
8093
|
-
if (!
|
|
8138
|
+
function resolveFilePath2(path5) {
|
|
8139
|
+
if (!path5)
|
|
8094
8140
|
return null;
|
|
8095
|
-
if (
|
|
8096
|
-
return
|
|
8097
|
-
return resolve4(ctx.directory,
|
|
8141
|
+
if (path5.startsWith("/"))
|
|
8142
|
+
return path5;
|
|
8143
|
+
return resolve4(ctx.directory, path5);
|
|
8098
8144
|
}
|
|
8099
|
-
function processFilePathForInjection(filePath, sessionID, output) {
|
|
8145
|
+
async function processFilePathForInjection(filePath, sessionID, output) {
|
|
8100
8146
|
const resolved = resolveFilePath2(filePath);
|
|
8101
8147
|
if (!resolved)
|
|
8102
8148
|
return;
|
|
@@ -8132,11 +8178,15 @@ function createRulesInjectorHook(ctx) {
|
|
|
8132
8178
|
return;
|
|
8133
8179
|
toInject.sort((a, b) => a.distance - b.distance);
|
|
8134
8180
|
for (const rule of toInject) {
|
|
8181
|
+
const { result, truncated } = await truncator.truncate(sessionID, rule.content);
|
|
8182
|
+
const truncationNotice = truncated ? `
|
|
8183
|
+
|
|
8184
|
+
[Note: Content was truncated to save context window space. For full context, please read the file directly: ${rule.relativePath}]` : "";
|
|
8135
8185
|
output.output += `
|
|
8136
8186
|
|
|
8137
8187
|
[Rule: ${rule.relativePath}]
|
|
8138
8188
|
[Match: ${rule.matchReason}]
|
|
8139
|
-
${
|
|
8189
|
+
${result}${truncationNotice}`;
|
|
8140
8190
|
}
|
|
8141
8191
|
saveInjectedRules(sessionID, cache2);
|
|
8142
8192
|
}
|
|
@@ -8166,14 +8216,14 @@ ${rule.content}`;
|
|
|
8166
8216
|
const toolExecuteAfter = async (input, output) => {
|
|
8167
8217
|
const toolName = input.tool.toLowerCase();
|
|
8168
8218
|
if (TRACKED_TOOLS.includes(toolName)) {
|
|
8169
|
-
processFilePathForInjection(output.title, input.sessionID, output);
|
|
8219
|
+
await processFilePathForInjection(output.title, input.sessionID, output);
|
|
8170
8220
|
return;
|
|
8171
8221
|
}
|
|
8172
8222
|
if (toolName === "batch") {
|
|
8173
8223
|
const filePaths = pendingBatchFiles.get(input.callID);
|
|
8174
8224
|
if (filePaths) {
|
|
8175
8225
|
for (const filePath of filePaths) {
|
|
8176
|
-
processFilePathForInjection(filePath, input.sessionID, output);
|
|
8226
|
+
await processFilePathForInjection(filePath, input.sessionID, output);
|
|
8177
8227
|
}
|
|
8178
8228
|
pendingBatchFiles.delete(input.callID);
|
|
8179
8229
|
}
|
|
@@ -8213,33 +8263,33 @@ function createBackgroundNotificationHook(manager) {
|
|
|
8213
8263
|
}
|
|
8214
8264
|
// src/hooks/auto-update-checker/checker.ts
|
|
8215
8265
|
import * as fs5 from "fs";
|
|
8216
|
-
import * as
|
|
8266
|
+
import * as path6 from "path";
|
|
8217
8267
|
import { fileURLToPath } from "url";
|
|
8218
8268
|
|
|
8219
8269
|
// src/hooks/auto-update-checker/constants.ts
|
|
8220
|
-
import * as
|
|
8221
|
-
import * as
|
|
8270
|
+
import * as path5 from "path";
|
|
8271
|
+
import * as os5 from "os";
|
|
8222
8272
|
var PACKAGE_NAME = "oh-my-opencode";
|
|
8223
8273
|
var NPM_REGISTRY_URL = `https://registry.npmjs.org/-/package/${PACKAGE_NAME}/dist-tags`;
|
|
8224
8274
|
var NPM_FETCH_TIMEOUT = 5000;
|
|
8225
8275
|
function getCacheDir2() {
|
|
8226
8276
|
if (process.platform === "win32") {
|
|
8227
|
-
return
|
|
8277
|
+
return path5.join(process.env.LOCALAPPDATA ?? os5.homedir(), "opencode");
|
|
8228
8278
|
}
|
|
8229
|
-
return
|
|
8279
|
+
return path5.join(os5.homedir(), ".cache", "opencode");
|
|
8230
8280
|
}
|
|
8231
8281
|
var CACHE_DIR = getCacheDir2();
|
|
8232
|
-
var VERSION_FILE =
|
|
8233
|
-
var INSTALLED_PACKAGE_JSON =
|
|
8282
|
+
var VERSION_FILE = path5.join(CACHE_DIR, "version");
|
|
8283
|
+
var INSTALLED_PACKAGE_JSON = path5.join(CACHE_DIR, "node_modules", PACKAGE_NAME, "package.json");
|
|
8234
8284
|
function getUserConfigDir2() {
|
|
8235
8285
|
if (process.platform === "win32") {
|
|
8236
|
-
return process.env.APPDATA ??
|
|
8286
|
+
return process.env.APPDATA ?? path5.join(os5.homedir(), "AppData", "Roaming");
|
|
8237
8287
|
}
|
|
8238
|
-
return process.env.XDG_CONFIG_HOME ??
|
|
8288
|
+
return process.env.XDG_CONFIG_HOME ?? path5.join(os5.homedir(), ".config");
|
|
8239
8289
|
}
|
|
8240
8290
|
var USER_CONFIG_DIR = getUserConfigDir2();
|
|
8241
|
-
var USER_OPENCODE_CONFIG =
|
|
8242
|
-
var USER_OPENCODE_CONFIG_JSONC =
|
|
8291
|
+
var USER_OPENCODE_CONFIG = path5.join(USER_CONFIG_DIR, "opencode", "opencode.json");
|
|
8292
|
+
var USER_OPENCODE_CONFIG_JSONC = path5.join(USER_CONFIG_DIR, "opencode", "opencode.jsonc");
|
|
8243
8293
|
|
|
8244
8294
|
// src/hooks/auto-update-checker/checker.ts
|
|
8245
8295
|
function stripJsonComments(json) {
|
|
@@ -8247,8 +8297,8 @@ function stripJsonComments(json) {
|
|
|
8247
8297
|
}
|
|
8248
8298
|
function getConfigPaths(directory) {
|
|
8249
8299
|
return [
|
|
8250
|
-
|
|
8251
|
-
|
|
8300
|
+
path6.join(directory, ".opencode", "opencode.json"),
|
|
8301
|
+
path6.join(directory, ".opencode", "opencode.jsonc"),
|
|
8252
8302
|
USER_OPENCODE_CONFIG,
|
|
8253
8303
|
USER_OPENCODE_CONFIG_JSONC
|
|
8254
8304
|
];
|
|
@@ -8279,9 +8329,9 @@ function getLocalDevPath(directory) {
|
|
|
8279
8329
|
function findPackageJsonUp(startPath) {
|
|
8280
8330
|
try {
|
|
8281
8331
|
const stat = fs5.statSync(startPath);
|
|
8282
|
-
let dir = stat.isDirectory() ? startPath :
|
|
8332
|
+
let dir = stat.isDirectory() ? startPath : path6.dirname(startPath);
|
|
8283
8333
|
for (let i = 0;i < 10; i++) {
|
|
8284
|
-
const pkgPath =
|
|
8334
|
+
const pkgPath = path6.join(dir, "package.json");
|
|
8285
8335
|
if (fs5.existsSync(pkgPath)) {
|
|
8286
8336
|
try {
|
|
8287
8337
|
const content = fs5.readFileSync(pkgPath, "utf-8");
|
|
@@ -8290,7 +8340,7 @@ function findPackageJsonUp(startPath) {
|
|
|
8290
8340
|
return pkgPath;
|
|
8291
8341
|
} catch {}
|
|
8292
8342
|
}
|
|
8293
|
-
const parent =
|
|
8343
|
+
const parent = path6.dirname(dir);
|
|
8294
8344
|
if (parent === dir)
|
|
8295
8345
|
break;
|
|
8296
8346
|
dir = parent;
|
|
@@ -8347,7 +8397,7 @@ function getCachedVersion() {
|
|
|
8347
8397
|
}
|
|
8348
8398
|
} catch {}
|
|
8349
8399
|
try {
|
|
8350
|
-
const currentDir =
|
|
8400
|
+
const currentDir = path6.dirname(fileURLToPath(import.meta.url));
|
|
8351
8401
|
const pkgPath = findPackageJsonUp(currentDir);
|
|
8352
8402
|
if (pkgPath) {
|
|
8353
8403
|
const content = fs5.readFileSync(pkgPath, "utf-8");
|
|
@@ -8423,12 +8473,12 @@ async function getLatestVersion() {
|
|
|
8423
8473
|
|
|
8424
8474
|
// src/hooks/auto-update-checker/cache.ts
|
|
8425
8475
|
import * as fs6 from "fs";
|
|
8426
|
-
import * as
|
|
8476
|
+
import * as path7 from "path";
|
|
8427
8477
|
function stripTrailingCommas(json) {
|
|
8428
8478
|
return json.replace(/,(\s*[}\]])/g, "$1");
|
|
8429
8479
|
}
|
|
8430
8480
|
function removeFromBunLock(packageName) {
|
|
8431
|
-
const lockPath =
|
|
8481
|
+
const lockPath = path7.join(CACHE_DIR, "bun.lock");
|
|
8432
8482
|
if (!fs6.existsSync(lockPath))
|
|
8433
8483
|
return false;
|
|
8434
8484
|
try {
|
|
@@ -8454,8 +8504,8 @@ function removeFromBunLock(packageName) {
|
|
|
8454
8504
|
}
|
|
8455
8505
|
function invalidatePackage(packageName = PACKAGE_NAME) {
|
|
8456
8506
|
try {
|
|
8457
|
-
const pkgDir =
|
|
8458
|
-
const pkgJsonPath =
|
|
8507
|
+
const pkgDir = path7.join(CACHE_DIR, "node_modules", packageName);
|
|
8508
|
+
const pkgJsonPath = path7.join(CACHE_DIR, "package.json");
|
|
8459
8509
|
let packageRemoved = false;
|
|
8460
8510
|
let dependencyRemoved = false;
|
|
8461
8511
|
let lockRemoved = false;
|
|
@@ -8649,12 +8699,12 @@ import {
|
|
|
8649
8699
|
writeFileSync as writeFileSync10,
|
|
8650
8700
|
unlinkSync as unlinkSync7
|
|
8651
8701
|
} from "fs";
|
|
8652
|
-
import { join as
|
|
8702
|
+
import { join as join32 } from "path";
|
|
8653
8703
|
|
|
8654
8704
|
// src/hooks/agent-usage-reminder/constants.ts
|
|
8655
|
-
import { join as
|
|
8656
|
-
var OPENCODE_STORAGE7 =
|
|
8657
|
-
var AGENT_USAGE_REMINDER_STORAGE =
|
|
8705
|
+
import { join as join31 } from "path";
|
|
8706
|
+
var OPENCODE_STORAGE7 = join31(xdgData ?? "", "opencode", "storage");
|
|
8707
|
+
var AGENT_USAGE_REMINDER_STORAGE = join31(OPENCODE_STORAGE7, "agent-usage-reminder");
|
|
8658
8708
|
var TARGET_TOOLS = new Set([
|
|
8659
8709
|
"grep",
|
|
8660
8710
|
"safe_grep",
|
|
@@ -8699,7 +8749,7 @@ ALWAYS prefer: Multiple parallel background_task calls > Direct tool calls
|
|
|
8699
8749
|
|
|
8700
8750
|
// src/hooks/agent-usage-reminder/storage.ts
|
|
8701
8751
|
function getStoragePath4(sessionID) {
|
|
8702
|
-
return
|
|
8752
|
+
return join32(AGENT_USAGE_REMINDER_STORAGE, `${sessionID}.json`);
|
|
8703
8753
|
}
|
|
8704
8754
|
function loadAgentUsageState(sessionID) {
|
|
8705
8755
|
const filePath = getStoragePath4(sessionID);
|
|
@@ -8825,6 +8875,7 @@ TELL THE USER WHAT AGENTS YOU WILL LEVERAGE NOW TO SATISFY USER'S REQUEST.
|
|
|
8825
8875
|
|
|
8826
8876
|
## ZERO TOLERANCE FAILURES
|
|
8827
8877
|
- **NO Scope Reduction**: Never make "demo", "skeleton", "simplified", "basic" versions - deliver FULL implementation
|
|
8878
|
+
- **NO MockUp Work**: When user asked you to do "port A", you must "port A", fully, 100%. No Extra feature, No reduced feature, no mock data, fully working 100% port.
|
|
8828
8879
|
- **NO Partial Completion**: Never stop at 60-80% saying "you can extend this..." - finish 100%
|
|
8829
8880
|
- **NO Assumed Shortcuts**: Never skip requirements you deem "optional" or "can be added later"
|
|
8830
8881
|
- **NO Premature Stopping**: Never declare done until ALL TODOs are completed and verified
|
|
@@ -9021,12 +9072,12 @@ import {
|
|
|
9021
9072
|
writeFileSync as writeFileSync11,
|
|
9022
9073
|
unlinkSync as unlinkSync8
|
|
9023
9074
|
} from "fs";
|
|
9024
|
-
import { join as
|
|
9075
|
+
import { join as join34 } from "path";
|
|
9025
9076
|
|
|
9026
9077
|
// src/hooks/interactive-bash-session/constants.ts
|
|
9027
|
-
import { join as
|
|
9028
|
-
var OPENCODE_STORAGE8 =
|
|
9029
|
-
var INTERACTIVE_BASH_SESSION_STORAGE =
|
|
9078
|
+
import { join as join33 } from "path";
|
|
9079
|
+
var OPENCODE_STORAGE8 = join33(xdgData ?? "", "opencode", "storage");
|
|
9080
|
+
var INTERACTIVE_BASH_SESSION_STORAGE = join33(OPENCODE_STORAGE8, "interactive-bash-session");
|
|
9030
9081
|
var OMO_SESSION_PREFIX = "omo-";
|
|
9031
9082
|
function buildSessionReminderMessage(sessions) {
|
|
9032
9083
|
if (sessions.length === 0)
|
|
@@ -9038,7 +9089,7 @@ function buildSessionReminderMessage(sessions) {
|
|
|
9038
9089
|
|
|
9039
9090
|
// src/hooks/interactive-bash-session/storage.ts
|
|
9040
9091
|
function getStoragePath5(sessionID) {
|
|
9041
|
-
return
|
|
9092
|
+
return join34(INTERACTIVE_BASH_SESSION_STORAGE, `${sessionID}.json`);
|
|
9042
9093
|
}
|
|
9043
9094
|
function loadInteractiveBashSessionState(sessionID) {
|
|
9044
9095
|
const filePath = getStoragePath5(sessionID);
|
|
@@ -11016,7 +11067,7 @@ async function createGoogleAntigravityAuthPlugin({
|
|
|
11016
11067
|
// src/features/claude-code-command-loader/loader.ts
|
|
11017
11068
|
import { existsSync as existsSync25, readdirSync as readdirSync7, readFileSync as readFileSync15 } from "fs";
|
|
11018
11069
|
import { homedir as homedir12 } from "os";
|
|
11019
|
-
import { join as
|
|
11070
|
+
import { join as join35, basename } from "path";
|
|
11020
11071
|
function loadCommandsFromDir(commandsDir, scope) {
|
|
11021
11072
|
if (!existsSync25(commandsDir)) {
|
|
11022
11073
|
return [];
|
|
@@ -11026,7 +11077,7 @@ function loadCommandsFromDir(commandsDir, scope) {
|
|
|
11026
11077
|
for (const entry of entries) {
|
|
11027
11078
|
if (!isMarkdownFile(entry))
|
|
11028
11079
|
continue;
|
|
11029
|
-
const commandPath =
|
|
11080
|
+
const commandPath = join35(commandsDir, entry.name);
|
|
11030
11081
|
const commandName = basename(entry.name, ".md");
|
|
11031
11082
|
try {
|
|
11032
11083
|
const content = readFileSync15(commandPath, "utf-8");
|
|
@@ -11069,29 +11120,29 @@ function commandsToRecord(commands) {
|
|
|
11069
11120
|
return result;
|
|
11070
11121
|
}
|
|
11071
11122
|
function loadUserCommands() {
|
|
11072
|
-
const userCommandsDir =
|
|
11123
|
+
const userCommandsDir = join35(homedir12(), ".claude", "commands");
|
|
11073
11124
|
const commands = loadCommandsFromDir(userCommandsDir, "user");
|
|
11074
11125
|
return commandsToRecord(commands);
|
|
11075
11126
|
}
|
|
11076
11127
|
function loadProjectCommands() {
|
|
11077
|
-
const projectCommandsDir =
|
|
11128
|
+
const projectCommandsDir = join35(process.cwd(), ".claude", "commands");
|
|
11078
11129
|
const commands = loadCommandsFromDir(projectCommandsDir, "project");
|
|
11079
11130
|
return commandsToRecord(commands);
|
|
11080
11131
|
}
|
|
11081
11132
|
function loadOpencodeGlobalCommands() {
|
|
11082
|
-
const opencodeCommandsDir =
|
|
11133
|
+
const opencodeCommandsDir = join35(homedir12(), ".config", "opencode", "command");
|
|
11083
11134
|
const commands = loadCommandsFromDir(opencodeCommandsDir, "opencode");
|
|
11084
11135
|
return commandsToRecord(commands);
|
|
11085
11136
|
}
|
|
11086
11137
|
function loadOpencodeProjectCommands() {
|
|
11087
|
-
const opencodeProjectDir =
|
|
11138
|
+
const opencodeProjectDir = join35(process.cwd(), ".opencode", "command");
|
|
11088
11139
|
const commands = loadCommandsFromDir(opencodeProjectDir, "opencode-project");
|
|
11089
11140
|
return commandsToRecord(commands);
|
|
11090
11141
|
}
|
|
11091
11142
|
// src/features/claude-code-skill-loader/loader.ts
|
|
11092
11143
|
import { existsSync as existsSync26, readdirSync as readdirSync8, readFileSync as readFileSync16 } from "fs";
|
|
11093
11144
|
import { homedir as homedir13 } from "os";
|
|
11094
|
-
import { join as
|
|
11145
|
+
import { join as join36 } from "path";
|
|
11095
11146
|
function loadSkillsFromDir(skillsDir, scope) {
|
|
11096
11147
|
if (!existsSync26(skillsDir)) {
|
|
11097
11148
|
return [];
|
|
@@ -11101,11 +11152,11 @@ function loadSkillsFromDir(skillsDir, scope) {
|
|
|
11101
11152
|
for (const entry of entries) {
|
|
11102
11153
|
if (entry.name.startsWith("."))
|
|
11103
11154
|
continue;
|
|
11104
|
-
const skillPath =
|
|
11155
|
+
const skillPath = join36(skillsDir, entry.name);
|
|
11105
11156
|
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
11106
11157
|
continue;
|
|
11107
11158
|
const resolvedPath = resolveSymlink(skillPath);
|
|
11108
|
-
const skillMdPath =
|
|
11159
|
+
const skillMdPath = join36(resolvedPath, "SKILL.md");
|
|
11109
11160
|
if (!existsSync26(skillMdPath))
|
|
11110
11161
|
continue;
|
|
11111
11162
|
try {
|
|
@@ -11143,7 +11194,7 @@ $ARGUMENTS
|
|
|
11143
11194
|
return skills;
|
|
11144
11195
|
}
|
|
11145
11196
|
function loadUserSkillsAsCommands() {
|
|
11146
|
-
const userSkillsDir =
|
|
11197
|
+
const userSkillsDir = join36(homedir13(), ".claude", "skills");
|
|
11147
11198
|
const skills = loadSkillsFromDir(userSkillsDir, "user");
|
|
11148
11199
|
return skills.reduce((acc, skill) => {
|
|
11149
11200
|
acc[skill.name] = skill.definition;
|
|
@@ -11151,7 +11202,7 @@ function loadUserSkillsAsCommands() {
|
|
|
11151
11202
|
}, {});
|
|
11152
11203
|
}
|
|
11153
11204
|
function loadProjectSkillsAsCommands() {
|
|
11154
|
-
const projectSkillsDir =
|
|
11205
|
+
const projectSkillsDir = join36(process.cwd(), ".claude", "skills");
|
|
11155
11206
|
const skills = loadSkillsFromDir(projectSkillsDir, "project");
|
|
11156
11207
|
return skills.reduce((acc, skill) => {
|
|
11157
11208
|
acc[skill.name] = skill.definition;
|
|
@@ -11161,7 +11212,7 @@ function loadProjectSkillsAsCommands() {
|
|
|
11161
11212
|
// src/features/claude-code-agent-loader/loader.ts
|
|
11162
11213
|
import { existsSync as existsSync27, readdirSync as readdirSync9, readFileSync as readFileSync17 } from "fs";
|
|
11163
11214
|
import { homedir as homedir14 } from "os";
|
|
11164
|
-
import { join as
|
|
11215
|
+
import { join as join37, basename as basename2 } from "path";
|
|
11165
11216
|
function parseToolsConfig(toolsStr) {
|
|
11166
11217
|
if (!toolsStr)
|
|
11167
11218
|
return;
|
|
@@ -11183,7 +11234,7 @@ function loadAgentsFromDir(agentsDir, scope) {
|
|
|
11183
11234
|
for (const entry of entries) {
|
|
11184
11235
|
if (!isMarkdownFile(entry))
|
|
11185
11236
|
continue;
|
|
11186
|
-
const agentPath =
|
|
11237
|
+
const agentPath = join37(agentsDir, entry.name);
|
|
11187
11238
|
const agentName = basename2(entry.name, ".md");
|
|
11188
11239
|
try {
|
|
11189
11240
|
const content = readFileSync17(agentPath, "utf-8");
|
|
@@ -11213,7 +11264,7 @@ function loadAgentsFromDir(agentsDir, scope) {
|
|
|
11213
11264
|
return agents;
|
|
11214
11265
|
}
|
|
11215
11266
|
function loadUserAgents() {
|
|
11216
|
-
const userAgentsDir =
|
|
11267
|
+
const userAgentsDir = join37(homedir14(), ".claude", "agents");
|
|
11217
11268
|
const agents = loadAgentsFromDir(userAgentsDir, "user");
|
|
11218
11269
|
const result = {};
|
|
11219
11270
|
for (const agent of agents) {
|
|
@@ -11222,7 +11273,7 @@ function loadUserAgents() {
|
|
|
11222
11273
|
return result;
|
|
11223
11274
|
}
|
|
11224
11275
|
function loadProjectAgents() {
|
|
11225
|
-
const projectAgentsDir =
|
|
11276
|
+
const projectAgentsDir = join37(process.cwd(), ".claude", "agents");
|
|
11226
11277
|
const agents = loadAgentsFromDir(projectAgentsDir, "project");
|
|
11227
11278
|
const result = {};
|
|
11228
11279
|
for (const agent of agents) {
|
|
@@ -11233,7 +11284,7 @@ function loadProjectAgents() {
|
|
|
11233
11284
|
// src/features/claude-code-mcp-loader/loader.ts
|
|
11234
11285
|
import { existsSync as existsSync28 } from "fs";
|
|
11235
11286
|
import { homedir as homedir15 } from "os";
|
|
11236
|
-
import { join as
|
|
11287
|
+
import { join as join38 } from "path";
|
|
11237
11288
|
|
|
11238
11289
|
// src/features/claude-code-mcp-loader/env-expander.ts
|
|
11239
11290
|
function expandEnvVars(value) {
|
|
@@ -11302,9 +11353,9 @@ function getMcpConfigPaths() {
|
|
|
11302
11353
|
const home = homedir15();
|
|
11303
11354
|
const cwd = process.cwd();
|
|
11304
11355
|
return [
|
|
11305
|
-
{ path:
|
|
11306
|
-
{ path:
|
|
11307
|
-
{ path:
|
|
11356
|
+
{ path: join38(home, ".claude", ".mcp.json"), scope: "user" },
|
|
11357
|
+
{ path: join38(cwd, ".mcp.json"), scope: "project" },
|
|
11358
|
+
{ path: join38(cwd, ".claude", ".mcp.json"), scope: "local" }
|
|
11308
11359
|
];
|
|
11309
11360
|
}
|
|
11310
11361
|
async function loadMcpConfigFile(filePath) {
|
|
@@ -11323,13 +11374,13 @@ async function loadMcpConfigs() {
|
|
|
11323
11374
|
const servers = {};
|
|
11324
11375
|
const loadedServers = [];
|
|
11325
11376
|
const paths = getMcpConfigPaths();
|
|
11326
|
-
for (const { path:
|
|
11327
|
-
const config = await loadMcpConfigFile(
|
|
11377
|
+
for (const { path: path8, scope } of paths) {
|
|
11378
|
+
const config = await loadMcpConfigFile(path8);
|
|
11328
11379
|
if (!config?.mcpServers)
|
|
11329
11380
|
continue;
|
|
11330
11381
|
for (const [name, serverConfig] of Object.entries(config.mcpServers)) {
|
|
11331
11382
|
if (serverConfig.disabled) {
|
|
11332
|
-
log(`Skipping disabled MCP server "${name}"`, { path:
|
|
11383
|
+
log(`Skipping disabled MCP server "${name}"`, { path: path8 });
|
|
11333
11384
|
continue;
|
|
11334
11385
|
}
|
|
11335
11386
|
try {
|
|
@@ -11340,7 +11391,7 @@ async function loadMcpConfigs() {
|
|
|
11340
11391
|
loadedServers.splice(existingIndex, 1);
|
|
11341
11392
|
}
|
|
11342
11393
|
loadedServers.push({ name, scope, config: transformed });
|
|
11343
|
-
log(`Loaded MCP server "${name}" from ${scope}`, { path:
|
|
11394
|
+
log(`Loaded MCP server "${name}" from ${scope}`, { path: path8 });
|
|
11344
11395
|
} catch (error) {
|
|
11345
11396
|
log(`Failed to transform MCP server "${name}"`, error);
|
|
11346
11397
|
}
|
|
@@ -11644,13 +11695,13 @@ var EXT_TO_LANG = {
|
|
|
11644
11695
|
};
|
|
11645
11696
|
// src/tools/lsp/config.ts
|
|
11646
11697
|
import { existsSync as existsSync29, readFileSync as readFileSync18 } from "fs";
|
|
11647
|
-
import { join as
|
|
11698
|
+
import { join as join39 } from "path";
|
|
11648
11699
|
import { homedir as homedir16 } from "os";
|
|
11649
|
-
function loadJsonFile(
|
|
11650
|
-
if (!existsSync29(
|
|
11700
|
+
function loadJsonFile(path8) {
|
|
11701
|
+
if (!existsSync29(path8))
|
|
11651
11702
|
return null;
|
|
11652
11703
|
try {
|
|
11653
|
-
return JSON.parse(readFileSync18(
|
|
11704
|
+
return JSON.parse(readFileSync18(path8, "utf-8"));
|
|
11654
11705
|
} catch {
|
|
11655
11706
|
return null;
|
|
11656
11707
|
}
|
|
@@ -11658,9 +11709,9 @@ function loadJsonFile(path7) {
|
|
|
11658
11709
|
function getConfigPaths2() {
|
|
11659
11710
|
const cwd = process.cwd();
|
|
11660
11711
|
return {
|
|
11661
|
-
project:
|
|
11662
|
-
user:
|
|
11663
|
-
opencode:
|
|
11712
|
+
project: join39(cwd, ".opencode", "oh-my-opencode.json"),
|
|
11713
|
+
user: join39(homedir16(), ".config", "opencode", "oh-my-opencode.json"),
|
|
11714
|
+
opencode: join39(homedir16(), ".config", "opencode", "opencode.json")
|
|
11664
11715
|
};
|
|
11665
11716
|
}
|
|
11666
11717
|
function loadAllConfigs() {
|
|
@@ -11756,18 +11807,18 @@ function isServerInstalled(command) {
|
|
|
11756
11807
|
const pathSeparator = isWindows2 ? ";" : ":";
|
|
11757
11808
|
const paths = pathEnv.split(pathSeparator);
|
|
11758
11809
|
for (const p of paths) {
|
|
11759
|
-
if (existsSync29(
|
|
11810
|
+
if (existsSync29(join39(p, cmd)) || existsSync29(join39(p, cmd + ext))) {
|
|
11760
11811
|
return true;
|
|
11761
11812
|
}
|
|
11762
11813
|
}
|
|
11763
11814
|
const cwd = process.cwd();
|
|
11764
11815
|
const additionalPaths = [
|
|
11765
|
-
|
|
11766
|
-
|
|
11767
|
-
|
|
11768
|
-
|
|
11769
|
-
|
|
11770
|
-
|
|
11816
|
+
join39(cwd, "node_modules", ".bin", cmd),
|
|
11817
|
+
join39(cwd, "node_modules", ".bin", cmd + ext),
|
|
11818
|
+
join39(homedir16(), ".config", "opencode", "bin", cmd),
|
|
11819
|
+
join39(homedir16(), ".config", "opencode", "bin", cmd + ext),
|
|
11820
|
+
join39(homedir16(), ".config", "opencode", "node_modules", ".bin", cmd),
|
|
11821
|
+
join39(homedir16(), ".config", "opencode", "node_modules", ".bin", cmd + ext)
|
|
11771
11822
|
];
|
|
11772
11823
|
for (const p of additionalPaths) {
|
|
11773
11824
|
if (existsSync29(p)) {
|
|
@@ -13379,10 +13430,10 @@ function mergeDefs(...defs) {
|
|
|
13379
13430
|
function cloneDef(schema) {
|
|
13380
13431
|
return mergeDefs(schema._zod.def);
|
|
13381
13432
|
}
|
|
13382
|
-
function getElementAtPath(obj,
|
|
13383
|
-
if (!
|
|
13433
|
+
function getElementAtPath(obj, path8) {
|
|
13434
|
+
if (!path8)
|
|
13384
13435
|
return obj;
|
|
13385
|
-
return
|
|
13436
|
+
return path8.reduce((acc, key) => acc?.[key], obj);
|
|
13386
13437
|
}
|
|
13387
13438
|
function promiseAllObject(promisesObj) {
|
|
13388
13439
|
const keys = Object.keys(promisesObj);
|
|
@@ -13741,11 +13792,11 @@ function aborted(x, startIndex = 0) {
|
|
|
13741
13792
|
}
|
|
13742
13793
|
return false;
|
|
13743
13794
|
}
|
|
13744
|
-
function prefixIssues(
|
|
13795
|
+
function prefixIssues(path8, issues) {
|
|
13745
13796
|
return issues.map((iss) => {
|
|
13746
13797
|
var _a;
|
|
13747
13798
|
(_a = iss).path ?? (_a.path = []);
|
|
13748
|
-
iss.path.unshift(
|
|
13799
|
+
iss.path.unshift(path8);
|
|
13749
13800
|
return iss;
|
|
13750
13801
|
});
|
|
13751
13802
|
}
|
|
@@ -13913,7 +13964,7 @@ function treeifyError(error, _mapper) {
|
|
|
13913
13964
|
return issue2.message;
|
|
13914
13965
|
};
|
|
13915
13966
|
const result = { errors: [] };
|
|
13916
|
-
const processError = (error2,
|
|
13967
|
+
const processError = (error2, path8 = []) => {
|
|
13917
13968
|
var _a, _b;
|
|
13918
13969
|
for (const issue2 of error2.issues) {
|
|
13919
13970
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
@@ -13923,7 +13974,7 @@ function treeifyError(error, _mapper) {
|
|
|
13923
13974
|
} else if (issue2.code === "invalid_element") {
|
|
13924
13975
|
processError({ issues: issue2.issues }, issue2.path);
|
|
13925
13976
|
} else {
|
|
13926
|
-
const fullpath = [...
|
|
13977
|
+
const fullpath = [...path8, ...issue2.path];
|
|
13927
13978
|
if (fullpath.length === 0) {
|
|
13928
13979
|
result.errors.push(mapper(issue2));
|
|
13929
13980
|
continue;
|
|
@@ -13955,8 +14006,8 @@ function treeifyError(error, _mapper) {
|
|
|
13955
14006
|
}
|
|
13956
14007
|
function toDotPath(_path) {
|
|
13957
14008
|
const segs = [];
|
|
13958
|
-
const
|
|
13959
|
-
for (const seg of
|
|
14009
|
+
const path8 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
14010
|
+
for (const seg of path8) {
|
|
13960
14011
|
if (typeof seg === "number")
|
|
13961
14012
|
segs.push(`[${seg}]`);
|
|
13962
14013
|
else if (typeof seg === "symbol")
|
|
@@ -25299,13 +25350,13 @@ var lsp_code_action_resolve = tool({
|
|
|
25299
25350
|
});
|
|
25300
25351
|
// src/tools/ast-grep/constants.ts
|
|
25301
25352
|
import { createRequire as createRequire4 } from "module";
|
|
25302
|
-
import { dirname as dirname6, join as
|
|
25353
|
+
import { dirname as dirname6, join as join41 } from "path";
|
|
25303
25354
|
import { existsSync as existsSync32, statSync as statSync4 } from "fs";
|
|
25304
25355
|
|
|
25305
25356
|
// src/tools/ast-grep/downloader.ts
|
|
25306
25357
|
var {spawn: spawn5 } = globalThis.Bun;
|
|
25307
25358
|
import { existsSync as existsSync31, mkdirSync as mkdirSync10, chmodSync as chmodSync2, unlinkSync as unlinkSync9 } from "fs";
|
|
25308
|
-
import { join as
|
|
25359
|
+
import { join as join40 } from "path";
|
|
25309
25360
|
import { homedir as homedir17 } from "os";
|
|
25310
25361
|
import { createRequire as createRequire3 } from "module";
|
|
25311
25362
|
var REPO2 = "ast-grep/ast-grep";
|
|
@@ -25331,18 +25382,18 @@ var PLATFORM_MAP2 = {
|
|
|
25331
25382
|
function getCacheDir3() {
|
|
25332
25383
|
if (process.platform === "win32") {
|
|
25333
25384
|
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
25334
|
-
const base2 = localAppData ||
|
|
25335
|
-
return
|
|
25385
|
+
const base2 = localAppData || join40(homedir17(), "AppData", "Local");
|
|
25386
|
+
return join40(base2, "oh-my-opencode", "bin");
|
|
25336
25387
|
}
|
|
25337
25388
|
const xdgCache2 = process.env.XDG_CACHE_HOME;
|
|
25338
|
-
const base = xdgCache2 ||
|
|
25339
|
-
return
|
|
25389
|
+
const base = xdgCache2 || join40(homedir17(), ".cache");
|
|
25390
|
+
return join40(base, "oh-my-opencode", "bin");
|
|
25340
25391
|
}
|
|
25341
25392
|
function getBinaryName3() {
|
|
25342
25393
|
return process.platform === "win32" ? "sg.exe" : "sg";
|
|
25343
25394
|
}
|
|
25344
25395
|
function getCachedBinaryPath2() {
|
|
25345
|
-
const binaryPath =
|
|
25396
|
+
const binaryPath = join40(getCacheDir3(), getBinaryName3());
|
|
25346
25397
|
return existsSync31(binaryPath) ? binaryPath : null;
|
|
25347
25398
|
}
|
|
25348
25399
|
async function extractZip2(archivePath, destDir) {
|
|
@@ -25369,12 +25420,12 @@ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
|
|
|
25369
25420
|
}
|
|
25370
25421
|
const cacheDir = getCacheDir3();
|
|
25371
25422
|
const binaryName = getBinaryName3();
|
|
25372
|
-
const binaryPath =
|
|
25423
|
+
const binaryPath = join40(cacheDir, binaryName);
|
|
25373
25424
|
if (existsSync31(binaryPath)) {
|
|
25374
25425
|
return binaryPath;
|
|
25375
25426
|
}
|
|
25376
|
-
const { arch, os:
|
|
25377
|
-
const assetName = `app-${arch}-${
|
|
25427
|
+
const { arch, os: os6 } = platformInfo;
|
|
25428
|
+
const assetName = `app-${arch}-${os6}.zip`;
|
|
25378
25429
|
const downloadUrl = `https://github.com/${REPO2}/releases/download/${version2}/${assetName}`;
|
|
25379
25430
|
console.log(`[oh-my-opencode] Downloading ast-grep binary...`);
|
|
25380
25431
|
try {
|
|
@@ -25385,7 +25436,7 @@ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
|
|
|
25385
25436
|
if (!response2.ok) {
|
|
25386
25437
|
throw new Error(`HTTP ${response2.status}: ${response2.statusText}`);
|
|
25387
25438
|
}
|
|
25388
|
-
const archivePath =
|
|
25439
|
+
const archivePath = join40(cacheDir, assetName);
|
|
25389
25440
|
const arrayBuffer = await response2.arrayBuffer();
|
|
25390
25441
|
await Bun.write(archivePath, arrayBuffer);
|
|
25391
25442
|
await extractZip2(archivePath, cacheDir);
|
|
@@ -25443,7 +25494,7 @@ function findSgCliPathSync() {
|
|
|
25443
25494
|
const require2 = createRequire4(import.meta.url);
|
|
25444
25495
|
const cliPkgPath = require2.resolve("@ast-grep/cli/package.json");
|
|
25445
25496
|
const cliDir = dirname6(cliPkgPath);
|
|
25446
|
-
const sgPath =
|
|
25497
|
+
const sgPath = join41(cliDir, binaryName);
|
|
25447
25498
|
if (existsSync32(sgPath) && isValidBinary(sgPath)) {
|
|
25448
25499
|
return sgPath;
|
|
25449
25500
|
}
|
|
@@ -25455,7 +25506,7 @@ function findSgCliPathSync() {
|
|
|
25455
25506
|
const pkgPath = require2.resolve(`${platformPkg}/package.json`);
|
|
25456
25507
|
const pkgDir = dirname6(pkgPath);
|
|
25457
25508
|
const astGrepName = process.platform === "win32" ? "ast-grep.exe" : "ast-grep";
|
|
25458
|
-
const binaryPath =
|
|
25509
|
+
const binaryPath = join41(pkgDir, astGrepName);
|
|
25459
25510
|
if (existsSync32(binaryPath) && isValidBinary(binaryPath)) {
|
|
25460
25511
|
return binaryPath;
|
|
25461
25512
|
}
|
|
@@ -25463,9 +25514,9 @@ function findSgCliPathSync() {
|
|
|
25463
25514
|
}
|
|
25464
25515
|
if (process.platform === "darwin") {
|
|
25465
25516
|
const homebrewPaths = ["/opt/homebrew/bin/sg", "/usr/local/bin/sg"];
|
|
25466
|
-
for (const
|
|
25467
|
-
if (existsSync32(
|
|
25468
|
-
return
|
|
25517
|
+
for (const path8 of homebrewPaths) {
|
|
25518
|
+
if (existsSync32(path8) && isValidBinary(path8)) {
|
|
25519
|
+
return path8;
|
|
25469
25520
|
}
|
|
25470
25521
|
}
|
|
25471
25522
|
}
|
|
@@ -25483,8 +25534,8 @@ function getSgCliPath() {
|
|
|
25483
25534
|
}
|
|
25484
25535
|
return "sg";
|
|
25485
25536
|
}
|
|
25486
|
-
function setSgCliPath(
|
|
25487
|
-
resolvedCliPath2 =
|
|
25537
|
+
function setSgCliPath(path8) {
|
|
25538
|
+
resolvedCliPath2 = path8;
|
|
25488
25539
|
}
|
|
25489
25540
|
var SG_CLI_PATH = getSgCliPath();
|
|
25490
25541
|
var CLI_LANGUAGES = [
|
|
@@ -25831,19 +25882,19 @@ var {spawn: spawn7 } = globalThis.Bun;
|
|
|
25831
25882
|
|
|
25832
25883
|
// src/tools/grep/constants.ts
|
|
25833
25884
|
import { existsSync as existsSync35 } from "fs";
|
|
25834
|
-
import { join as
|
|
25885
|
+
import { join as join43, dirname as dirname7 } from "path";
|
|
25835
25886
|
import { spawnSync } from "child_process";
|
|
25836
25887
|
|
|
25837
25888
|
// src/tools/grep/downloader.ts
|
|
25838
25889
|
import { existsSync as existsSync34, mkdirSync as mkdirSync11, chmodSync as chmodSync3, unlinkSync as unlinkSync10, readdirSync as readdirSync10 } from "fs";
|
|
25839
|
-
import { join as
|
|
25890
|
+
import { join as join42 } from "path";
|
|
25840
25891
|
function getInstallDir() {
|
|
25841
25892
|
const homeDir = process.env.HOME || process.env.USERPROFILE || ".";
|
|
25842
|
-
return
|
|
25893
|
+
return join42(homeDir, ".cache", "oh-my-opencode", "bin");
|
|
25843
25894
|
}
|
|
25844
25895
|
function getRgPath() {
|
|
25845
25896
|
const isWindows2 = process.platform === "win32";
|
|
25846
|
-
return
|
|
25897
|
+
return join42(getInstallDir(), isWindows2 ? "rg.exe" : "rg");
|
|
25847
25898
|
}
|
|
25848
25899
|
function getInstalledRipgrepPath() {
|
|
25849
25900
|
const rgPath = getRgPath();
|
|
@@ -25870,10 +25921,10 @@ function getOpenCodeBundledRg() {
|
|
|
25870
25921
|
const isWindows2 = process.platform === "win32";
|
|
25871
25922
|
const rgName = isWindows2 ? "rg.exe" : "rg";
|
|
25872
25923
|
const candidates = [
|
|
25873
|
-
|
|
25874
|
-
|
|
25875
|
-
|
|
25876
|
-
|
|
25924
|
+
join43(execDir, rgName),
|
|
25925
|
+
join43(execDir, "bin", rgName),
|
|
25926
|
+
join43(execDir, "..", "bin", rgName),
|
|
25927
|
+
join43(execDir, "..", "libexec", rgName)
|
|
25877
25928
|
];
|
|
25878
25929
|
for (const candidate of candidates) {
|
|
25879
25930
|
if (existsSync35(candidate)) {
|
|
@@ -26281,7 +26332,7 @@ var glob = tool({
|
|
|
26281
26332
|
// src/tools/slashcommand/tools.ts
|
|
26282
26333
|
import { existsSync as existsSync36, readdirSync as readdirSync11, readFileSync as readFileSync21 } from "fs";
|
|
26283
26334
|
import { homedir as homedir18 } from "os";
|
|
26284
|
-
import { join as
|
|
26335
|
+
import { join as join44, basename as basename3, dirname as dirname8 } from "path";
|
|
26285
26336
|
function discoverCommandsFromDir(commandsDir, scope) {
|
|
26286
26337
|
if (!existsSync36(commandsDir)) {
|
|
26287
26338
|
return [];
|
|
@@ -26291,7 +26342,7 @@ function discoverCommandsFromDir(commandsDir, scope) {
|
|
|
26291
26342
|
for (const entry of entries) {
|
|
26292
26343
|
if (!isMarkdownFile(entry))
|
|
26293
26344
|
continue;
|
|
26294
|
-
const commandPath =
|
|
26345
|
+
const commandPath = join44(commandsDir, entry.name);
|
|
26295
26346
|
const commandName = basename3(entry.name, ".md");
|
|
26296
26347
|
try {
|
|
26297
26348
|
const content = readFileSync21(commandPath, "utf-8");
|
|
@@ -26319,10 +26370,10 @@ function discoverCommandsFromDir(commandsDir, scope) {
|
|
|
26319
26370
|
return commands;
|
|
26320
26371
|
}
|
|
26321
26372
|
function discoverCommandsSync() {
|
|
26322
|
-
const userCommandsDir =
|
|
26323
|
-
const projectCommandsDir =
|
|
26324
|
-
const opencodeGlobalDir =
|
|
26325
|
-
const opencodeProjectDir =
|
|
26373
|
+
const userCommandsDir = join44(homedir18(), ".claude", "commands");
|
|
26374
|
+
const projectCommandsDir = join44(process.cwd(), ".claude", "commands");
|
|
26375
|
+
const opencodeGlobalDir = join44(homedir18(), ".config", "opencode", "command");
|
|
26376
|
+
const opencodeProjectDir = join44(process.cwd(), ".opencode", "command");
|
|
26326
26377
|
const userCommands = discoverCommandsFromDir(userCommandsDir, "user");
|
|
26327
26378
|
const opencodeGlobalCommands = discoverCommandsFromDir(opencodeGlobalDir, "opencode");
|
|
26328
26379
|
const projectCommands = discoverCommandsFromDir(projectCommandsDir, "project");
|
|
@@ -26445,6 +26496,484 @@ Provide a command name to execute.`;
|
|
|
26445
26496
|
Try a different command name.`;
|
|
26446
26497
|
}
|
|
26447
26498
|
});
|
|
26499
|
+
// src/tools/session-manager/constants.ts
|
|
26500
|
+
import { join as join45 } from "path";
|
|
26501
|
+
import { homedir as homedir19 } from "os";
|
|
26502
|
+
var OPENCODE_STORAGE9 = getOpenCodeStorageDir();
|
|
26503
|
+
var MESSAGE_STORAGE4 = join45(OPENCODE_STORAGE9, "message");
|
|
26504
|
+
var PART_STORAGE4 = join45(OPENCODE_STORAGE9, "part");
|
|
26505
|
+
var TODO_DIR2 = join45(homedir19(), ".claude", "todos");
|
|
26506
|
+
var TRANSCRIPT_DIR2 = join45(homedir19(), ".claude", "transcripts");
|
|
26507
|
+
var SESSION_LIST_DESCRIPTION = `List all OpenCode sessions with optional filtering.
|
|
26508
|
+
|
|
26509
|
+
Returns a list of available session IDs with metadata including message count, date range, and agents used.
|
|
26510
|
+
|
|
26511
|
+
Arguments:
|
|
26512
|
+
- limit (optional): Maximum number of sessions to return
|
|
26513
|
+
- from_date (optional): Filter sessions from this date (ISO 8601 format)
|
|
26514
|
+
- to_date (optional): Filter sessions until this date (ISO 8601 format)
|
|
26515
|
+
|
|
26516
|
+
Example output:
|
|
26517
|
+
| Session ID | Messages | First | Last | Agents |
|
|
26518
|
+
|------------|----------|-------|------|--------|
|
|
26519
|
+
| ses_abc123 | 45 | 2025-12-20 | 2025-12-24 | build, oracle |
|
|
26520
|
+
| ses_def456 | 12 | 2025-12-19 | 2025-12-19 | build |`;
|
|
26521
|
+
var SESSION_READ_DESCRIPTION = `Read messages and history from an OpenCode session.
|
|
26522
|
+
|
|
26523
|
+
Returns a formatted view of session messages with role, timestamp, and content. Optionally includes todos and transcript data.
|
|
26524
|
+
|
|
26525
|
+
Arguments:
|
|
26526
|
+
- session_id (required): Session ID to read
|
|
26527
|
+
- include_todos (optional): Include todo list if available (default: false)
|
|
26528
|
+
- include_transcript (optional): Include transcript log if available (default: false)
|
|
26529
|
+
- limit (optional): Maximum number of messages to return (default: all)
|
|
26530
|
+
|
|
26531
|
+
Example output:
|
|
26532
|
+
Session: ses_abc123
|
|
26533
|
+
Messages: 45
|
|
26534
|
+
Date Range: 2025-12-20 to 2025-12-24
|
|
26535
|
+
|
|
26536
|
+
[Message 1] user (2025-12-20 10:30:00)
|
|
26537
|
+
Hello, can you help me with...
|
|
26538
|
+
|
|
26539
|
+
[Message 2] assistant (2025-12-20 10:30:15)
|
|
26540
|
+
Of course! Let me help you with...`;
|
|
26541
|
+
var SESSION_SEARCH_DESCRIPTION = `Search for content within OpenCode session messages.
|
|
26542
|
+
|
|
26543
|
+
Performs full-text search across session messages and returns matching excerpts with context.
|
|
26544
|
+
|
|
26545
|
+
Arguments:
|
|
26546
|
+
- query (required): Search query string
|
|
26547
|
+
- session_id (optional): Search within specific session only (default: all sessions)
|
|
26548
|
+
- case_sensitive (optional): Case-sensitive search (default: false)
|
|
26549
|
+
- limit (optional): Maximum number of results to return (default: 20)
|
|
26550
|
+
|
|
26551
|
+
Example output:
|
|
26552
|
+
Found 3 matches across 2 sessions:
|
|
26553
|
+
|
|
26554
|
+
[ses_abc123] Message msg_001 (user)
|
|
26555
|
+
...implement the **session manager** tool...
|
|
26556
|
+
|
|
26557
|
+
[ses_abc123] Message msg_005 (assistant)
|
|
26558
|
+
...I'll create a **session manager** with full search...
|
|
26559
|
+
|
|
26560
|
+
[ses_def456] Message msg_012 (user)
|
|
26561
|
+
...use the **session manager** to find...`;
|
|
26562
|
+
var SESSION_INFO_DESCRIPTION = `Get metadata and statistics about an OpenCode session.
|
|
26563
|
+
|
|
26564
|
+
Returns detailed information about a session including message count, date range, agents used, and available data sources.
|
|
26565
|
+
|
|
26566
|
+
Arguments:
|
|
26567
|
+
- session_id (required): Session ID to inspect
|
|
26568
|
+
|
|
26569
|
+
Example output:
|
|
26570
|
+
Session ID: ses_abc123
|
|
26571
|
+
Messages: 45
|
|
26572
|
+
Date Range: 2025-12-20 10:30:00 to 2025-12-24 15:45:30
|
|
26573
|
+
Duration: 4 days, 5 hours
|
|
26574
|
+
Agents Used: build, oracle, librarian
|
|
26575
|
+
Has Todos: Yes (12 items, 8 completed)
|
|
26576
|
+
Has Transcript: Yes (234 entries)`;
|
|
26577
|
+
|
|
26578
|
+
// src/tools/session-manager/storage.ts
|
|
26579
|
+
import { existsSync as existsSync37, readdirSync as readdirSync12, readFileSync as readFileSync22 } from "fs";
|
|
26580
|
+
import { join as join46 } from "path";
|
|
26581
|
+
function getAllSessions() {
|
|
26582
|
+
if (!existsSync37(MESSAGE_STORAGE4))
|
|
26583
|
+
return [];
|
|
26584
|
+
const sessions = [];
|
|
26585
|
+
function scanDirectory(dir) {
|
|
26586
|
+
try {
|
|
26587
|
+
for (const entry of readdirSync12(dir, { withFileTypes: true })) {
|
|
26588
|
+
if (entry.isDirectory()) {
|
|
26589
|
+
const sessionPath = join46(dir, entry.name);
|
|
26590
|
+
const files = readdirSync12(sessionPath);
|
|
26591
|
+
if (files.some((f) => f.endsWith(".json"))) {
|
|
26592
|
+
sessions.push(entry.name);
|
|
26593
|
+
} else {
|
|
26594
|
+
scanDirectory(sessionPath);
|
|
26595
|
+
}
|
|
26596
|
+
}
|
|
26597
|
+
}
|
|
26598
|
+
} catch {
|
|
26599
|
+
return;
|
|
26600
|
+
}
|
|
26601
|
+
}
|
|
26602
|
+
scanDirectory(MESSAGE_STORAGE4);
|
|
26603
|
+
return [...new Set(sessions)];
|
|
26604
|
+
}
|
|
26605
|
+
function getMessageDir5(sessionID) {
|
|
26606
|
+
if (!existsSync37(MESSAGE_STORAGE4))
|
|
26607
|
+
return "";
|
|
26608
|
+
const directPath = join46(MESSAGE_STORAGE4, sessionID);
|
|
26609
|
+
if (existsSync37(directPath)) {
|
|
26610
|
+
return directPath;
|
|
26611
|
+
}
|
|
26612
|
+
for (const dir of readdirSync12(MESSAGE_STORAGE4)) {
|
|
26613
|
+
const sessionPath = join46(MESSAGE_STORAGE4, dir, sessionID);
|
|
26614
|
+
if (existsSync37(sessionPath)) {
|
|
26615
|
+
return sessionPath;
|
|
26616
|
+
}
|
|
26617
|
+
}
|
|
26618
|
+
return "";
|
|
26619
|
+
}
|
|
26620
|
+
function sessionExists(sessionID) {
|
|
26621
|
+
return getMessageDir5(sessionID) !== "";
|
|
26622
|
+
}
|
|
26623
|
+
function readSessionMessages(sessionID) {
|
|
26624
|
+
const messageDir = getMessageDir5(sessionID);
|
|
26625
|
+
if (!messageDir || !existsSync37(messageDir))
|
|
26626
|
+
return [];
|
|
26627
|
+
const messages = [];
|
|
26628
|
+
for (const file2 of readdirSync12(messageDir)) {
|
|
26629
|
+
if (!file2.endsWith(".json"))
|
|
26630
|
+
continue;
|
|
26631
|
+
try {
|
|
26632
|
+
const content = readFileSync22(join46(messageDir, file2), "utf-8");
|
|
26633
|
+
const meta = JSON.parse(content);
|
|
26634
|
+
const parts = readParts2(meta.id);
|
|
26635
|
+
messages.push({
|
|
26636
|
+
id: meta.id,
|
|
26637
|
+
role: meta.role,
|
|
26638
|
+
agent: meta.agent,
|
|
26639
|
+
time: meta.time,
|
|
26640
|
+
parts
|
|
26641
|
+
});
|
|
26642
|
+
} catch {
|
|
26643
|
+
continue;
|
|
26644
|
+
}
|
|
26645
|
+
}
|
|
26646
|
+
return messages.sort((a, b) => {
|
|
26647
|
+
const aTime = a.time?.created ?? 0;
|
|
26648
|
+
const bTime = b.time?.created ?? 0;
|
|
26649
|
+
if (aTime !== bTime)
|
|
26650
|
+
return aTime - bTime;
|
|
26651
|
+
return a.id.localeCompare(b.id);
|
|
26652
|
+
});
|
|
26653
|
+
}
|
|
26654
|
+
function readParts2(messageID) {
|
|
26655
|
+
const partDir = join46(PART_STORAGE4, messageID);
|
|
26656
|
+
if (!existsSync37(partDir))
|
|
26657
|
+
return [];
|
|
26658
|
+
const parts = [];
|
|
26659
|
+
for (const file2 of readdirSync12(partDir)) {
|
|
26660
|
+
if (!file2.endsWith(".json"))
|
|
26661
|
+
continue;
|
|
26662
|
+
try {
|
|
26663
|
+
const content = readFileSync22(join46(partDir, file2), "utf-8");
|
|
26664
|
+
parts.push(JSON.parse(content));
|
|
26665
|
+
} catch {
|
|
26666
|
+
continue;
|
|
26667
|
+
}
|
|
26668
|
+
}
|
|
26669
|
+
return parts.sort((a, b) => a.id.localeCompare(b.id));
|
|
26670
|
+
}
|
|
26671
|
+
function readSessionTodos(sessionID) {
|
|
26672
|
+
if (!existsSync37(TODO_DIR2))
|
|
26673
|
+
return [];
|
|
26674
|
+
const todoFiles = readdirSync12(TODO_DIR2).filter((f) => f.includes(sessionID) && f.endsWith(".json"));
|
|
26675
|
+
for (const file2 of todoFiles) {
|
|
26676
|
+
try {
|
|
26677
|
+
const content = readFileSync22(join46(TODO_DIR2, file2), "utf-8");
|
|
26678
|
+
const data = JSON.parse(content);
|
|
26679
|
+
if (Array.isArray(data)) {
|
|
26680
|
+
return data.map((item) => ({
|
|
26681
|
+
id: item.id || "",
|
|
26682
|
+
content: item.content || "",
|
|
26683
|
+
status: item.status || "pending",
|
|
26684
|
+
priority: item.priority
|
|
26685
|
+
}));
|
|
26686
|
+
}
|
|
26687
|
+
} catch {
|
|
26688
|
+
continue;
|
|
26689
|
+
}
|
|
26690
|
+
}
|
|
26691
|
+
return [];
|
|
26692
|
+
}
|
|
26693
|
+
function readSessionTranscript(sessionID) {
|
|
26694
|
+
if (!existsSync37(TRANSCRIPT_DIR2))
|
|
26695
|
+
return 0;
|
|
26696
|
+
const transcriptFile = join46(TRANSCRIPT_DIR2, `${sessionID}.jsonl`);
|
|
26697
|
+
if (!existsSync37(transcriptFile))
|
|
26698
|
+
return 0;
|
|
26699
|
+
try {
|
|
26700
|
+
const content = readFileSync22(transcriptFile, "utf-8");
|
|
26701
|
+
return content.trim().split(`
|
|
26702
|
+
`).filter(Boolean).length;
|
|
26703
|
+
} catch {
|
|
26704
|
+
return 0;
|
|
26705
|
+
}
|
|
26706
|
+
}
|
|
26707
|
+
function getSessionInfo(sessionID) {
|
|
26708
|
+
const messages = readSessionMessages(sessionID);
|
|
26709
|
+
if (messages.length === 0)
|
|
26710
|
+
return null;
|
|
26711
|
+
const agentsUsed = new Set;
|
|
26712
|
+
let firstMessage;
|
|
26713
|
+
let lastMessage;
|
|
26714
|
+
for (const msg of messages) {
|
|
26715
|
+
if (msg.agent)
|
|
26716
|
+
agentsUsed.add(msg.agent);
|
|
26717
|
+
if (msg.time?.created) {
|
|
26718
|
+
const date5 = new Date(msg.time.created);
|
|
26719
|
+
if (!firstMessage || date5 < firstMessage)
|
|
26720
|
+
firstMessage = date5;
|
|
26721
|
+
if (!lastMessage || date5 > lastMessage)
|
|
26722
|
+
lastMessage = date5;
|
|
26723
|
+
}
|
|
26724
|
+
}
|
|
26725
|
+
const todos = readSessionTodos(sessionID);
|
|
26726
|
+
const transcriptEntries = readSessionTranscript(sessionID);
|
|
26727
|
+
return {
|
|
26728
|
+
id: sessionID,
|
|
26729
|
+
message_count: messages.length,
|
|
26730
|
+
first_message: firstMessage,
|
|
26731
|
+
last_message: lastMessage,
|
|
26732
|
+
agents_used: Array.from(agentsUsed),
|
|
26733
|
+
has_todos: todos.length > 0,
|
|
26734
|
+
has_transcript: transcriptEntries > 0,
|
|
26735
|
+
todos,
|
|
26736
|
+
transcript_entries: transcriptEntries
|
|
26737
|
+
};
|
|
26738
|
+
}
|
|
26739
|
+
|
|
26740
|
+
// src/tools/session-manager/utils.ts
|
|
26741
|
+
function formatSessionList(sessionIDs) {
|
|
26742
|
+
if (sessionIDs.length === 0) {
|
|
26743
|
+
return "No sessions found.";
|
|
26744
|
+
}
|
|
26745
|
+
const infos = sessionIDs.map((id) => getSessionInfo(id)).filter((info) => info !== null);
|
|
26746
|
+
if (infos.length === 0) {
|
|
26747
|
+
return "No valid sessions found.";
|
|
26748
|
+
}
|
|
26749
|
+
const headers = ["Session ID", "Messages", "First", "Last", "Agents"];
|
|
26750
|
+
const rows = infos.map((info) => [
|
|
26751
|
+
info.id,
|
|
26752
|
+
info.message_count.toString(),
|
|
26753
|
+
info.first_message?.toISOString().split("T")[0] ?? "N/A",
|
|
26754
|
+
info.last_message?.toISOString().split("T")[0] ?? "N/A",
|
|
26755
|
+
info.agents_used.join(", ") || "none"
|
|
26756
|
+
]);
|
|
26757
|
+
const colWidths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
26758
|
+
const formatRow = (cells) => {
|
|
26759
|
+
return "| " + cells.map((cell, i) => cell.padEnd(colWidths[i])).join(" | ").trim() + " |";
|
|
26760
|
+
};
|
|
26761
|
+
const separator = "|" + colWidths.map((w) => "-".repeat(w + 2)).join("|") + "|";
|
|
26762
|
+
return [formatRow(headers), separator, ...rows.map(formatRow)].join(`
|
|
26763
|
+
`);
|
|
26764
|
+
}
|
|
26765
|
+
function formatSessionMessages(messages, includeTodos, todos) {
|
|
26766
|
+
if (messages.length === 0) {
|
|
26767
|
+
return "No messages found in this session.";
|
|
26768
|
+
}
|
|
26769
|
+
const lines = [];
|
|
26770
|
+
for (const msg of messages) {
|
|
26771
|
+
const timestamp = msg.time?.created ? new Date(msg.time.created).toISOString() : "Unknown time";
|
|
26772
|
+
const agent = msg.agent ? ` (${msg.agent})` : "";
|
|
26773
|
+
lines.push(`
|
|
26774
|
+
[${msg.role}${agent}] ${timestamp}`);
|
|
26775
|
+
for (const part of msg.parts) {
|
|
26776
|
+
if (part.type === "text" && part.text) {
|
|
26777
|
+
lines.push(part.text.trim());
|
|
26778
|
+
} else if (part.type === "thinking" && part.thinking) {
|
|
26779
|
+
lines.push(`[thinking] ${part.thinking.substring(0, 200)}...`);
|
|
26780
|
+
} else if ((part.type === "tool_use" || part.type === "tool") && part.tool) {
|
|
26781
|
+
const input = part.input ? JSON.stringify(part.input).substring(0, 100) : "";
|
|
26782
|
+
lines.push(`[tool: ${part.tool}] ${input}`);
|
|
26783
|
+
} else if (part.type === "tool_result") {
|
|
26784
|
+
const output = part.output ? part.output.substring(0, 200) : "";
|
|
26785
|
+
lines.push(`[tool result] ${output}...`);
|
|
26786
|
+
}
|
|
26787
|
+
}
|
|
26788
|
+
}
|
|
26789
|
+
if (includeTodos && todos && todos.length > 0) {
|
|
26790
|
+
lines.push(`
|
|
26791
|
+
|
|
26792
|
+
=== Todos ===`);
|
|
26793
|
+
for (const todo of todos) {
|
|
26794
|
+
const status = todo.status === "completed" ? "\u2713" : todo.status === "in_progress" ? "\u2192" : "\u25CB";
|
|
26795
|
+
lines.push(`${status} [${todo.status}] ${todo.content}`);
|
|
26796
|
+
}
|
|
26797
|
+
}
|
|
26798
|
+
return lines.join(`
|
|
26799
|
+
`);
|
|
26800
|
+
}
|
|
26801
|
+
function formatSessionInfo(info) {
|
|
26802
|
+
const lines = [
|
|
26803
|
+
`Session ID: ${info.id}`,
|
|
26804
|
+
`Messages: ${info.message_count}`,
|
|
26805
|
+
`Date Range: ${info.first_message?.toISOString() ?? "N/A"} to ${info.last_message?.toISOString() ?? "N/A"}`,
|
|
26806
|
+
`Agents Used: ${info.agents_used.join(", ") || "none"}`,
|
|
26807
|
+
`Has Todos: ${info.has_todos ? `Yes (${info.todos?.length ?? 0} items)` : "No"}`,
|
|
26808
|
+
`Has Transcript: ${info.has_transcript ? `Yes (${info.transcript_entries} entries)` : "No"}`
|
|
26809
|
+
];
|
|
26810
|
+
if (info.first_message && info.last_message) {
|
|
26811
|
+
const duration3 = info.last_message.getTime() - info.first_message.getTime();
|
|
26812
|
+
const days = Math.floor(duration3 / (1000 * 60 * 60 * 24));
|
|
26813
|
+
const hours = Math.floor(duration3 % (1000 * 60 * 60 * 24) / (1000 * 60 * 60));
|
|
26814
|
+
if (days > 0 || hours > 0) {
|
|
26815
|
+
lines.push(`Duration: ${days} days, ${hours} hours`);
|
|
26816
|
+
}
|
|
26817
|
+
}
|
|
26818
|
+
return lines.join(`
|
|
26819
|
+
`);
|
|
26820
|
+
}
|
|
26821
|
+
function formatSearchResults(results) {
|
|
26822
|
+
if (results.length === 0) {
|
|
26823
|
+
return "No matches found.";
|
|
26824
|
+
}
|
|
26825
|
+
const lines = [`Found ${results.length} matches:
|
|
26826
|
+
`];
|
|
26827
|
+
for (const result of results) {
|
|
26828
|
+
const timestamp = result.timestamp ? new Date(result.timestamp).toISOString() : "";
|
|
26829
|
+
lines.push(`[${result.session_id}] ${result.message_id} (${result.role}) ${timestamp}`);
|
|
26830
|
+
lines.push(` ${result.excerpt}`);
|
|
26831
|
+
lines.push(` Matches: ${result.match_count}
|
|
26832
|
+
`);
|
|
26833
|
+
}
|
|
26834
|
+
return lines.join(`
|
|
26835
|
+
`);
|
|
26836
|
+
}
|
|
26837
|
+
function filterSessionsByDate(sessionIDs, fromDate, toDate) {
|
|
26838
|
+
if (!fromDate && !toDate)
|
|
26839
|
+
return sessionIDs;
|
|
26840
|
+
const from = fromDate ? new Date(fromDate) : null;
|
|
26841
|
+
const to = toDate ? new Date(toDate) : null;
|
|
26842
|
+
return sessionIDs.filter((id) => {
|
|
26843
|
+
const info = getSessionInfo(id);
|
|
26844
|
+
if (!info || !info.last_message)
|
|
26845
|
+
return false;
|
|
26846
|
+
if (from && info.last_message < from)
|
|
26847
|
+
return false;
|
|
26848
|
+
if (to && info.last_message > to)
|
|
26849
|
+
return false;
|
|
26850
|
+
return true;
|
|
26851
|
+
});
|
|
26852
|
+
}
|
|
26853
|
+
function searchInSession(sessionID, query, caseSensitive = false) {
|
|
26854
|
+
const messages = readSessionMessages(sessionID);
|
|
26855
|
+
const results = [];
|
|
26856
|
+
const searchQuery = caseSensitive ? query : query.toLowerCase();
|
|
26857
|
+
for (const msg of messages) {
|
|
26858
|
+
let matchCount = 0;
|
|
26859
|
+
let excerpts = [];
|
|
26860
|
+
for (const part of msg.parts) {
|
|
26861
|
+
if (part.type === "text" && part.text) {
|
|
26862
|
+
const text = caseSensitive ? part.text : part.text.toLowerCase();
|
|
26863
|
+
const matches = text.split(searchQuery).length - 1;
|
|
26864
|
+
if (matches > 0) {
|
|
26865
|
+
matchCount += matches;
|
|
26866
|
+
const index = text.indexOf(searchQuery);
|
|
26867
|
+
if (index !== -1) {
|
|
26868
|
+
const start = Math.max(0, index - 50);
|
|
26869
|
+
const end = Math.min(text.length, index + searchQuery.length + 50);
|
|
26870
|
+
let excerpt = part.text.substring(start, end);
|
|
26871
|
+
if (start > 0)
|
|
26872
|
+
excerpt = "..." + excerpt;
|
|
26873
|
+
if (end < text.length)
|
|
26874
|
+
excerpt = excerpt + "...";
|
|
26875
|
+
excerpts.push(excerpt);
|
|
26876
|
+
}
|
|
26877
|
+
}
|
|
26878
|
+
}
|
|
26879
|
+
}
|
|
26880
|
+
if (matchCount > 0) {
|
|
26881
|
+
results.push({
|
|
26882
|
+
session_id: sessionID,
|
|
26883
|
+
message_id: msg.id,
|
|
26884
|
+
role: msg.role,
|
|
26885
|
+
excerpt: excerpts[0] || "",
|
|
26886
|
+
match_count: matchCount,
|
|
26887
|
+
timestamp: msg.time?.created
|
|
26888
|
+
});
|
|
26889
|
+
}
|
|
26890
|
+
}
|
|
26891
|
+
return results;
|
|
26892
|
+
}
|
|
26893
|
+
|
|
26894
|
+
// src/tools/session-manager/tools.ts
|
|
26895
|
+
var session_list = tool({
|
|
26896
|
+
description: SESSION_LIST_DESCRIPTION,
|
|
26897
|
+
args: {
|
|
26898
|
+
limit: tool.schema.number().optional().describe("Maximum number of sessions to return"),
|
|
26899
|
+
from_date: tool.schema.string().optional().describe("Filter sessions from this date (ISO 8601 format)"),
|
|
26900
|
+
to_date: tool.schema.string().optional().describe("Filter sessions until this date (ISO 8601 format)")
|
|
26901
|
+
},
|
|
26902
|
+
execute: async (args, _context) => {
|
|
26903
|
+
try {
|
|
26904
|
+
let sessions = getAllSessions();
|
|
26905
|
+
if (args.from_date || args.to_date) {
|
|
26906
|
+
sessions = filterSessionsByDate(sessions, args.from_date, args.to_date);
|
|
26907
|
+
}
|
|
26908
|
+
if (args.limit && args.limit > 0) {
|
|
26909
|
+
sessions = sessions.slice(0, args.limit);
|
|
26910
|
+
}
|
|
26911
|
+
return formatSessionList(sessions);
|
|
26912
|
+
} catch (e) {
|
|
26913
|
+
return `Error: ${e instanceof Error ? e.message : String(e)}`;
|
|
26914
|
+
}
|
|
26915
|
+
}
|
|
26916
|
+
});
|
|
26917
|
+
var session_read = tool({
|
|
26918
|
+
description: SESSION_READ_DESCRIPTION,
|
|
26919
|
+
args: {
|
|
26920
|
+
session_id: tool.schema.string().describe("Session ID to read"),
|
|
26921
|
+
include_todos: tool.schema.boolean().optional().describe("Include todo list if available (default: false)"),
|
|
26922
|
+
include_transcript: tool.schema.boolean().optional().describe("Include transcript log if available (default: false)"),
|
|
26923
|
+
limit: tool.schema.number().optional().describe("Maximum number of messages to return (default: all)")
|
|
26924
|
+
},
|
|
26925
|
+
execute: async (args, _context) => {
|
|
26926
|
+
try {
|
|
26927
|
+
if (!sessionExists(args.session_id)) {
|
|
26928
|
+
return `Session not found: ${args.session_id}`;
|
|
26929
|
+
}
|
|
26930
|
+
let messages = readSessionMessages(args.session_id);
|
|
26931
|
+
if (args.limit && args.limit > 0) {
|
|
26932
|
+
messages = messages.slice(0, args.limit);
|
|
26933
|
+
}
|
|
26934
|
+
const todos = args.include_todos ? readSessionTodos(args.session_id) : undefined;
|
|
26935
|
+
return formatSessionMessages(messages, args.include_todos, todos);
|
|
26936
|
+
} catch (e) {
|
|
26937
|
+
return `Error: ${e instanceof Error ? e.message : String(e)}`;
|
|
26938
|
+
}
|
|
26939
|
+
}
|
|
26940
|
+
});
|
|
26941
|
+
var session_search = tool({
|
|
26942
|
+
description: SESSION_SEARCH_DESCRIPTION,
|
|
26943
|
+
args: {
|
|
26944
|
+
query: tool.schema.string().describe("Search query string"),
|
|
26945
|
+
session_id: tool.schema.string().optional().describe("Search within specific session only (default: all sessions)"),
|
|
26946
|
+
case_sensitive: tool.schema.boolean().optional().describe("Case-sensitive search (default: false)"),
|
|
26947
|
+
limit: tool.schema.number().optional().describe("Maximum number of results to return (default: 20)")
|
|
26948
|
+
},
|
|
26949
|
+
execute: async (args, _context) => {
|
|
26950
|
+
try {
|
|
26951
|
+
const sessions = args.session_id ? [args.session_id] : getAllSessions();
|
|
26952
|
+
const allResults = sessions.flatMap((sid) => searchInSession(sid, args.query, args.case_sensitive));
|
|
26953
|
+
const limited = args.limit && args.limit > 0 ? allResults.slice(0, args.limit) : allResults.slice(0, 20);
|
|
26954
|
+
return formatSearchResults(limited);
|
|
26955
|
+
} catch (e) {
|
|
26956
|
+
return `Error: ${e instanceof Error ? e.message : String(e)}`;
|
|
26957
|
+
}
|
|
26958
|
+
}
|
|
26959
|
+
});
|
|
26960
|
+
var session_info = tool({
|
|
26961
|
+
description: SESSION_INFO_DESCRIPTION,
|
|
26962
|
+
args: {
|
|
26963
|
+
session_id: tool.schema.string().describe("Session ID to inspect")
|
|
26964
|
+
},
|
|
26965
|
+
execute: async (args, _context) => {
|
|
26966
|
+
try {
|
|
26967
|
+
const info = getSessionInfo(args.session_id);
|
|
26968
|
+
if (!info) {
|
|
26969
|
+
return `Session not found: ${args.session_id}`;
|
|
26970
|
+
}
|
|
26971
|
+
return formatSessionInfo(info);
|
|
26972
|
+
} catch (e) {
|
|
26973
|
+
return `Error: ${e instanceof Error ? e.message : String(e)}`;
|
|
26974
|
+
}
|
|
26975
|
+
}
|
|
26976
|
+
});
|
|
26448
26977
|
// src/tools/interactive-bash/constants.ts
|
|
26449
26978
|
var DEFAULT_TIMEOUT_MS4 = 60000;
|
|
26450
26979
|
var BLOCKED_TMUX_SUBCOMMANDS = [
|
|
@@ -26478,12 +27007,12 @@ async function findTmuxPath() {
|
|
|
26478
27007
|
return null;
|
|
26479
27008
|
}
|
|
26480
27009
|
const stdout = await new Response(proc.stdout).text();
|
|
26481
|
-
const
|
|
27010
|
+
const path8 = stdout.trim().split(`
|
|
26482
27011
|
`)[0];
|
|
26483
|
-
if (!
|
|
27012
|
+
if (!path8) {
|
|
26484
27013
|
return null;
|
|
26485
27014
|
}
|
|
26486
|
-
const verifyProc = spawn9([
|
|
27015
|
+
const verifyProc = spawn9([path8, "-V"], {
|
|
26487
27016
|
stdout: "pipe",
|
|
26488
27017
|
stderr: "pipe"
|
|
26489
27018
|
});
|
|
@@ -26491,7 +27020,7 @@ async function findTmuxPath() {
|
|
|
26491
27020
|
if (verifyExitCode !== 0) {
|
|
26492
27021
|
return null;
|
|
26493
27022
|
}
|
|
26494
|
-
return
|
|
27023
|
+
return path8;
|
|
26495
27024
|
} catch {
|
|
26496
27025
|
return null;
|
|
26497
27026
|
}
|
|
@@ -26504,9 +27033,9 @@ async function getTmuxPath() {
|
|
|
26504
27033
|
return initPromise3;
|
|
26505
27034
|
}
|
|
26506
27035
|
initPromise3 = (async () => {
|
|
26507
|
-
const
|
|
26508
|
-
tmuxPath =
|
|
26509
|
-
return
|
|
27036
|
+
const path8 = await findTmuxPath();
|
|
27037
|
+
tmuxPath = path8;
|
|
27038
|
+
return path8;
|
|
26510
27039
|
})();
|
|
26511
27040
|
return initPromise3;
|
|
26512
27041
|
}
|
|
@@ -26596,6 +27125,10 @@ var interactive_bash = tool({
|
|
|
26596
27125
|
}
|
|
26597
27126
|
}
|
|
26598
27127
|
});
|
|
27128
|
+
// src/tools/background-task/tools.ts
|
|
27129
|
+
import { existsSync as existsSync38, readdirSync as readdirSync13 } from "fs";
|
|
27130
|
+
import { join as join47 } from "path";
|
|
27131
|
+
|
|
26599
27132
|
// src/tools/background-task/constants.ts
|
|
26600
27133
|
var BACKGROUND_TASK_DESCRIPTION = `Run agent task in background. Returns task_id immediately; notifies on completion.
|
|
26601
27134
|
|
|
@@ -26604,6 +27137,19 @@ var BACKGROUND_OUTPUT_DESCRIPTION = `Get output from background task. System not
|
|
|
26604
27137
|
var BACKGROUND_CANCEL_DESCRIPTION = `Cancel running background task(s). Use all=true to cancel ALL before final answer.`;
|
|
26605
27138
|
|
|
26606
27139
|
// src/tools/background-task/tools.ts
|
|
27140
|
+
function getMessageDir6(sessionID) {
|
|
27141
|
+
if (!existsSync38(MESSAGE_STORAGE))
|
|
27142
|
+
return null;
|
|
27143
|
+
const directPath = join47(MESSAGE_STORAGE, sessionID);
|
|
27144
|
+
if (existsSync38(directPath))
|
|
27145
|
+
return directPath;
|
|
27146
|
+
for (const dir of readdirSync13(MESSAGE_STORAGE)) {
|
|
27147
|
+
const sessionPath = join47(MESSAGE_STORAGE, dir, sessionID);
|
|
27148
|
+
if (existsSync38(sessionPath))
|
|
27149
|
+
return sessionPath;
|
|
27150
|
+
}
|
|
27151
|
+
return null;
|
|
27152
|
+
}
|
|
26607
27153
|
function formatDuration(start, end) {
|
|
26608
27154
|
const duration3 = (end ?? new Date).getTime() - start.getTime();
|
|
26609
27155
|
const seconds = Math.floor(duration3 / 1000);
|
|
@@ -26630,12 +27176,16 @@ function createBackgroundTask(manager) {
|
|
|
26630
27176
|
return `\u274C Agent parameter is required. Please specify which agent to use (e.g., "explore", "librarian", "build", etc.)`;
|
|
26631
27177
|
}
|
|
26632
27178
|
try {
|
|
27179
|
+
const messageDir = getMessageDir6(toolContext.sessionID);
|
|
27180
|
+
const prevMessage = messageDir ? findNearestMessageWithFields(messageDir) : null;
|
|
27181
|
+
const parentModel = prevMessage?.model?.providerID && prevMessage?.model?.modelID ? { providerID: prevMessage.model.providerID, modelID: prevMessage.model.modelID } : undefined;
|
|
26633
27182
|
const task = await manager.launch({
|
|
26634
27183
|
description: args.description,
|
|
26635
27184
|
prompt: args.prompt,
|
|
26636
27185
|
agent: args.agent.trim(),
|
|
26637
27186
|
parentSessionID: toolContext.sessionID,
|
|
26638
|
-
parentMessageID: toolContext.messageID
|
|
27187
|
+
parentMessageID: toolContext.messageID,
|
|
27188
|
+
parentModel
|
|
26639
27189
|
});
|
|
26640
27190
|
return `Background task launched successfully.
|
|
26641
27191
|
|
|
@@ -27145,20 +27695,24 @@ var builtinTools = {
|
|
|
27145
27695
|
ast_grep_replace,
|
|
27146
27696
|
grep,
|
|
27147
27697
|
glob,
|
|
27148
|
-
slashcommand
|
|
27698
|
+
slashcommand,
|
|
27699
|
+
session_list,
|
|
27700
|
+
session_read,
|
|
27701
|
+
session_search,
|
|
27702
|
+
session_info
|
|
27149
27703
|
};
|
|
27150
27704
|
// src/features/background-agent/manager.ts
|
|
27151
|
-
import { existsSync as
|
|
27152
|
-
import { join as
|
|
27153
|
-
function
|
|
27154
|
-
if (!
|
|
27705
|
+
import { existsSync as existsSync39, readdirSync as readdirSync14 } from "fs";
|
|
27706
|
+
import { join as join48 } from "path";
|
|
27707
|
+
function getMessageDir7(sessionID) {
|
|
27708
|
+
if (!existsSync39(MESSAGE_STORAGE))
|
|
27155
27709
|
return null;
|
|
27156
|
-
const directPath =
|
|
27157
|
-
if (
|
|
27710
|
+
const directPath = join48(MESSAGE_STORAGE, sessionID);
|
|
27711
|
+
if (existsSync39(directPath))
|
|
27158
27712
|
return directPath;
|
|
27159
|
-
for (const dir of
|
|
27160
|
-
const sessionPath =
|
|
27161
|
-
if (
|
|
27713
|
+
for (const dir of readdirSync14(MESSAGE_STORAGE)) {
|
|
27714
|
+
const sessionPath = join48(MESSAGE_STORAGE, dir, sessionID);
|
|
27715
|
+
if (existsSync39(sessionPath))
|
|
27162
27716
|
return sessionPath;
|
|
27163
27717
|
}
|
|
27164
27718
|
return null;
|
|
@@ -27204,7 +27758,8 @@ class BackgroundManager {
|
|
|
27204
27758
|
progress: {
|
|
27205
27759
|
toolCalls: 0,
|
|
27206
27760
|
lastUpdate: new Date
|
|
27207
|
-
}
|
|
27761
|
+
},
|
|
27762
|
+
parentModel: input.parentModel
|
|
27208
27763
|
};
|
|
27209
27764
|
this.tasks.set(task.id, task);
|
|
27210
27765
|
this.startPolling();
|
|
@@ -27394,12 +27949,15 @@ class BackgroundManager {
|
|
|
27394
27949
|
log("[background-agent] Sending notification to parent session:", { parentSessionID: task.parentSessionID });
|
|
27395
27950
|
setTimeout(async () => {
|
|
27396
27951
|
try {
|
|
27397
|
-
const messageDir =
|
|
27952
|
+
const messageDir = getMessageDir7(task.parentSessionID);
|
|
27398
27953
|
const prevMessage = messageDir ? findNearestMessageWithFields(messageDir) : null;
|
|
27954
|
+
const modelContext = task.parentModel ?? prevMessage?.model;
|
|
27955
|
+
const modelField = modelContext?.providerID && modelContext?.modelID ? { providerID: modelContext.providerID, modelID: modelContext.modelID } : undefined;
|
|
27399
27956
|
await this.client.session.prompt({
|
|
27400
27957
|
path: { id: task.parentSessionID },
|
|
27401
27958
|
body: {
|
|
27402
27959
|
agent: prevMessage?.agent,
|
|
27960
|
+
model: modelField,
|
|
27403
27961
|
parts: [{ type: "text", text: message }]
|
|
27404
27962
|
},
|
|
27405
27963
|
query: { directory: this.directory }
|
|
@@ -27562,6 +28120,7 @@ var OverridableAgentNameSchema = exports_external.enum([
|
|
|
27562
28120
|
"build",
|
|
27563
28121
|
"plan",
|
|
27564
28122
|
"Sisyphus",
|
|
28123
|
+
"OpenCode-Builder",
|
|
27565
28124
|
"Planner-Sisyphus",
|
|
27566
28125
|
"oracle",
|
|
27567
28126
|
"librarian",
|
|
@@ -27610,6 +28169,7 @@ var AgentOverridesSchema = exports_external.object({
|
|
|
27610
28169
|
build: AgentOverrideConfigSchema.optional(),
|
|
27611
28170
|
plan: AgentOverrideConfigSchema.optional(),
|
|
27612
28171
|
Sisyphus: AgentOverrideConfigSchema.optional(),
|
|
28172
|
+
"OpenCode-Builder": AgentOverrideConfigSchema.optional(),
|
|
27613
28173
|
"Planner-Sisyphus": AgentOverrideConfigSchema.optional(),
|
|
27614
28174
|
oracle: AgentOverrideConfigSchema.optional(),
|
|
27615
28175
|
librarian: AgentOverrideConfigSchema.optional(),
|
|
@@ -27626,14 +28186,17 @@ var ClaudeCodeConfigSchema = exports_external.object({
|
|
|
27626
28186
|
hooks: exports_external.boolean().optional()
|
|
27627
28187
|
});
|
|
27628
28188
|
var SisyphusAgentConfigSchema = exports_external.object({
|
|
27629
|
-
disabled: exports_external.boolean().optional()
|
|
28189
|
+
disabled: exports_external.boolean().optional(),
|
|
28190
|
+
default_builder_enabled: exports_external.boolean().optional(),
|
|
28191
|
+
planner_enabled: exports_external.boolean().optional(),
|
|
28192
|
+
replace_plan: exports_external.boolean().optional()
|
|
27630
28193
|
});
|
|
27631
28194
|
var ExperimentalConfigSchema = exports_external.object({
|
|
27632
28195
|
aggressive_truncation: exports_external.boolean().optional(),
|
|
27633
28196
|
auto_resume: exports_external.boolean().optional(),
|
|
27634
28197
|
preemptive_compaction: exports_external.boolean().optional(),
|
|
27635
28198
|
preemptive_compaction_threshold: exports_external.number().min(0.5).max(0.95).optional(),
|
|
27636
|
-
truncate_all_tool_outputs: exports_external.boolean().
|
|
28199
|
+
truncate_all_tool_outputs: exports_external.boolean().default(true)
|
|
27637
28200
|
});
|
|
27638
28201
|
var OhMyOpenCodeConfigSchema = exports_external.object({
|
|
27639
28202
|
$schema: exports_external.string().optional(),
|
|
@@ -27720,7 +28283,7 @@ var PLAN_PERMISSION = {
|
|
|
27720
28283
|
|
|
27721
28284
|
// src/index.ts
|
|
27722
28285
|
import * as fs7 from "fs";
|
|
27723
|
-
import * as
|
|
28286
|
+
import * as path8 from "path";
|
|
27724
28287
|
var AGENT_NAME_MAP = {
|
|
27725
28288
|
omo: "Sisyphus",
|
|
27726
28289
|
OmO: "Sisyphus",
|
|
@@ -27773,7 +28336,7 @@ function migrateConfigFile(configPath, rawConfig) {
|
|
|
27773
28336
|
}
|
|
27774
28337
|
return needsWrite;
|
|
27775
28338
|
}
|
|
27776
|
-
function loadConfigFromPath2(configPath) {
|
|
28339
|
+
function loadConfigFromPath2(configPath, ctx) {
|
|
27777
28340
|
try {
|
|
27778
28341
|
if (fs7.existsSync(configPath)) {
|
|
27779
28342
|
const content = fs7.readFileSync(configPath, "utf-8");
|
|
@@ -27784,6 +28347,21 @@ function loadConfigFromPath2(configPath) {
|
|
|
27784
28347
|
const errorMsg = result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join(", ");
|
|
27785
28348
|
log(`Config validation error in ${configPath}:`, result.error.issues);
|
|
27786
28349
|
addConfigLoadError({ path: configPath, error: `Validation error: ${errorMsg}` });
|
|
28350
|
+
const errorList = result.error.issues.map((issue2) => `\u2022 ${issue2.path.join(".")}: ${issue2.message}`).join(`
|
|
28351
|
+
`);
|
|
28352
|
+
ctx.client.tui.showToast({
|
|
28353
|
+
body: {
|
|
28354
|
+
title: "\u274C OhMyOpenCode: Config Validation Failed",
|
|
28355
|
+
message: `Failed to load ${configPath}
|
|
28356
|
+
|
|
28357
|
+
Validation errors:
|
|
28358
|
+
${errorList}
|
|
28359
|
+
|
|
28360
|
+
Config will be ignored. Please fix the errors above.`,
|
|
28361
|
+
variant: "error",
|
|
28362
|
+
duration: 1e4
|
|
28363
|
+
}
|
|
28364
|
+
}).catch(() => {});
|
|
27787
28365
|
return null;
|
|
27788
28366
|
}
|
|
27789
28367
|
log(`Config loaded from ${configPath}`, { agents: result.data.agents });
|
|
@@ -27793,6 +28371,21 @@ function loadConfigFromPath2(configPath) {
|
|
|
27793
28371
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
27794
28372
|
log(`Error loading config from ${configPath}:`, err);
|
|
27795
28373
|
addConfigLoadError({ path: configPath, error: errorMsg });
|
|
28374
|
+
const hint = err instanceof SyntaxError ? `
|
|
28375
|
+
|
|
28376
|
+
Hint: Check for syntax errors in your JSON file (missing commas, quotes, brackets, etc.)` : "";
|
|
28377
|
+
ctx.client.tui.showToast({
|
|
28378
|
+
body: {
|
|
28379
|
+
title: "\u274C OhMyOpenCode: Config Load Failed",
|
|
28380
|
+
message: `Failed to load ${configPath}
|
|
28381
|
+
|
|
28382
|
+
Error: ${errorMsg}${hint}
|
|
28383
|
+
|
|
28384
|
+
Config will be ignored. Please fix the error above.`,
|
|
28385
|
+
variant: "error",
|
|
28386
|
+
duration: 1e4
|
|
28387
|
+
}
|
|
28388
|
+
}).catch(() => {});
|
|
27796
28389
|
}
|
|
27797
28390
|
return null;
|
|
27798
28391
|
}
|
|
@@ -27822,11 +28415,11 @@ function mergeConfigs(base, override) {
|
|
|
27822
28415
|
claude_code: deepMerge(base.claude_code, override.claude_code)
|
|
27823
28416
|
};
|
|
27824
28417
|
}
|
|
27825
|
-
function loadPluginConfig(directory) {
|
|
27826
|
-
const userConfigPath =
|
|
27827
|
-
const projectConfigPath =
|
|
27828
|
-
let config3 = loadConfigFromPath2(userConfigPath) ?? {};
|
|
27829
|
-
const projectConfig = loadConfigFromPath2(projectConfigPath);
|
|
28418
|
+
function loadPluginConfig(directory, ctx) {
|
|
28419
|
+
const userConfigPath = path8.join(getUserConfigDir(), "opencode", "oh-my-opencode.json");
|
|
28420
|
+
const projectConfigPath = path8.join(directory, ".opencode", "oh-my-opencode.json");
|
|
28421
|
+
let config3 = loadConfigFromPath2(userConfigPath, ctx) ?? {};
|
|
28422
|
+
const projectConfig = loadConfigFromPath2(projectConfigPath, ctx);
|
|
27830
28423
|
if (projectConfig) {
|
|
27831
28424
|
config3 = mergeConfigs(config3, projectConfig);
|
|
27832
28425
|
}
|
|
@@ -27840,7 +28433,7 @@ function loadPluginConfig(directory) {
|
|
|
27840
28433
|
return config3;
|
|
27841
28434
|
}
|
|
27842
28435
|
var OhMyOpenCodePlugin = async (ctx) => {
|
|
27843
|
-
const pluginConfig = loadPluginConfig(ctx.directory);
|
|
28436
|
+
const pluginConfig = loadPluginConfig(ctx.directory, ctx);
|
|
27844
28437
|
const disabledHooks = new Set(pluginConfig.disabled_hooks ?? []);
|
|
27845
28438
|
const isHookEnabled = (hookName) => !disabledHooks.has(hookName);
|
|
27846
28439
|
const modelContextLimitsCache = new Map;
|
|
@@ -27934,26 +28527,49 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
27934
28527
|
const userAgents = pluginConfig.claude_code?.agents ?? true ? loadUserAgents() : {};
|
|
27935
28528
|
const projectAgents = pluginConfig.claude_code?.agents ?? true ? loadProjectAgents() : {};
|
|
27936
28529
|
const isSisyphusEnabled = pluginConfig.sisyphus_agent?.disabled !== true;
|
|
28530
|
+
const builderEnabled = pluginConfig.sisyphus_agent?.default_builder_enabled ?? false;
|
|
28531
|
+
const plannerEnabled = pluginConfig.sisyphus_agent?.planner_enabled ?? true;
|
|
28532
|
+
const replacePlan = pluginConfig.sisyphus_agent?.replace_plan ?? true;
|
|
27937
28533
|
if (isSisyphusEnabled && builtinAgents.Sisyphus) {
|
|
27938
|
-
const
|
|
27939
|
-
|
|
27940
|
-
const plannerSisyphusBase = {
|
|
27941
|
-
...planConfigWithoutName,
|
|
27942
|
-
prompt: PLAN_SYSTEM_PROMPT,
|
|
27943
|
-
permission: PLAN_PERMISSION,
|
|
27944
|
-
description: `${config3.agent?.plan?.description ?? "Plan agent"} (OhMyOpenCode version)`,
|
|
27945
|
-
color: config3.agent?.plan?.color ?? "#6495ED"
|
|
28534
|
+
const agentConfig = {
|
|
28535
|
+
Sisyphus: builtinAgents.Sisyphus
|
|
27946
28536
|
};
|
|
27947
|
-
|
|
28537
|
+
if (builderEnabled) {
|
|
28538
|
+
const { name: _buildName, ...buildConfigWithoutName } = config3.agent?.build ?? {};
|
|
28539
|
+
const openCodeBuilderOverride = pluginConfig.agents?.["OpenCode-Builder"];
|
|
28540
|
+
const openCodeBuilderBase = {
|
|
28541
|
+
...buildConfigWithoutName,
|
|
28542
|
+
description: `${config3.agent?.build?.description ?? "Build agent"} (OpenCode default)`
|
|
28543
|
+
};
|
|
28544
|
+
agentConfig["OpenCode-Builder"] = openCodeBuilderOverride ? { ...openCodeBuilderBase, ...openCodeBuilderOverride } : openCodeBuilderBase;
|
|
28545
|
+
}
|
|
28546
|
+
if (plannerEnabled) {
|
|
28547
|
+
const { name: _planName, ...planConfigWithoutName } = config3.agent?.plan ?? {};
|
|
28548
|
+
const plannerSisyphusOverride = pluginConfig.agents?.["Planner-Sisyphus"];
|
|
28549
|
+
const plannerSisyphusBase = {
|
|
28550
|
+
...planConfigWithoutName,
|
|
28551
|
+
prompt: PLAN_SYSTEM_PROMPT,
|
|
28552
|
+
permission: PLAN_PERMISSION,
|
|
28553
|
+
description: `${config3.agent?.plan?.description ?? "Plan agent"} (OhMyOpenCode version)`,
|
|
28554
|
+
color: config3.agent?.plan?.color ?? "#6495ED"
|
|
28555
|
+
};
|
|
28556
|
+
agentConfig["Planner-Sisyphus"] = plannerSisyphusOverride ? { ...plannerSisyphusBase, ...plannerSisyphusOverride } : plannerSisyphusBase;
|
|
28557
|
+
}
|
|
28558
|
+
const filteredConfigAgents = config3.agent ? Object.fromEntries(Object.entries(config3.agent).filter(([key]) => {
|
|
28559
|
+
if (key === "build")
|
|
28560
|
+
return false;
|
|
28561
|
+
if (key === "plan" && replacePlan)
|
|
28562
|
+
return false;
|
|
28563
|
+
return true;
|
|
28564
|
+
})) : {};
|
|
27948
28565
|
config3.agent = {
|
|
27949
|
-
|
|
27950
|
-
"Planner-Sisyphus": plannerSisyphusConfig,
|
|
28566
|
+
...agentConfig,
|
|
27951
28567
|
...Object.fromEntries(Object.entries(builtinAgents).filter(([k]) => k !== "Sisyphus")),
|
|
27952
28568
|
...userAgents,
|
|
27953
28569
|
...projectAgents,
|
|
27954
|
-
...
|
|
28570
|
+
...filteredConfigAgents,
|
|
27955
28571
|
build: { ...config3.agent?.build, mode: "subagent" },
|
|
27956
|
-
plan: { ...config3.agent?.plan, mode: "subagent" }
|
|
28572
|
+
...replacePlan ? { plan: { ...config3.agent?.plan, mode: "subagent" } } : {}
|
|
27957
28573
|
};
|
|
27958
28574
|
} else {
|
|
27959
28575
|
config3.agent = {
|