oh-my-opencode 2.5.4 → 2.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +13 -5
- package/README.ko.md +13 -5
- package/README.md +43 -5
- package/README.zh-cn.md +13 -5
- package/dist/cli/get-local-version/formatter.d.ts +3 -0
- package/dist/cli/get-local-version/index.d.ts +3 -0
- package/dist/cli/get-local-version/types.d.ts +13 -0
- package/dist/cli/index.js +1241 -108
- package/dist/config/index.d.ts +1 -1
- package/dist/config/schema.d.ts +87 -0
- package/dist/features/background-agent/manager.d.ts +1 -0
- package/dist/features/claude-code-plugin-loader/index.d.ts +3 -0
- package/dist/features/claude-code-plugin-loader/loader.d.ts +20 -0
- package/dist/features/claude-code-plugin-loader/types.d.ts +173 -0
- package/dist/hooks/anthropic-auto-compact/index.d.ts +1 -1
- package/dist/hooks/anthropic-auto-compact/pruning-deduplication.d.ts +7 -0
- package/dist/hooks/anthropic-auto-compact/pruning-deduplication.test.d.ts +1 -0
- package/dist/hooks/anthropic-auto-compact/pruning-executor.d.ts +3 -0
- package/dist/hooks/anthropic-auto-compact/pruning-purge-errors.d.ts +7 -0
- package/dist/hooks/anthropic-auto-compact/pruning-storage.d.ts +2 -0
- package/dist/hooks/anthropic-auto-compact/pruning-supersede.d.ts +6 -0
- package/dist/hooks/anthropic-auto-compact/pruning-types.d.ts +36 -0
- package/dist/hooks/anthropic-auto-compact/types.d.ts +5 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/session-notification-utils.d.ts +9 -0
- package/dist/hooks/thinking-block-validator/index.d.ts +30 -0
- package/dist/index.js +2723 -644
- package/dist/shared/claude-config-dir.d.ts +1 -0
- package/dist/shared/claude-config-dir.test.d.ts +1 -0
- package/dist/shared/index.d.ts +2 -0
- package/dist/shared/jsonc-parser.d.ts +15 -0
- package/dist/shared/jsonc-parser.test.d.ts +1 -0
- package/dist/tools/ast-grep/index.d.ts +2 -90
- package/dist/tools/ast-grep/tools.d.ts +3 -88
- package/dist/tools/background-task/tools.d.ts +4 -38
- package/dist/tools/call-omo-agent/tools.d.ts +2 -21
- package/dist/tools/glob/tools.d.ts +2 -11
- package/dist/tools/grep/tools.d.ts +2 -13
- package/dist/tools/index.d.ts +3 -367
- package/dist/tools/interactive-bash/tools.d.ts +2 -9
- package/dist/tools/look-at/tools.d.ts +2 -12
- package/dist/tools/lsp/tools.d.ts +12 -152
- package/dist/tools/session-manager/tools.d.ts +5 -52
- package/dist/tools/slashcommand/tools.d.ts +2 -9
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -574,7 +574,7 @@ var require_parse = __commonJS((exports, module) => {
|
|
|
574
574
|
var syntaxError = (type, char) => {
|
|
575
575
|
return `Missing ${type}: "${char}" - use "\\\\${char}" to match literal characters`;
|
|
576
576
|
};
|
|
577
|
-
var
|
|
577
|
+
var parse3 = (input, options) => {
|
|
578
578
|
if (typeof input !== "string") {
|
|
579
579
|
throw new TypeError("Expected a string");
|
|
580
580
|
}
|
|
@@ -723,7 +723,7 @@ var require_parse = __commonJS((exports, module) => {
|
|
|
723
723
|
output = token.close = `)$))${extglobStar}`;
|
|
724
724
|
}
|
|
725
725
|
if (token.inner.includes("*") && (rest = remaining()) && /^\.[^\\/.]+$/.test(rest)) {
|
|
726
|
-
const expression =
|
|
726
|
+
const expression = parse3(rest, { ...options, fastpaths: false }).output;
|
|
727
727
|
output = token.close = `)${expression})${extglobStar})`;
|
|
728
728
|
}
|
|
729
729
|
if (token.prev.type === "bos") {
|
|
@@ -1249,7 +1249,7 @@ var require_parse = __commonJS((exports, module) => {
|
|
|
1249
1249
|
}
|
|
1250
1250
|
return state2;
|
|
1251
1251
|
};
|
|
1252
|
-
|
|
1252
|
+
parse3.fastpaths = (input, options) => {
|
|
1253
1253
|
const opts = { ...options };
|
|
1254
1254
|
const max = typeof opts.maxLength === "number" ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH;
|
|
1255
1255
|
const len = input.length;
|
|
@@ -1317,13 +1317,13 @@ var require_parse = __commonJS((exports, module) => {
|
|
|
1317
1317
|
}
|
|
1318
1318
|
return source;
|
|
1319
1319
|
};
|
|
1320
|
-
module.exports =
|
|
1320
|
+
module.exports = parse3;
|
|
1321
1321
|
});
|
|
1322
1322
|
|
|
1323
1323
|
// node_modules/picomatch/lib/picomatch.js
|
|
1324
1324
|
var require_picomatch = __commonJS((exports, module) => {
|
|
1325
1325
|
var scan = require_scan();
|
|
1326
|
-
var
|
|
1326
|
+
var parse3 = require_parse();
|
|
1327
1327
|
var utils = require_utils();
|
|
1328
1328
|
var constants = require_constants();
|
|
1329
1329
|
var isObject = (val) => val && typeof val === "object" && !Array.isArray(val);
|
|
@@ -1389,11 +1389,11 @@ var require_picomatch = __commonJS((exports, module) => {
|
|
|
1389
1389
|
return { isMatch: false, output: "" };
|
|
1390
1390
|
}
|
|
1391
1391
|
const opts = options || {};
|
|
1392
|
-
const
|
|
1392
|
+
const format2 = opts.format || (posix ? utils.toPosixSlashes : null);
|
|
1393
1393
|
let match = input === glob;
|
|
1394
|
-
let output = match &&
|
|
1394
|
+
let output = match && format2 ? format2(input) : input;
|
|
1395
1395
|
if (match === false) {
|
|
1396
|
-
output =
|
|
1396
|
+
output = format2 ? format2(input) : input;
|
|
1397
1397
|
match = output === glob;
|
|
1398
1398
|
}
|
|
1399
1399
|
if (match === false || opts.capture === true) {
|
|
@@ -1413,7 +1413,7 @@ var require_picomatch = __commonJS((exports, module) => {
|
|
|
1413
1413
|
picomatch.parse = (pattern, options) => {
|
|
1414
1414
|
if (Array.isArray(pattern))
|
|
1415
1415
|
return pattern.map((p) => picomatch.parse(p, options));
|
|
1416
|
-
return
|
|
1416
|
+
return parse3(pattern, { ...options, fastpaths: false });
|
|
1417
1417
|
};
|
|
1418
1418
|
picomatch.scan = (input, options) => scan(input, options);
|
|
1419
1419
|
picomatch.compileRe = (state2, options, returnOutput = false, returnState = false) => {
|
|
@@ -1439,10 +1439,10 @@ var require_picomatch = __commonJS((exports, module) => {
|
|
|
1439
1439
|
}
|
|
1440
1440
|
let parsed = { negated: false, fastpaths: true };
|
|
1441
1441
|
if (options.fastpaths !== false && (input[0] === "." || input[0] === "*")) {
|
|
1442
|
-
parsed.output =
|
|
1442
|
+
parsed.output = parse3.fastpaths(input, options);
|
|
1443
1443
|
}
|
|
1444
1444
|
if (!parsed.output) {
|
|
1445
|
-
parsed =
|
|
1445
|
+
parsed = parse3(input, options);
|
|
1446
1446
|
}
|
|
1447
1447
|
return picomatch.compileRe(parsed, options, returnOutput, returnState);
|
|
1448
1448
|
};
|
|
@@ -3228,10 +3228,13 @@ async function getContextWindowUsage(ctx, sessionID) {
|
|
|
3228
3228
|
}
|
|
3229
3229
|
}
|
|
3230
3230
|
async function dynamicTruncate(ctx, sessionID, output, options = {}) {
|
|
3231
|
-
const {
|
|
3231
|
+
const {
|
|
3232
|
+
targetMaxTokens = DEFAULT_TARGET_MAX_TOKENS,
|
|
3233
|
+
preserveHeaderLines = 3
|
|
3234
|
+
} = options;
|
|
3232
3235
|
const usage = await getContextWindowUsage(ctx, sessionID);
|
|
3233
3236
|
if (!usage) {
|
|
3234
|
-
return
|
|
3237
|
+
return truncateToTokenLimit(output, targetMaxTokens, preserveHeaderLines);
|
|
3235
3238
|
}
|
|
3236
3239
|
const maxOutputTokens = Math.min(usage.remainingTokens * 0.5, targetMaxTokens);
|
|
3237
3240
|
if (maxOutputTokens <= 0) {
|
|
@@ -3292,6 +3295,884 @@ function clearConfigLoadErrors() {
|
|
|
3292
3295
|
function addConfigLoadError(error) {
|
|
3293
3296
|
configLoadErrors.push(error);
|
|
3294
3297
|
}
|
|
3298
|
+
// src/shared/claude-config-dir.ts
|
|
3299
|
+
import { homedir as homedir4 } from "os";
|
|
3300
|
+
import { join as join5 } from "path";
|
|
3301
|
+
function getClaudeConfigDir() {
|
|
3302
|
+
const envConfigDir = process.env.CLAUDE_CONFIG_DIR;
|
|
3303
|
+
if (envConfigDir) {
|
|
3304
|
+
return envConfigDir;
|
|
3305
|
+
}
|
|
3306
|
+
return join5(homedir4(), ".claude");
|
|
3307
|
+
}
|
|
3308
|
+
// src/shared/jsonc-parser.ts
|
|
3309
|
+
import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
|
|
3310
|
+
|
|
3311
|
+
// node_modules/jsonc-parser/lib/esm/impl/scanner.js
|
|
3312
|
+
function createScanner(text, ignoreTrivia = false) {
|
|
3313
|
+
const len = text.length;
|
|
3314
|
+
let pos = 0, value = "", tokenOffset = 0, token = 16, lineNumber = 0, lineStartOffset = 0, tokenLineStartOffset = 0, prevTokenLineStartOffset = 0, scanError = 0;
|
|
3315
|
+
function scanHexDigits(count, exact) {
|
|
3316
|
+
let digits = 0;
|
|
3317
|
+
let value2 = 0;
|
|
3318
|
+
while (digits < count || !exact) {
|
|
3319
|
+
let ch = text.charCodeAt(pos);
|
|
3320
|
+
if (ch >= 48 && ch <= 57) {
|
|
3321
|
+
value2 = value2 * 16 + ch - 48;
|
|
3322
|
+
} else if (ch >= 65 && ch <= 70) {
|
|
3323
|
+
value2 = value2 * 16 + ch - 65 + 10;
|
|
3324
|
+
} else if (ch >= 97 && ch <= 102) {
|
|
3325
|
+
value2 = value2 * 16 + ch - 97 + 10;
|
|
3326
|
+
} else {
|
|
3327
|
+
break;
|
|
3328
|
+
}
|
|
3329
|
+
pos++;
|
|
3330
|
+
digits++;
|
|
3331
|
+
}
|
|
3332
|
+
if (digits < count) {
|
|
3333
|
+
value2 = -1;
|
|
3334
|
+
}
|
|
3335
|
+
return value2;
|
|
3336
|
+
}
|
|
3337
|
+
function setPosition(newPosition) {
|
|
3338
|
+
pos = newPosition;
|
|
3339
|
+
value = "";
|
|
3340
|
+
tokenOffset = 0;
|
|
3341
|
+
token = 16;
|
|
3342
|
+
scanError = 0;
|
|
3343
|
+
}
|
|
3344
|
+
function scanNumber() {
|
|
3345
|
+
let start = pos;
|
|
3346
|
+
if (text.charCodeAt(pos) === 48) {
|
|
3347
|
+
pos++;
|
|
3348
|
+
} else {
|
|
3349
|
+
pos++;
|
|
3350
|
+
while (pos < text.length && isDigit(text.charCodeAt(pos))) {
|
|
3351
|
+
pos++;
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3354
|
+
if (pos < text.length && text.charCodeAt(pos) === 46) {
|
|
3355
|
+
pos++;
|
|
3356
|
+
if (pos < text.length && isDigit(text.charCodeAt(pos))) {
|
|
3357
|
+
pos++;
|
|
3358
|
+
while (pos < text.length && isDigit(text.charCodeAt(pos))) {
|
|
3359
|
+
pos++;
|
|
3360
|
+
}
|
|
3361
|
+
} else {
|
|
3362
|
+
scanError = 3;
|
|
3363
|
+
return text.substring(start, pos);
|
|
3364
|
+
}
|
|
3365
|
+
}
|
|
3366
|
+
let end = pos;
|
|
3367
|
+
if (pos < text.length && (text.charCodeAt(pos) === 69 || text.charCodeAt(pos) === 101)) {
|
|
3368
|
+
pos++;
|
|
3369
|
+
if (pos < text.length && text.charCodeAt(pos) === 43 || text.charCodeAt(pos) === 45) {
|
|
3370
|
+
pos++;
|
|
3371
|
+
}
|
|
3372
|
+
if (pos < text.length && isDigit(text.charCodeAt(pos))) {
|
|
3373
|
+
pos++;
|
|
3374
|
+
while (pos < text.length && isDigit(text.charCodeAt(pos))) {
|
|
3375
|
+
pos++;
|
|
3376
|
+
}
|
|
3377
|
+
end = pos;
|
|
3378
|
+
} else {
|
|
3379
|
+
scanError = 3;
|
|
3380
|
+
}
|
|
3381
|
+
}
|
|
3382
|
+
return text.substring(start, end);
|
|
3383
|
+
}
|
|
3384
|
+
function scanString() {
|
|
3385
|
+
let result = "", start = pos;
|
|
3386
|
+
while (true) {
|
|
3387
|
+
if (pos >= len) {
|
|
3388
|
+
result += text.substring(start, pos);
|
|
3389
|
+
scanError = 2;
|
|
3390
|
+
break;
|
|
3391
|
+
}
|
|
3392
|
+
const ch = text.charCodeAt(pos);
|
|
3393
|
+
if (ch === 34) {
|
|
3394
|
+
result += text.substring(start, pos);
|
|
3395
|
+
pos++;
|
|
3396
|
+
break;
|
|
3397
|
+
}
|
|
3398
|
+
if (ch === 92) {
|
|
3399
|
+
result += text.substring(start, pos);
|
|
3400
|
+
pos++;
|
|
3401
|
+
if (pos >= len) {
|
|
3402
|
+
scanError = 2;
|
|
3403
|
+
break;
|
|
3404
|
+
}
|
|
3405
|
+
const ch2 = text.charCodeAt(pos++);
|
|
3406
|
+
switch (ch2) {
|
|
3407
|
+
case 34:
|
|
3408
|
+
result += '"';
|
|
3409
|
+
break;
|
|
3410
|
+
case 92:
|
|
3411
|
+
result += "\\";
|
|
3412
|
+
break;
|
|
3413
|
+
case 47:
|
|
3414
|
+
result += "/";
|
|
3415
|
+
break;
|
|
3416
|
+
case 98:
|
|
3417
|
+
result += "\b";
|
|
3418
|
+
break;
|
|
3419
|
+
case 102:
|
|
3420
|
+
result += "\f";
|
|
3421
|
+
break;
|
|
3422
|
+
case 110:
|
|
3423
|
+
result += `
|
|
3424
|
+
`;
|
|
3425
|
+
break;
|
|
3426
|
+
case 114:
|
|
3427
|
+
result += "\r";
|
|
3428
|
+
break;
|
|
3429
|
+
case 116:
|
|
3430
|
+
result += "\t";
|
|
3431
|
+
break;
|
|
3432
|
+
case 117:
|
|
3433
|
+
const ch3 = scanHexDigits(4, true);
|
|
3434
|
+
if (ch3 >= 0) {
|
|
3435
|
+
result += String.fromCharCode(ch3);
|
|
3436
|
+
} else {
|
|
3437
|
+
scanError = 4;
|
|
3438
|
+
}
|
|
3439
|
+
break;
|
|
3440
|
+
default:
|
|
3441
|
+
scanError = 5;
|
|
3442
|
+
}
|
|
3443
|
+
start = pos;
|
|
3444
|
+
continue;
|
|
3445
|
+
}
|
|
3446
|
+
if (ch >= 0 && ch <= 31) {
|
|
3447
|
+
if (isLineBreak(ch)) {
|
|
3448
|
+
result += text.substring(start, pos);
|
|
3449
|
+
scanError = 2;
|
|
3450
|
+
break;
|
|
3451
|
+
} else {
|
|
3452
|
+
scanError = 6;
|
|
3453
|
+
}
|
|
3454
|
+
}
|
|
3455
|
+
pos++;
|
|
3456
|
+
}
|
|
3457
|
+
return result;
|
|
3458
|
+
}
|
|
3459
|
+
function scanNext() {
|
|
3460
|
+
value = "";
|
|
3461
|
+
scanError = 0;
|
|
3462
|
+
tokenOffset = pos;
|
|
3463
|
+
lineStartOffset = lineNumber;
|
|
3464
|
+
prevTokenLineStartOffset = tokenLineStartOffset;
|
|
3465
|
+
if (pos >= len) {
|
|
3466
|
+
tokenOffset = len;
|
|
3467
|
+
return token = 17;
|
|
3468
|
+
}
|
|
3469
|
+
let code = text.charCodeAt(pos);
|
|
3470
|
+
if (isWhiteSpace(code)) {
|
|
3471
|
+
do {
|
|
3472
|
+
pos++;
|
|
3473
|
+
value += String.fromCharCode(code);
|
|
3474
|
+
code = text.charCodeAt(pos);
|
|
3475
|
+
} while (isWhiteSpace(code));
|
|
3476
|
+
return token = 15;
|
|
3477
|
+
}
|
|
3478
|
+
if (isLineBreak(code)) {
|
|
3479
|
+
pos++;
|
|
3480
|
+
value += String.fromCharCode(code);
|
|
3481
|
+
if (code === 13 && text.charCodeAt(pos) === 10) {
|
|
3482
|
+
pos++;
|
|
3483
|
+
value += `
|
|
3484
|
+
`;
|
|
3485
|
+
}
|
|
3486
|
+
lineNumber++;
|
|
3487
|
+
tokenLineStartOffset = pos;
|
|
3488
|
+
return token = 14;
|
|
3489
|
+
}
|
|
3490
|
+
switch (code) {
|
|
3491
|
+
case 123:
|
|
3492
|
+
pos++;
|
|
3493
|
+
return token = 1;
|
|
3494
|
+
case 125:
|
|
3495
|
+
pos++;
|
|
3496
|
+
return token = 2;
|
|
3497
|
+
case 91:
|
|
3498
|
+
pos++;
|
|
3499
|
+
return token = 3;
|
|
3500
|
+
case 93:
|
|
3501
|
+
pos++;
|
|
3502
|
+
return token = 4;
|
|
3503
|
+
case 58:
|
|
3504
|
+
pos++;
|
|
3505
|
+
return token = 6;
|
|
3506
|
+
case 44:
|
|
3507
|
+
pos++;
|
|
3508
|
+
return token = 5;
|
|
3509
|
+
case 34:
|
|
3510
|
+
pos++;
|
|
3511
|
+
value = scanString();
|
|
3512
|
+
return token = 10;
|
|
3513
|
+
case 47:
|
|
3514
|
+
const start = pos - 1;
|
|
3515
|
+
if (text.charCodeAt(pos + 1) === 47) {
|
|
3516
|
+
pos += 2;
|
|
3517
|
+
while (pos < len) {
|
|
3518
|
+
if (isLineBreak(text.charCodeAt(pos))) {
|
|
3519
|
+
break;
|
|
3520
|
+
}
|
|
3521
|
+
pos++;
|
|
3522
|
+
}
|
|
3523
|
+
value = text.substring(start, pos);
|
|
3524
|
+
return token = 12;
|
|
3525
|
+
}
|
|
3526
|
+
if (text.charCodeAt(pos + 1) === 42) {
|
|
3527
|
+
pos += 2;
|
|
3528
|
+
const safeLength = len - 1;
|
|
3529
|
+
let commentClosed = false;
|
|
3530
|
+
while (pos < safeLength) {
|
|
3531
|
+
const ch = text.charCodeAt(pos);
|
|
3532
|
+
if (ch === 42 && text.charCodeAt(pos + 1) === 47) {
|
|
3533
|
+
pos += 2;
|
|
3534
|
+
commentClosed = true;
|
|
3535
|
+
break;
|
|
3536
|
+
}
|
|
3537
|
+
pos++;
|
|
3538
|
+
if (isLineBreak(ch)) {
|
|
3539
|
+
if (ch === 13 && text.charCodeAt(pos) === 10) {
|
|
3540
|
+
pos++;
|
|
3541
|
+
}
|
|
3542
|
+
lineNumber++;
|
|
3543
|
+
tokenLineStartOffset = pos;
|
|
3544
|
+
}
|
|
3545
|
+
}
|
|
3546
|
+
if (!commentClosed) {
|
|
3547
|
+
pos++;
|
|
3548
|
+
scanError = 1;
|
|
3549
|
+
}
|
|
3550
|
+
value = text.substring(start, pos);
|
|
3551
|
+
return token = 13;
|
|
3552
|
+
}
|
|
3553
|
+
value += String.fromCharCode(code);
|
|
3554
|
+
pos++;
|
|
3555
|
+
return token = 16;
|
|
3556
|
+
case 45:
|
|
3557
|
+
value += String.fromCharCode(code);
|
|
3558
|
+
pos++;
|
|
3559
|
+
if (pos === len || !isDigit(text.charCodeAt(pos))) {
|
|
3560
|
+
return token = 16;
|
|
3561
|
+
}
|
|
3562
|
+
case 48:
|
|
3563
|
+
case 49:
|
|
3564
|
+
case 50:
|
|
3565
|
+
case 51:
|
|
3566
|
+
case 52:
|
|
3567
|
+
case 53:
|
|
3568
|
+
case 54:
|
|
3569
|
+
case 55:
|
|
3570
|
+
case 56:
|
|
3571
|
+
case 57:
|
|
3572
|
+
value += scanNumber();
|
|
3573
|
+
return token = 11;
|
|
3574
|
+
default:
|
|
3575
|
+
while (pos < len && isUnknownContentCharacter(code)) {
|
|
3576
|
+
pos++;
|
|
3577
|
+
code = text.charCodeAt(pos);
|
|
3578
|
+
}
|
|
3579
|
+
if (tokenOffset !== pos) {
|
|
3580
|
+
value = text.substring(tokenOffset, pos);
|
|
3581
|
+
switch (value) {
|
|
3582
|
+
case "true":
|
|
3583
|
+
return token = 8;
|
|
3584
|
+
case "false":
|
|
3585
|
+
return token = 9;
|
|
3586
|
+
case "null":
|
|
3587
|
+
return token = 7;
|
|
3588
|
+
}
|
|
3589
|
+
return token = 16;
|
|
3590
|
+
}
|
|
3591
|
+
value += String.fromCharCode(code);
|
|
3592
|
+
pos++;
|
|
3593
|
+
return token = 16;
|
|
3594
|
+
}
|
|
3595
|
+
}
|
|
3596
|
+
function isUnknownContentCharacter(code) {
|
|
3597
|
+
if (isWhiteSpace(code) || isLineBreak(code)) {
|
|
3598
|
+
return false;
|
|
3599
|
+
}
|
|
3600
|
+
switch (code) {
|
|
3601
|
+
case 125:
|
|
3602
|
+
case 93:
|
|
3603
|
+
case 123:
|
|
3604
|
+
case 91:
|
|
3605
|
+
case 34:
|
|
3606
|
+
case 58:
|
|
3607
|
+
case 44:
|
|
3608
|
+
case 47:
|
|
3609
|
+
return false;
|
|
3610
|
+
}
|
|
3611
|
+
return true;
|
|
3612
|
+
}
|
|
3613
|
+
function scanNextNonTrivia() {
|
|
3614
|
+
let result;
|
|
3615
|
+
do {
|
|
3616
|
+
result = scanNext();
|
|
3617
|
+
} while (result >= 12 && result <= 15);
|
|
3618
|
+
return result;
|
|
3619
|
+
}
|
|
3620
|
+
return {
|
|
3621
|
+
setPosition,
|
|
3622
|
+
getPosition: () => pos,
|
|
3623
|
+
scan: ignoreTrivia ? scanNextNonTrivia : scanNext,
|
|
3624
|
+
getToken: () => token,
|
|
3625
|
+
getTokenValue: () => value,
|
|
3626
|
+
getTokenOffset: () => tokenOffset,
|
|
3627
|
+
getTokenLength: () => pos - tokenOffset,
|
|
3628
|
+
getTokenStartLine: () => lineStartOffset,
|
|
3629
|
+
getTokenStartCharacter: () => tokenOffset - prevTokenLineStartOffset,
|
|
3630
|
+
getTokenError: () => scanError
|
|
3631
|
+
};
|
|
3632
|
+
}
|
|
3633
|
+
function isWhiteSpace(ch) {
|
|
3634
|
+
return ch === 32 || ch === 9;
|
|
3635
|
+
}
|
|
3636
|
+
function isLineBreak(ch) {
|
|
3637
|
+
return ch === 10 || ch === 13;
|
|
3638
|
+
}
|
|
3639
|
+
function isDigit(ch) {
|
|
3640
|
+
return ch >= 48 && ch <= 57;
|
|
3641
|
+
}
|
|
3642
|
+
var CharacterCodes;
|
|
3643
|
+
(function(CharacterCodes2) {
|
|
3644
|
+
CharacterCodes2[CharacterCodes2["lineFeed"] = 10] = "lineFeed";
|
|
3645
|
+
CharacterCodes2[CharacterCodes2["carriageReturn"] = 13] = "carriageReturn";
|
|
3646
|
+
CharacterCodes2[CharacterCodes2["space"] = 32] = "space";
|
|
3647
|
+
CharacterCodes2[CharacterCodes2["_0"] = 48] = "_0";
|
|
3648
|
+
CharacterCodes2[CharacterCodes2["_1"] = 49] = "_1";
|
|
3649
|
+
CharacterCodes2[CharacterCodes2["_2"] = 50] = "_2";
|
|
3650
|
+
CharacterCodes2[CharacterCodes2["_3"] = 51] = "_3";
|
|
3651
|
+
CharacterCodes2[CharacterCodes2["_4"] = 52] = "_4";
|
|
3652
|
+
CharacterCodes2[CharacterCodes2["_5"] = 53] = "_5";
|
|
3653
|
+
CharacterCodes2[CharacterCodes2["_6"] = 54] = "_6";
|
|
3654
|
+
CharacterCodes2[CharacterCodes2["_7"] = 55] = "_7";
|
|
3655
|
+
CharacterCodes2[CharacterCodes2["_8"] = 56] = "_8";
|
|
3656
|
+
CharacterCodes2[CharacterCodes2["_9"] = 57] = "_9";
|
|
3657
|
+
CharacterCodes2[CharacterCodes2["a"] = 97] = "a";
|
|
3658
|
+
CharacterCodes2[CharacterCodes2["b"] = 98] = "b";
|
|
3659
|
+
CharacterCodes2[CharacterCodes2["c"] = 99] = "c";
|
|
3660
|
+
CharacterCodes2[CharacterCodes2["d"] = 100] = "d";
|
|
3661
|
+
CharacterCodes2[CharacterCodes2["e"] = 101] = "e";
|
|
3662
|
+
CharacterCodes2[CharacterCodes2["f"] = 102] = "f";
|
|
3663
|
+
CharacterCodes2[CharacterCodes2["g"] = 103] = "g";
|
|
3664
|
+
CharacterCodes2[CharacterCodes2["h"] = 104] = "h";
|
|
3665
|
+
CharacterCodes2[CharacterCodes2["i"] = 105] = "i";
|
|
3666
|
+
CharacterCodes2[CharacterCodes2["j"] = 106] = "j";
|
|
3667
|
+
CharacterCodes2[CharacterCodes2["k"] = 107] = "k";
|
|
3668
|
+
CharacterCodes2[CharacterCodes2["l"] = 108] = "l";
|
|
3669
|
+
CharacterCodes2[CharacterCodes2["m"] = 109] = "m";
|
|
3670
|
+
CharacterCodes2[CharacterCodes2["n"] = 110] = "n";
|
|
3671
|
+
CharacterCodes2[CharacterCodes2["o"] = 111] = "o";
|
|
3672
|
+
CharacterCodes2[CharacterCodes2["p"] = 112] = "p";
|
|
3673
|
+
CharacterCodes2[CharacterCodes2["q"] = 113] = "q";
|
|
3674
|
+
CharacterCodes2[CharacterCodes2["r"] = 114] = "r";
|
|
3675
|
+
CharacterCodes2[CharacterCodes2["s"] = 115] = "s";
|
|
3676
|
+
CharacterCodes2[CharacterCodes2["t"] = 116] = "t";
|
|
3677
|
+
CharacterCodes2[CharacterCodes2["u"] = 117] = "u";
|
|
3678
|
+
CharacterCodes2[CharacterCodes2["v"] = 118] = "v";
|
|
3679
|
+
CharacterCodes2[CharacterCodes2["w"] = 119] = "w";
|
|
3680
|
+
CharacterCodes2[CharacterCodes2["x"] = 120] = "x";
|
|
3681
|
+
CharacterCodes2[CharacterCodes2["y"] = 121] = "y";
|
|
3682
|
+
CharacterCodes2[CharacterCodes2["z"] = 122] = "z";
|
|
3683
|
+
CharacterCodes2[CharacterCodes2["A"] = 65] = "A";
|
|
3684
|
+
CharacterCodes2[CharacterCodes2["B"] = 66] = "B";
|
|
3685
|
+
CharacterCodes2[CharacterCodes2["C"] = 67] = "C";
|
|
3686
|
+
CharacterCodes2[CharacterCodes2["D"] = 68] = "D";
|
|
3687
|
+
CharacterCodes2[CharacterCodes2["E"] = 69] = "E";
|
|
3688
|
+
CharacterCodes2[CharacterCodes2["F"] = 70] = "F";
|
|
3689
|
+
CharacterCodes2[CharacterCodes2["G"] = 71] = "G";
|
|
3690
|
+
CharacterCodes2[CharacterCodes2["H"] = 72] = "H";
|
|
3691
|
+
CharacterCodes2[CharacterCodes2["I"] = 73] = "I";
|
|
3692
|
+
CharacterCodes2[CharacterCodes2["J"] = 74] = "J";
|
|
3693
|
+
CharacterCodes2[CharacterCodes2["K"] = 75] = "K";
|
|
3694
|
+
CharacterCodes2[CharacterCodes2["L"] = 76] = "L";
|
|
3695
|
+
CharacterCodes2[CharacterCodes2["M"] = 77] = "M";
|
|
3696
|
+
CharacterCodes2[CharacterCodes2["N"] = 78] = "N";
|
|
3697
|
+
CharacterCodes2[CharacterCodes2["O"] = 79] = "O";
|
|
3698
|
+
CharacterCodes2[CharacterCodes2["P"] = 80] = "P";
|
|
3699
|
+
CharacterCodes2[CharacterCodes2["Q"] = 81] = "Q";
|
|
3700
|
+
CharacterCodes2[CharacterCodes2["R"] = 82] = "R";
|
|
3701
|
+
CharacterCodes2[CharacterCodes2["S"] = 83] = "S";
|
|
3702
|
+
CharacterCodes2[CharacterCodes2["T"] = 84] = "T";
|
|
3703
|
+
CharacterCodes2[CharacterCodes2["U"] = 85] = "U";
|
|
3704
|
+
CharacterCodes2[CharacterCodes2["V"] = 86] = "V";
|
|
3705
|
+
CharacterCodes2[CharacterCodes2["W"] = 87] = "W";
|
|
3706
|
+
CharacterCodes2[CharacterCodes2["X"] = 88] = "X";
|
|
3707
|
+
CharacterCodes2[CharacterCodes2["Y"] = 89] = "Y";
|
|
3708
|
+
CharacterCodes2[CharacterCodes2["Z"] = 90] = "Z";
|
|
3709
|
+
CharacterCodes2[CharacterCodes2["asterisk"] = 42] = "asterisk";
|
|
3710
|
+
CharacterCodes2[CharacterCodes2["backslash"] = 92] = "backslash";
|
|
3711
|
+
CharacterCodes2[CharacterCodes2["closeBrace"] = 125] = "closeBrace";
|
|
3712
|
+
CharacterCodes2[CharacterCodes2["closeBracket"] = 93] = "closeBracket";
|
|
3713
|
+
CharacterCodes2[CharacterCodes2["colon"] = 58] = "colon";
|
|
3714
|
+
CharacterCodes2[CharacterCodes2["comma"] = 44] = "comma";
|
|
3715
|
+
CharacterCodes2[CharacterCodes2["dot"] = 46] = "dot";
|
|
3716
|
+
CharacterCodes2[CharacterCodes2["doubleQuote"] = 34] = "doubleQuote";
|
|
3717
|
+
CharacterCodes2[CharacterCodes2["minus"] = 45] = "minus";
|
|
3718
|
+
CharacterCodes2[CharacterCodes2["openBrace"] = 123] = "openBrace";
|
|
3719
|
+
CharacterCodes2[CharacterCodes2["openBracket"] = 91] = "openBracket";
|
|
3720
|
+
CharacterCodes2[CharacterCodes2["plus"] = 43] = "plus";
|
|
3721
|
+
CharacterCodes2[CharacterCodes2["slash"] = 47] = "slash";
|
|
3722
|
+
CharacterCodes2[CharacterCodes2["formFeed"] = 12] = "formFeed";
|
|
3723
|
+
CharacterCodes2[CharacterCodes2["tab"] = 9] = "tab";
|
|
3724
|
+
})(CharacterCodes || (CharacterCodes = {}));
|
|
3725
|
+
|
|
3726
|
+
// node_modules/jsonc-parser/lib/esm/impl/string-intern.js
|
|
3727
|
+
var cachedSpaces = new Array(20).fill(0).map((_, index) => {
|
|
3728
|
+
return " ".repeat(index);
|
|
3729
|
+
});
|
|
3730
|
+
var maxCachedValues = 200;
|
|
3731
|
+
var cachedBreakLinesWithSpaces = {
|
|
3732
|
+
" ": {
|
|
3733
|
+
"\n": new Array(maxCachedValues).fill(0).map((_, index) => {
|
|
3734
|
+
return `
|
|
3735
|
+
` + " ".repeat(index);
|
|
3736
|
+
}),
|
|
3737
|
+
"\r": new Array(maxCachedValues).fill(0).map((_, index) => {
|
|
3738
|
+
return "\r" + " ".repeat(index);
|
|
3739
|
+
}),
|
|
3740
|
+
"\r\n": new Array(maxCachedValues).fill(0).map((_, index) => {
|
|
3741
|
+
return `\r
|
|
3742
|
+
` + " ".repeat(index);
|
|
3743
|
+
})
|
|
3744
|
+
},
|
|
3745
|
+
"\t": {
|
|
3746
|
+
"\n": new Array(maxCachedValues).fill(0).map((_, index) => {
|
|
3747
|
+
return `
|
|
3748
|
+
` + "\t".repeat(index);
|
|
3749
|
+
}),
|
|
3750
|
+
"\r": new Array(maxCachedValues).fill(0).map((_, index) => {
|
|
3751
|
+
return "\r" + "\t".repeat(index);
|
|
3752
|
+
}),
|
|
3753
|
+
"\r\n": new Array(maxCachedValues).fill(0).map((_, index) => {
|
|
3754
|
+
return `\r
|
|
3755
|
+
` + "\t".repeat(index);
|
|
3756
|
+
})
|
|
3757
|
+
}
|
|
3758
|
+
};
|
|
3759
|
+
|
|
3760
|
+
// node_modules/jsonc-parser/lib/esm/impl/parser.js
|
|
3761
|
+
var ParseOptions;
|
|
3762
|
+
(function(ParseOptions2) {
|
|
3763
|
+
ParseOptions2.DEFAULT = {
|
|
3764
|
+
allowTrailingComma: false
|
|
3765
|
+
};
|
|
3766
|
+
})(ParseOptions || (ParseOptions = {}));
|
|
3767
|
+
function parse(text, errors = [], options = ParseOptions.DEFAULT) {
|
|
3768
|
+
let currentProperty = null;
|
|
3769
|
+
let currentParent = [];
|
|
3770
|
+
const previousParents = [];
|
|
3771
|
+
function onValue(value) {
|
|
3772
|
+
if (Array.isArray(currentParent)) {
|
|
3773
|
+
currentParent.push(value);
|
|
3774
|
+
} else if (currentProperty !== null) {
|
|
3775
|
+
currentParent[currentProperty] = value;
|
|
3776
|
+
}
|
|
3777
|
+
}
|
|
3778
|
+
const visitor = {
|
|
3779
|
+
onObjectBegin: () => {
|
|
3780
|
+
const object = {};
|
|
3781
|
+
onValue(object);
|
|
3782
|
+
previousParents.push(currentParent);
|
|
3783
|
+
currentParent = object;
|
|
3784
|
+
currentProperty = null;
|
|
3785
|
+
},
|
|
3786
|
+
onObjectProperty: (name) => {
|
|
3787
|
+
currentProperty = name;
|
|
3788
|
+
},
|
|
3789
|
+
onObjectEnd: () => {
|
|
3790
|
+
currentParent = previousParents.pop();
|
|
3791
|
+
},
|
|
3792
|
+
onArrayBegin: () => {
|
|
3793
|
+
const array = [];
|
|
3794
|
+
onValue(array);
|
|
3795
|
+
previousParents.push(currentParent);
|
|
3796
|
+
currentParent = array;
|
|
3797
|
+
currentProperty = null;
|
|
3798
|
+
},
|
|
3799
|
+
onArrayEnd: () => {
|
|
3800
|
+
currentParent = previousParents.pop();
|
|
3801
|
+
},
|
|
3802
|
+
onLiteralValue: onValue,
|
|
3803
|
+
onError: (error, offset, length) => {
|
|
3804
|
+
errors.push({ error, offset, length });
|
|
3805
|
+
}
|
|
3806
|
+
};
|
|
3807
|
+
visit(text, visitor, options);
|
|
3808
|
+
return currentParent[0];
|
|
3809
|
+
}
|
|
3810
|
+
function visit(text, visitor, options = ParseOptions.DEFAULT) {
|
|
3811
|
+
const _scanner = createScanner(text, false);
|
|
3812
|
+
const _jsonPath = [];
|
|
3813
|
+
let suppressedCallbacks = 0;
|
|
3814
|
+
function toNoArgVisit(visitFunction) {
|
|
3815
|
+
return visitFunction ? () => suppressedCallbacks === 0 && visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter()) : () => true;
|
|
3816
|
+
}
|
|
3817
|
+
function toOneArgVisit(visitFunction) {
|
|
3818
|
+
return visitFunction ? (arg) => suppressedCallbacks === 0 && visitFunction(arg, _scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter()) : () => true;
|
|
3819
|
+
}
|
|
3820
|
+
function toOneArgVisitWithPath(visitFunction) {
|
|
3821
|
+
return visitFunction ? (arg) => suppressedCallbacks === 0 && visitFunction(arg, _scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter(), () => _jsonPath.slice()) : () => true;
|
|
3822
|
+
}
|
|
3823
|
+
function toBeginVisit(visitFunction) {
|
|
3824
|
+
return visitFunction ? () => {
|
|
3825
|
+
if (suppressedCallbacks > 0) {
|
|
3826
|
+
suppressedCallbacks++;
|
|
3827
|
+
} else {
|
|
3828
|
+
let cbReturn = visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter(), () => _jsonPath.slice());
|
|
3829
|
+
if (cbReturn === false) {
|
|
3830
|
+
suppressedCallbacks = 1;
|
|
3831
|
+
}
|
|
3832
|
+
}
|
|
3833
|
+
} : () => true;
|
|
3834
|
+
}
|
|
3835
|
+
function toEndVisit(visitFunction) {
|
|
3836
|
+
return visitFunction ? () => {
|
|
3837
|
+
if (suppressedCallbacks > 0) {
|
|
3838
|
+
suppressedCallbacks--;
|
|
3839
|
+
}
|
|
3840
|
+
if (suppressedCallbacks === 0) {
|
|
3841
|
+
visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter());
|
|
3842
|
+
}
|
|
3843
|
+
} : () => true;
|
|
3844
|
+
}
|
|
3845
|
+
const onObjectBegin = toBeginVisit(visitor.onObjectBegin), onObjectProperty = toOneArgVisitWithPath(visitor.onObjectProperty), onObjectEnd = toEndVisit(visitor.onObjectEnd), onArrayBegin = toBeginVisit(visitor.onArrayBegin), onArrayEnd = toEndVisit(visitor.onArrayEnd), onLiteralValue = toOneArgVisitWithPath(visitor.onLiteralValue), onSeparator = toOneArgVisit(visitor.onSeparator), onComment = toNoArgVisit(visitor.onComment), onError = toOneArgVisit(visitor.onError);
|
|
3846
|
+
const disallowComments = options && options.disallowComments;
|
|
3847
|
+
const allowTrailingComma = options && options.allowTrailingComma;
|
|
3848
|
+
function scanNext() {
|
|
3849
|
+
while (true) {
|
|
3850
|
+
const token = _scanner.scan();
|
|
3851
|
+
switch (_scanner.getTokenError()) {
|
|
3852
|
+
case 4:
|
|
3853
|
+
handleError(14);
|
|
3854
|
+
break;
|
|
3855
|
+
case 5:
|
|
3856
|
+
handleError(15);
|
|
3857
|
+
break;
|
|
3858
|
+
case 3:
|
|
3859
|
+
handleError(13);
|
|
3860
|
+
break;
|
|
3861
|
+
case 1:
|
|
3862
|
+
if (!disallowComments) {
|
|
3863
|
+
handleError(11);
|
|
3864
|
+
}
|
|
3865
|
+
break;
|
|
3866
|
+
case 2:
|
|
3867
|
+
handleError(12);
|
|
3868
|
+
break;
|
|
3869
|
+
case 6:
|
|
3870
|
+
handleError(16);
|
|
3871
|
+
break;
|
|
3872
|
+
}
|
|
3873
|
+
switch (token) {
|
|
3874
|
+
case 12:
|
|
3875
|
+
case 13:
|
|
3876
|
+
if (disallowComments) {
|
|
3877
|
+
handleError(10);
|
|
3878
|
+
} else {
|
|
3879
|
+
onComment();
|
|
3880
|
+
}
|
|
3881
|
+
break;
|
|
3882
|
+
case 16:
|
|
3883
|
+
handleError(1);
|
|
3884
|
+
break;
|
|
3885
|
+
case 15:
|
|
3886
|
+
case 14:
|
|
3887
|
+
break;
|
|
3888
|
+
default:
|
|
3889
|
+
return token;
|
|
3890
|
+
}
|
|
3891
|
+
}
|
|
3892
|
+
}
|
|
3893
|
+
function handleError(error, skipUntilAfter = [], skipUntil = []) {
|
|
3894
|
+
onError(error);
|
|
3895
|
+
if (skipUntilAfter.length + skipUntil.length > 0) {
|
|
3896
|
+
let token = _scanner.getToken();
|
|
3897
|
+
while (token !== 17) {
|
|
3898
|
+
if (skipUntilAfter.indexOf(token) !== -1) {
|
|
3899
|
+
scanNext();
|
|
3900
|
+
break;
|
|
3901
|
+
} else if (skipUntil.indexOf(token) !== -1) {
|
|
3902
|
+
break;
|
|
3903
|
+
}
|
|
3904
|
+
token = scanNext();
|
|
3905
|
+
}
|
|
3906
|
+
}
|
|
3907
|
+
}
|
|
3908
|
+
function parseString(isValue) {
|
|
3909
|
+
const value = _scanner.getTokenValue();
|
|
3910
|
+
if (isValue) {
|
|
3911
|
+
onLiteralValue(value);
|
|
3912
|
+
} else {
|
|
3913
|
+
onObjectProperty(value);
|
|
3914
|
+
_jsonPath.push(value);
|
|
3915
|
+
}
|
|
3916
|
+
scanNext();
|
|
3917
|
+
return true;
|
|
3918
|
+
}
|
|
3919
|
+
function parseLiteral() {
|
|
3920
|
+
switch (_scanner.getToken()) {
|
|
3921
|
+
case 11:
|
|
3922
|
+
const tokenValue = _scanner.getTokenValue();
|
|
3923
|
+
let value = Number(tokenValue);
|
|
3924
|
+
if (isNaN(value)) {
|
|
3925
|
+
handleError(2);
|
|
3926
|
+
value = 0;
|
|
3927
|
+
}
|
|
3928
|
+
onLiteralValue(value);
|
|
3929
|
+
break;
|
|
3930
|
+
case 7:
|
|
3931
|
+
onLiteralValue(null);
|
|
3932
|
+
break;
|
|
3933
|
+
case 8:
|
|
3934
|
+
onLiteralValue(true);
|
|
3935
|
+
break;
|
|
3936
|
+
case 9:
|
|
3937
|
+
onLiteralValue(false);
|
|
3938
|
+
break;
|
|
3939
|
+
default:
|
|
3940
|
+
return false;
|
|
3941
|
+
}
|
|
3942
|
+
scanNext();
|
|
3943
|
+
return true;
|
|
3944
|
+
}
|
|
3945
|
+
function parseProperty() {
|
|
3946
|
+
if (_scanner.getToken() !== 10) {
|
|
3947
|
+
handleError(3, [], [2, 5]);
|
|
3948
|
+
return false;
|
|
3949
|
+
}
|
|
3950
|
+
parseString(false);
|
|
3951
|
+
if (_scanner.getToken() === 6) {
|
|
3952
|
+
onSeparator(":");
|
|
3953
|
+
scanNext();
|
|
3954
|
+
if (!parseValue()) {
|
|
3955
|
+
handleError(4, [], [2, 5]);
|
|
3956
|
+
}
|
|
3957
|
+
} else {
|
|
3958
|
+
handleError(5, [], [2, 5]);
|
|
3959
|
+
}
|
|
3960
|
+
_jsonPath.pop();
|
|
3961
|
+
return true;
|
|
3962
|
+
}
|
|
3963
|
+
function parseObject() {
|
|
3964
|
+
onObjectBegin();
|
|
3965
|
+
scanNext();
|
|
3966
|
+
let needsComma = false;
|
|
3967
|
+
while (_scanner.getToken() !== 2 && _scanner.getToken() !== 17) {
|
|
3968
|
+
if (_scanner.getToken() === 5) {
|
|
3969
|
+
if (!needsComma) {
|
|
3970
|
+
handleError(4, [], []);
|
|
3971
|
+
}
|
|
3972
|
+
onSeparator(",");
|
|
3973
|
+
scanNext();
|
|
3974
|
+
if (_scanner.getToken() === 2 && allowTrailingComma) {
|
|
3975
|
+
break;
|
|
3976
|
+
}
|
|
3977
|
+
} else if (needsComma) {
|
|
3978
|
+
handleError(6, [], []);
|
|
3979
|
+
}
|
|
3980
|
+
if (!parseProperty()) {
|
|
3981
|
+
handleError(4, [], [2, 5]);
|
|
3982
|
+
}
|
|
3983
|
+
needsComma = true;
|
|
3984
|
+
}
|
|
3985
|
+
onObjectEnd();
|
|
3986
|
+
if (_scanner.getToken() !== 2) {
|
|
3987
|
+
handleError(7, [2], []);
|
|
3988
|
+
} else {
|
|
3989
|
+
scanNext();
|
|
3990
|
+
}
|
|
3991
|
+
return true;
|
|
3992
|
+
}
|
|
3993
|
+
function parseArray() {
|
|
3994
|
+
onArrayBegin();
|
|
3995
|
+
scanNext();
|
|
3996
|
+
let isFirstElement = true;
|
|
3997
|
+
let needsComma = false;
|
|
3998
|
+
while (_scanner.getToken() !== 4 && _scanner.getToken() !== 17) {
|
|
3999
|
+
if (_scanner.getToken() === 5) {
|
|
4000
|
+
if (!needsComma) {
|
|
4001
|
+
handleError(4, [], []);
|
|
4002
|
+
}
|
|
4003
|
+
onSeparator(",");
|
|
4004
|
+
scanNext();
|
|
4005
|
+
if (_scanner.getToken() === 4 && allowTrailingComma) {
|
|
4006
|
+
break;
|
|
4007
|
+
}
|
|
4008
|
+
} else if (needsComma) {
|
|
4009
|
+
handleError(6, [], []);
|
|
4010
|
+
}
|
|
4011
|
+
if (isFirstElement) {
|
|
4012
|
+
_jsonPath.push(0);
|
|
4013
|
+
isFirstElement = false;
|
|
4014
|
+
} else {
|
|
4015
|
+
_jsonPath[_jsonPath.length - 1]++;
|
|
4016
|
+
}
|
|
4017
|
+
if (!parseValue()) {
|
|
4018
|
+
handleError(4, [], [4, 5]);
|
|
4019
|
+
}
|
|
4020
|
+
needsComma = true;
|
|
4021
|
+
}
|
|
4022
|
+
onArrayEnd();
|
|
4023
|
+
if (!isFirstElement) {
|
|
4024
|
+
_jsonPath.pop();
|
|
4025
|
+
}
|
|
4026
|
+
if (_scanner.getToken() !== 4) {
|
|
4027
|
+
handleError(8, [4], []);
|
|
4028
|
+
} else {
|
|
4029
|
+
scanNext();
|
|
4030
|
+
}
|
|
4031
|
+
return true;
|
|
4032
|
+
}
|
|
4033
|
+
function parseValue() {
|
|
4034
|
+
switch (_scanner.getToken()) {
|
|
4035
|
+
case 3:
|
|
4036
|
+
return parseArray();
|
|
4037
|
+
case 1:
|
|
4038
|
+
return parseObject();
|
|
4039
|
+
case 10:
|
|
4040
|
+
return parseString(true);
|
|
4041
|
+
default:
|
|
4042
|
+
return parseLiteral();
|
|
4043
|
+
}
|
|
4044
|
+
}
|
|
4045
|
+
scanNext();
|
|
4046
|
+
if (_scanner.getToken() === 17) {
|
|
4047
|
+
if (options.allowEmptyContent) {
|
|
4048
|
+
return true;
|
|
4049
|
+
}
|
|
4050
|
+
handleError(4, [], []);
|
|
4051
|
+
return false;
|
|
4052
|
+
}
|
|
4053
|
+
if (!parseValue()) {
|
|
4054
|
+
handleError(4, [], []);
|
|
4055
|
+
return false;
|
|
4056
|
+
}
|
|
4057
|
+
if (_scanner.getToken() !== 17) {
|
|
4058
|
+
handleError(9, [], []);
|
|
4059
|
+
}
|
|
4060
|
+
return true;
|
|
4061
|
+
}
|
|
4062
|
+
|
|
4063
|
+
// node_modules/jsonc-parser/lib/esm/main.js
|
|
4064
|
+
var ScanError;
|
|
4065
|
+
(function(ScanError2) {
|
|
4066
|
+
ScanError2[ScanError2["None"] = 0] = "None";
|
|
4067
|
+
ScanError2[ScanError2["UnexpectedEndOfComment"] = 1] = "UnexpectedEndOfComment";
|
|
4068
|
+
ScanError2[ScanError2["UnexpectedEndOfString"] = 2] = "UnexpectedEndOfString";
|
|
4069
|
+
ScanError2[ScanError2["UnexpectedEndOfNumber"] = 3] = "UnexpectedEndOfNumber";
|
|
4070
|
+
ScanError2[ScanError2["InvalidUnicode"] = 4] = "InvalidUnicode";
|
|
4071
|
+
ScanError2[ScanError2["InvalidEscapeCharacter"] = 5] = "InvalidEscapeCharacter";
|
|
4072
|
+
ScanError2[ScanError2["InvalidCharacter"] = 6] = "InvalidCharacter";
|
|
4073
|
+
})(ScanError || (ScanError = {}));
|
|
4074
|
+
var SyntaxKind;
|
|
4075
|
+
(function(SyntaxKind2) {
|
|
4076
|
+
SyntaxKind2[SyntaxKind2["OpenBraceToken"] = 1] = "OpenBraceToken";
|
|
4077
|
+
SyntaxKind2[SyntaxKind2["CloseBraceToken"] = 2] = "CloseBraceToken";
|
|
4078
|
+
SyntaxKind2[SyntaxKind2["OpenBracketToken"] = 3] = "OpenBracketToken";
|
|
4079
|
+
SyntaxKind2[SyntaxKind2["CloseBracketToken"] = 4] = "CloseBracketToken";
|
|
4080
|
+
SyntaxKind2[SyntaxKind2["CommaToken"] = 5] = "CommaToken";
|
|
4081
|
+
SyntaxKind2[SyntaxKind2["ColonToken"] = 6] = "ColonToken";
|
|
4082
|
+
SyntaxKind2[SyntaxKind2["NullKeyword"] = 7] = "NullKeyword";
|
|
4083
|
+
SyntaxKind2[SyntaxKind2["TrueKeyword"] = 8] = "TrueKeyword";
|
|
4084
|
+
SyntaxKind2[SyntaxKind2["FalseKeyword"] = 9] = "FalseKeyword";
|
|
4085
|
+
SyntaxKind2[SyntaxKind2["StringLiteral"] = 10] = "StringLiteral";
|
|
4086
|
+
SyntaxKind2[SyntaxKind2["NumericLiteral"] = 11] = "NumericLiteral";
|
|
4087
|
+
SyntaxKind2[SyntaxKind2["LineCommentTrivia"] = 12] = "LineCommentTrivia";
|
|
4088
|
+
SyntaxKind2[SyntaxKind2["BlockCommentTrivia"] = 13] = "BlockCommentTrivia";
|
|
4089
|
+
SyntaxKind2[SyntaxKind2["LineBreakTrivia"] = 14] = "LineBreakTrivia";
|
|
4090
|
+
SyntaxKind2[SyntaxKind2["Trivia"] = 15] = "Trivia";
|
|
4091
|
+
SyntaxKind2[SyntaxKind2["Unknown"] = 16] = "Unknown";
|
|
4092
|
+
SyntaxKind2[SyntaxKind2["EOF"] = 17] = "EOF";
|
|
4093
|
+
})(SyntaxKind || (SyntaxKind = {}));
|
|
4094
|
+
var parse2 = parse;
|
|
4095
|
+
var ParseErrorCode;
|
|
4096
|
+
(function(ParseErrorCode2) {
|
|
4097
|
+
ParseErrorCode2[ParseErrorCode2["InvalidSymbol"] = 1] = "InvalidSymbol";
|
|
4098
|
+
ParseErrorCode2[ParseErrorCode2["InvalidNumberFormat"] = 2] = "InvalidNumberFormat";
|
|
4099
|
+
ParseErrorCode2[ParseErrorCode2["PropertyNameExpected"] = 3] = "PropertyNameExpected";
|
|
4100
|
+
ParseErrorCode2[ParseErrorCode2["ValueExpected"] = 4] = "ValueExpected";
|
|
4101
|
+
ParseErrorCode2[ParseErrorCode2["ColonExpected"] = 5] = "ColonExpected";
|
|
4102
|
+
ParseErrorCode2[ParseErrorCode2["CommaExpected"] = 6] = "CommaExpected";
|
|
4103
|
+
ParseErrorCode2[ParseErrorCode2["CloseBraceExpected"] = 7] = "CloseBraceExpected";
|
|
4104
|
+
ParseErrorCode2[ParseErrorCode2["CloseBracketExpected"] = 8] = "CloseBracketExpected";
|
|
4105
|
+
ParseErrorCode2[ParseErrorCode2["EndOfFileExpected"] = 9] = "EndOfFileExpected";
|
|
4106
|
+
ParseErrorCode2[ParseErrorCode2["InvalidCommentToken"] = 10] = "InvalidCommentToken";
|
|
4107
|
+
ParseErrorCode2[ParseErrorCode2["UnexpectedEndOfComment"] = 11] = "UnexpectedEndOfComment";
|
|
4108
|
+
ParseErrorCode2[ParseErrorCode2["UnexpectedEndOfString"] = 12] = "UnexpectedEndOfString";
|
|
4109
|
+
ParseErrorCode2[ParseErrorCode2["UnexpectedEndOfNumber"] = 13] = "UnexpectedEndOfNumber";
|
|
4110
|
+
ParseErrorCode2[ParseErrorCode2["InvalidUnicode"] = 14] = "InvalidUnicode";
|
|
4111
|
+
ParseErrorCode2[ParseErrorCode2["InvalidEscapeCharacter"] = 15] = "InvalidEscapeCharacter";
|
|
4112
|
+
ParseErrorCode2[ParseErrorCode2["InvalidCharacter"] = 16] = "InvalidCharacter";
|
|
4113
|
+
})(ParseErrorCode || (ParseErrorCode = {}));
|
|
4114
|
+
function printParseErrorCode(code) {
|
|
4115
|
+
switch (code) {
|
|
4116
|
+
case 1:
|
|
4117
|
+
return "InvalidSymbol";
|
|
4118
|
+
case 2:
|
|
4119
|
+
return "InvalidNumberFormat";
|
|
4120
|
+
case 3:
|
|
4121
|
+
return "PropertyNameExpected";
|
|
4122
|
+
case 4:
|
|
4123
|
+
return "ValueExpected";
|
|
4124
|
+
case 5:
|
|
4125
|
+
return "ColonExpected";
|
|
4126
|
+
case 6:
|
|
4127
|
+
return "CommaExpected";
|
|
4128
|
+
case 7:
|
|
4129
|
+
return "CloseBraceExpected";
|
|
4130
|
+
case 8:
|
|
4131
|
+
return "CloseBracketExpected";
|
|
4132
|
+
case 9:
|
|
4133
|
+
return "EndOfFileExpected";
|
|
4134
|
+
case 10:
|
|
4135
|
+
return "InvalidCommentToken";
|
|
4136
|
+
case 11:
|
|
4137
|
+
return "UnexpectedEndOfComment";
|
|
4138
|
+
case 12:
|
|
4139
|
+
return "UnexpectedEndOfString";
|
|
4140
|
+
case 13:
|
|
4141
|
+
return "UnexpectedEndOfNumber";
|
|
4142
|
+
case 14:
|
|
4143
|
+
return "InvalidUnicode";
|
|
4144
|
+
case 15:
|
|
4145
|
+
return "InvalidEscapeCharacter";
|
|
4146
|
+
case 16:
|
|
4147
|
+
return "InvalidCharacter";
|
|
4148
|
+
}
|
|
4149
|
+
return "<unknown ParseErrorCode>";
|
|
4150
|
+
}
|
|
4151
|
+
|
|
4152
|
+
// src/shared/jsonc-parser.ts
|
|
4153
|
+
function parseJsonc(content) {
|
|
4154
|
+
const errors = [];
|
|
4155
|
+
const result = parse2(content, errors, {
|
|
4156
|
+
allowTrailingComma: true,
|
|
4157
|
+
disallowComments: false
|
|
4158
|
+
});
|
|
4159
|
+
if (errors.length > 0) {
|
|
4160
|
+
const errorMessages = errors.map((e) => `${printParseErrorCode(e.error)} at offset ${e.offset}`).join(", ");
|
|
4161
|
+
throw new SyntaxError(`JSONC parse error: ${errorMessages}`);
|
|
4162
|
+
}
|
|
4163
|
+
return result;
|
|
4164
|
+
}
|
|
4165
|
+
function detectConfigFile(basePath) {
|
|
4166
|
+
const jsoncPath = `${basePath}.jsonc`;
|
|
4167
|
+
const jsonPath = `${basePath}.json`;
|
|
4168
|
+
if (existsSync4(jsoncPath)) {
|
|
4169
|
+
return { format: "jsonc", path: jsoncPath };
|
|
4170
|
+
}
|
|
4171
|
+
if (existsSync4(jsonPath)) {
|
|
4172
|
+
return { format: "json", path: jsonPath };
|
|
4173
|
+
}
|
|
4174
|
+
return { format: "none", path: jsonPath };
|
|
4175
|
+
}
|
|
3295
4176
|
// src/agents/utils.ts
|
|
3296
4177
|
var agentSources = {
|
|
3297
4178
|
Sisyphus: createSisyphusAgent,
|
|
@@ -3367,8 +4248,8 @@ function createBuiltinAgents(disabledAgents = [], agentOverrides = {}, directory
|
|
|
3367
4248
|
return result;
|
|
3368
4249
|
}
|
|
3369
4250
|
// src/hooks/todo-continuation-enforcer.ts
|
|
3370
|
-
import { existsSync as
|
|
3371
|
-
import { join as
|
|
4251
|
+
import { existsSync as existsSync6, readdirSync as readdirSync2 } from "fs";
|
|
4252
|
+
import { join as join8 } from "path";
|
|
3372
4253
|
|
|
3373
4254
|
// src/features/claude-code-session-state/state.ts
|
|
3374
4255
|
var subagentSessions = new Set;
|
|
@@ -3380,14 +4261,14 @@ function getMainSessionID() {
|
|
|
3380
4261
|
return mainSessionID;
|
|
3381
4262
|
}
|
|
3382
4263
|
// src/features/hook-message-injector/injector.ts
|
|
3383
|
-
import { existsSync as
|
|
3384
|
-
import { join as
|
|
4264
|
+
import { existsSync as existsSync5, mkdirSync, readFileSync as readFileSync3, readdirSync, writeFileSync } from "fs";
|
|
4265
|
+
import { join as join7 } from "path";
|
|
3385
4266
|
|
|
3386
4267
|
// src/features/hook-message-injector/constants.ts
|
|
3387
|
-
import { join as
|
|
4268
|
+
import { join as join6 } from "path";
|
|
3388
4269
|
var OPENCODE_STORAGE = getOpenCodeStorageDir();
|
|
3389
|
-
var MESSAGE_STORAGE =
|
|
3390
|
-
var PART_STORAGE =
|
|
4270
|
+
var MESSAGE_STORAGE = join6(OPENCODE_STORAGE, "message");
|
|
4271
|
+
var PART_STORAGE = join6(OPENCODE_STORAGE, "part");
|
|
3391
4272
|
|
|
3392
4273
|
// src/features/hook-message-injector/injector.ts
|
|
3393
4274
|
function findNearestMessageWithFields(messageDir) {
|
|
@@ -3395,7 +4276,7 @@ function findNearestMessageWithFields(messageDir) {
|
|
|
3395
4276
|
const files = readdirSync(messageDir).filter((f) => f.endsWith(".json")).sort().reverse();
|
|
3396
4277
|
for (const file of files) {
|
|
3397
4278
|
try {
|
|
3398
|
-
const content =
|
|
4279
|
+
const content = readFileSync3(join7(messageDir, file), "utf-8");
|
|
3399
4280
|
const msg = JSON.parse(content);
|
|
3400
4281
|
if (msg.agent && msg.model?.providerID && msg.model?.modelID) {
|
|
3401
4282
|
return msg;
|
|
@@ -3420,16 +4301,16 @@ function generatePartId() {
|
|
|
3420
4301
|
return `prt_${timestamp}${random}`;
|
|
3421
4302
|
}
|
|
3422
4303
|
function getOrCreateMessageDir(sessionID) {
|
|
3423
|
-
if (!
|
|
4304
|
+
if (!existsSync5(MESSAGE_STORAGE)) {
|
|
3424
4305
|
mkdirSync(MESSAGE_STORAGE, { recursive: true });
|
|
3425
4306
|
}
|
|
3426
|
-
const directPath =
|
|
3427
|
-
if (
|
|
4307
|
+
const directPath = join7(MESSAGE_STORAGE, sessionID);
|
|
4308
|
+
if (existsSync5(directPath)) {
|
|
3428
4309
|
return directPath;
|
|
3429
4310
|
}
|
|
3430
4311
|
for (const dir of readdirSync(MESSAGE_STORAGE)) {
|
|
3431
|
-
const sessionPath =
|
|
3432
|
-
if (
|
|
4312
|
+
const sessionPath = join7(MESSAGE_STORAGE, dir, sessionID);
|
|
4313
|
+
if (existsSync5(sessionPath)) {
|
|
3433
4314
|
return sessionPath;
|
|
3434
4315
|
}
|
|
3435
4316
|
}
|
|
@@ -3482,12 +4363,12 @@ function injectHookMessage(sessionID, hookContent, originalMessage) {
|
|
|
3482
4363
|
sessionID
|
|
3483
4364
|
};
|
|
3484
4365
|
try {
|
|
3485
|
-
writeFileSync(
|
|
3486
|
-
const partDir =
|
|
3487
|
-
if (!
|
|
4366
|
+
writeFileSync(join7(messageDir, `${messageID}.json`), JSON.stringify(messageMeta, null, 2));
|
|
4367
|
+
const partDir = join7(PART_STORAGE, messageID);
|
|
4368
|
+
if (!existsSync5(partDir)) {
|
|
3488
4369
|
mkdirSync(partDir, { recursive: true });
|
|
3489
4370
|
}
|
|
3490
|
-
writeFileSync(
|
|
4371
|
+
writeFileSync(join7(partDir, `${partID}.json`), JSON.stringify(textPart, null, 2));
|
|
3491
4372
|
return true;
|
|
3492
4373
|
} catch {
|
|
3493
4374
|
return false;
|
|
@@ -3520,14 +4401,14 @@ Incomplete tasks remain in your todo list. Continue working on the next pending
|
|
|
3520
4401
|
- Mark each task complete when finished
|
|
3521
4402
|
- Do not stop until all tasks are done`;
|
|
3522
4403
|
function getMessageDir(sessionID) {
|
|
3523
|
-
if (!
|
|
4404
|
+
if (!existsSync6(MESSAGE_STORAGE))
|
|
3524
4405
|
return null;
|
|
3525
|
-
const directPath =
|
|
3526
|
-
if (
|
|
4406
|
+
const directPath = join8(MESSAGE_STORAGE, sessionID);
|
|
4407
|
+
if (existsSync6(directPath))
|
|
3527
4408
|
return directPath;
|
|
3528
4409
|
for (const dir of readdirSync2(MESSAGE_STORAGE)) {
|
|
3529
|
-
const sessionPath =
|
|
3530
|
-
if (
|
|
4410
|
+
const sessionPath = join8(MESSAGE_STORAGE, dir, sessionID);
|
|
4411
|
+
if (existsSync6(sessionPath))
|
|
3531
4412
|
return sessionPath;
|
|
3532
4413
|
}
|
|
3533
4414
|
return null;
|
|
@@ -3883,6 +4764,130 @@ ${CONTEXT_REMINDER}
|
|
|
3883
4764
|
}
|
|
3884
4765
|
// src/hooks/session-notification.ts
|
|
3885
4766
|
import { platform } from "os";
|
|
4767
|
+
|
|
4768
|
+
// src/hooks/session-notification-utils.ts
|
|
4769
|
+
var {spawn: spawn2 } = globalThis.Bun;
|
|
4770
|
+
var notifySendPath = null;
|
|
4771
|
+
var notifySendPromise = null;
|
|
4772
|
+
var osascriptPath = null;
|
|
4773
|
+
var osascriptPromise = null;
|
|
4774
|
+
var powershellPath = null;
|
|
4775
|
+
var powershellPromise = null;
|
|
4776
|
+
var afplayPath = null;
|
|
4777
|
+
var afplayPromise = null;
|
|
4778
|
+
var paplayPath = null;
|
|
4779
|
+
var paplayPromise = null;
|
|
4780
|
+
var aplayPath = null;
|
|
4781
|
+
var aplayPromise = null;
|
|
4782
|
+
async function findCommand(commandName) {
|
|
4783
|
+
const isWindows = process.platform === "win32";
|
|
4784
|
+
const cmd = isWindows ? "where" : "which";
|
|
4785
|
+
try {
|
|
4786
|
+
const proc = spawn2([cmd, commandName], {
|
|
4787
|
+
stdout: "pipe",
|
|
4788
|
+
stderr: "pipe"
|
|
4789
|
+
});
|
|
4790
|
+
const exitCode = await proc.exited;
|
|
4791
|
+
if (exitCode !== 0) {
|
|
4792
|
+
return null;
|
|
4793
|
+
}
|
|
4794
|
+
const stdout = await new Response(proc.stdout).text();
|
|
4795
|
+
const path4 = stdout.trim().split(`
|
|
4796
|
+
`)[0];
|
|
4797
|
+
if (!path4) {
|
|
4798
|
+
return null;
|
|
4799
|
+
}
|
|
4800
|
+
return path4;
|
|
4801
|
+
} catch {
|
|
4802
|
+
return null;
|
|
4803
|
+
}
|
|
4804
|
+
}
|
|
4805
|
+
async function getNotifySendPath() {
|
|
4806
|
+
if (notifySendPath !== null)
|
|
4807
|
+
return notifySendPath;
|
|
4808
|
+
if (notifySendPromise)
|
|
4809
|
+
return notifySendPromise;
|
|
4810
|
+
notifySendPromise = (async () => {
|
|
4811
|
+
const path4 = await findCommand("notify-send");
|
|
4812
|
+
notifySendPath = path4;
|
|
4813
|
+
return path4;
|
|
4814
|
+
})();
|
|
4815
|
+
return notifySendPromise;
|
|
4816
|
+
}
|
|
4817
|
+
async function getOsascriptPath() {
|
|
4818
|
+
if (osascriptPath !== null)
|
|
4819
|
+
return osascriptPath;
|
|
4820
|
+
if (osascriptPromise)
|
|
4821
|
+
return osascriptPromise;
|
|
4822
|
+
osascriptPromise = (async () => {
|
|
4823
|
+
const path4 = await findCommand("osascript");
|
|
4824
|
+
osascriptPath = path4;
|
|
4825
|
+
return path4;
|
|
4826
|
+
})();
|
|
4827
|
+
return osascriptPromise;
|
|
4828
|
+
}
|
|
4829
|
+
async function getPowershellPath() {
|
|
4830
|
+
if (powershellPath !== null)
|
|
4831
|
+
return powershellPath;
|
|
4832
|
+
if (powershellPromise)
|
|
4833
|
+
return powershellPromise;
|
|
4834
|
+
powershellPromise = (async () => {
|
|
4835
|
+
const path4 = await findCommand("powershell");
|
|
4836
|
+
powershellPath = path4;
|
|
4837
|
+
return path4;
|
|
4838
|
+
})();
|
|
4839
|
+
return powershellPromise;
|
|
4840
|
+
}
|
|
4841
|
+
async function getAfplayPath() {
|
|
4842
|
+
if (afplayPath !== null)
|
|
4843
|
+
return afplayPath;
|
|
4844
|
+
if (afplayPromise)
|
|
4845
|
+
return afplayPromise;
|
|
4846
|
+
afplayPromise = (async () => {
|
|
4847
|
+
const path4 = await findCommand("afplay");
|
|
4848
|
+
afplayPath = path4;
|
|
4849
|
+
return path4;
|
|
4850
|
+
})();
|
|
4851
|
+
return afplayPromise;
|
|
4852
|
+
}
|
|
4853
|
+
async function getPaplayPath() {
|
|
4854
|
+
if (paplayPath !== null)
|
|
4855
|
+
return paplayPath;
|
|
4856
|
+
if (paplayPromise)
|
|
4857
|
+
return paplayPromise;
|
|
4858
|
+
paplayPromise = (async () => {
|
|
4859
|
+
const path4 = await findCommand("paplay");
|
|
4860
|
+
paplayPath = path4;
|
|
4861
|
+
return path4;
|
|
4862
|
+
})();
|
|
4863
|
+
return paplayPromise;
|
|
4864
|
+
}
|
|
4865
|
+
async function getAplayPath() {
|
|
4866
|
+
if (aplayPath !== null)
|
|
4867
|
+
return aplayPath;
|
|
4868
|
+
if (aplayPromise)
|
|
4869
|
+
return aplayPromise;
|
|
4870
|
+
aplayPromise = (async () => {
|
|
4871
|
+
const path4 = await findCommand("aplay");
|
|
4872
|
+
aplayPath = path4;
|
|
4873
|
+
return path4;
|
|
4874
|
+
})();
|
|
4875
|
+
return aplayPromise;
|
|
4876
|
+
}
|
|
4877
|
+
function startBackgroundCheck(platform) {
|
|
4878
|
+
if (platform === "darwin") {
|
|
4879
|
+
getOsascriptPath().catch(() => {});
|
|
4880
|
+
getAfplayPath().catch(() => {});
|
|
4881
|
+
} else if (platform === "linux") {
|
|
4882
|
+
getNotifySendPath().catch(() => {});
|
|
4883
|
+
getPaplayPath().catch(() => {});
|
|
4884
|
+
getAplayPath().catch(() => {});
|
|
4885
|
+
} else if (platform === "win32") {
|
|
4886
|
+
getPowershellPath().catch(() => {});
|
|
4887
|
+
}
|
|
4888
|
+
}
|
|
4889
|
+
|
|
4890
|
+
// src/hooks/session-notification.ts
|
|
3886
4891
|
function detectPlatform() {
|
|
3887
4892
|
const p = platform();
|
|
3888
4893
|
if (p === "darwin" || p === "linux" || p === "win32")
|
|
@@ -3904,15 +4909,25 @@ function getDefaultSoundPath(p) {
|
|
|
3904
4909
|
async function sendNotification(ctx, p, title, message) {
|
|
3905
4910
|
switch (p) {
|
|
3906
4911
|
case "darwin": {
|
|
4912
|
+
const osascriptPath2 = await getOsascriptPath();
|
|
4913
|
+
if (!osascriptPath2)
|
|
4914
|
+
return;
|
|
3907
4915
|
const esTitle = title.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
3908
4916
|
const esMessage = message.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
3909
|
-
await ctx
|
|
4917
|
+
await ctx.$`${osascriptPath2} -e ${'display notification "' + esMessage + '" with title "' + esTitle + '"'}`.catch(() => {});
|
|
3910
4918
|
break;
|
|
3911
4919
|
}
|
|
3912
|
-
case "linux":
|
|
3913
|
-
|
|
4920
|
+
case "linux": {
|
|
4921
|
+
const notifySendPath2 = await getNotifySendPath();
|
|
4922
|
+
if (!notifySendPath2)
|
|
4923
|
+
return;
|
|
4924
|
+
await ctx.$`${notifySendPath2} ${title} ${message} 2>/dev/null`.catch(() => {});
|
|
3914
4925
|
break;
|
|
4926
|
+
}
|
|
3915
4927
|
case "win32": {
|
|
4928
|
+
const powershellPath2 = await getPowershellPath();
|
|
4929
|
+
if (!powershellPath2)
|
|
4930
|
+
return;
|
|
3916
4931
|
const psTitle = title.replace(/'/g, "''");
|
|
3917
4932
|
const psMessage = message.replace(/'/g, "''");
|
|
3918
4933
|
const toastScript = `
|
|
@@ -3927,24 +4942,39 @@ $Toast = [Windows.UI.Notifications.ToastNotification]::new($SerializedXml)
|
|
|
3927
4942
|
$Notifier = [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier('OpenCode')
|
|
3928
4943
|
$Notifier.Show($Toast)
|
|
3929
4944
|
`.trim().replace(/\n/g, "; ");
|
|
3930
|
-
await ctx
|
|
4945
|
+
await ctx.$`${powershellPath2} -Command ${toastScript}`.catch(() => {});
|
|
3931
4946
|
break;
|
|
3932
4947
|
}
|
|
3933
4948
|
}
|
|
3934
4949
|
}
|
|
3935
4950
|
async function playSound(ctx, p, soundPath) {
|
|
3936
4951
|
switch (p) {
|
|
3937
|
-
case "darwin":
|
|
3938
|
-
|
|
4952
|
+
case "darwin": {
|
|
4953
|
+
const afplayPath2 = await getAfplayPath();
|
|
4954
|
+
if (!afplayPath2)
|
|
4955
|
+
return;
|
|
4956
|
+
ctx.$`${afplayPath2} ${soundPath}`.catch(() => {});
|
|
3939
4957
|
break;
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
4958
|
+
}
|
|
4959
|
+
case "linux": {
|
|
4960
|
+
const paplayPath2 = await getPaplayPath();
|
|
4961
|
+
if (paplayPath2) {
|
|
4962
|
+
ctx.$`${paplayPath2} ${soundPath} 2>/dev/null`.catch(() => {});
|
|
4963
|
+
} else {
|
|
4964
|
+
const aplayPath2 = await getAplayPath();
|
|
4965
|
+
if (aplayPath2) {
|
|
4966
|
+
ctx.$`${aplayPath2} ${soundPath} 2>/dev/null`.catch(() => {});
|
|
4967
|
+
}
|
|
4968
|
+
}
|
|
3944
4969
|
break;
|
|
3945
|
-
|
|
3946
|
-
|
|
4970
|
+
}
|
|
4971
|
+
case "win32": {
|
|
4972
|
+
const powershellPath2 = await getPowershellPath();
|
|
4973
|
+
if (!powershellPath2)
|
|
4974
|
+
return;
|
|
4975
|
+
ctx.$`${powershellPath2} -Command ${"(New-Object Media.SoundPlayer '" + soundPath + "').PlaySync()"}`.catch(() => {});
|
|
3947
4976
|
break;
|
|
4977
|
+
}
|
|
3948
4978
|
}
|
|
3949
4979
|
}
|
|
3950
4980
|
async function hasIncompleteTodos(ctx, sessionID) {
|
|
@@ -3961,6 +4991,7 @@ async function hasIncompleteTodos(ctx, sessionID) {
|
|
|
3961
4991
|
function createSessionNotification(ctx, config = {}) {
|
|
3962
4992
|
const currentPlatform = detectPlatform();
|
|
3963
4993
|
const defaultSoundPath = getDefaultSoundPath(currentPlatform);
|
|
4994
|
+
startBackgroundCheck(currentPlatform);
|
|
3964
4995
|
const mergedConfig = {
|
|
3965
4996
|
title: "OpenCode",
|
|
3966
4997
|
message: "Agent is ready for input",
|
|
@@ -4118,11 +5149,11 @@ function createSessionNotification(ctx, config = {}) {
|
|
|
4118
5149
|
};
|
|
4119
5150
|
}
|
|
4120
5151
|
// src/hooks/session-recovery/storage.ts
|
|
4121
|
-
import { existsSync as
|
|
4122
|
-
import { join as
|
|
5152
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync2, readdirSync as readdirSync3, readFileSync as readFileSync4, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
5153
|
+
import { join as join10 } from "path";
|
|
4123
5154
|
|
|
4124
5155
|
// src/hooks/session-recovery/constants.ts
|
|
4125
|
-
import { join as
|
|
5156
|
+
import { join as join9 } from "path";
|
|
4126
5157
|
|
|
4127
5158
|
// node_modules/xdg-basedir/index.js
|
|
4128
5159
|
import os4 from "os";
|
|
@@ -4144,9 +5175,9 @@ if (xdgConfig) {
|
|
|
4144
5175
|
}
|
|
4145
5176
|
|
|
4146
5177
|
// src/hooks/session-recovery/constants.ts
|
|
4147
|
-
var OPENCODE_STORAGE2 =
|
|
4148
|
-
var MESSAGE_STORAGE2 =
|
|
4149
|
-
var PART_STORAGE2 =
|
|
5178
|
+
var OPENCODE_STORAGE2 = join9(xdgData ?? "", "opencode", "storage");
|
|
5179
|
+
var MESSAGE_STORAGE2 = join9(OPENCODE_STORAGE2, "message");
|
|
5180
|
+
var PART_STORAGE2 = join9(OPENCODE_STORAGE2, "part");
|
|
4150
5181
|
var THINKING_TYPES = new Set(["thinking", "redacted_thinking", "reasoning"]);
|
|
4151
5182
|
var META_TYPES = new Set(["step-start", "step-finish"]);
|
|
4152
5183
|
var CONTENT_TYPES = new Set(["text", "tool", "tool_use", "tool_result"]);
|
|
@@ -4158,15 +5189,15 @@ function generatePartId2() {
|
|
|
4158
5189
|
return `prt_${timestamp}${random}`;
|
|
4159
5190
|
}
|
|
4160
5191
|
function getMessageDir2(sessionID) {
|
|
4161
|
-
if (!
|
|
5192
|
+
if (!existsSync7(MESSAGE_STORAGE2))
|
|
4162
5193
|
return "";
|
|
4163
|
-
const directPath =
|
|
4164
|
-
if (
|
|
5194
|
+
const directPath = join10(MESSAGE_STORAGE2, sessionID);
|
|
5195
|
+
if (existsSync7(directPath)) {
|
|
4165
5196
|
return directPath;
|
|
4166
5197
|
}
|
|
4167
5198
|
for (const dir of readdirSync3(MESSAGE_STORAGE2)) {
|
|
4168
|
-
const sessionPath =
|
|
4169
|
-
if (
|
|
5199
|
+
const sessionPath = join10(MESSAGE_STORAGE2, dir, sessionID);
|
|
5200
|
+
if (existsSync7(sessionPath)) {
|
|
4170
5201
|
return sessionPath;
|
|
4171
5202
|
}
|
|
4172
5203
|
}
|
|
@@ -4174,14 +5205,14 @@ function getMessageDir2(sessionID) {
|
|
|
4174
5205
|
}
|
|
4175
5206
|
function readMessages(sessionID) {
|
|
4176
5207
|
const messageDir = getMessageDir2(sessionID);
|
|
4177
|
-
if (!messageDir || !
|
|
5208
|
+
if (!messageDir || !existsSync7(messageDir))
|
|
4178
5209
|
return [];
|
|
4179
5210
|
const messages = [];
|
|
4180
5211
|
for (const file of readdirSync3(messageDir)) {
|
|
4181
5212
|
if (!file.endsWith(".json"))
|
|
4182
5213
|
continue;
|
|
4183
5214
|
try {
|
|
4184
|
-
const content =
|
|
5215
|
+
const content = readFileSync4(join10(messageDir, file), "utf-8");
|
|
4185
5216
|
messages.push(JSON.parse(content));
|
|
4186
5217
|
} catch {
|
|
4187
5218
|
continue;
|
|
@@ -4196,15 +5227,15 @@ function readMessages(sessionID) {
|
|
|
4196
5227
|
});
|
|
4197
5228
|
}
|
|
4198
5229
|
function readParts(messageID) {
|
|
4199
|
-
const partDir =
|
|
4200
|
-
if (!
|
|
5230
|
+
const partDir = join10(PART_STORAGE2, messageID);
|
|
5231
|
+
if (!existsSync7(partDir))
|
|
4201
5232
|
return [];
|
|
4202
5233
|
const parts = [];
|
|
4203
5234
|
for (const file of readdirSync3(partDir)) {
|
|
4204
5235
|
if (!file.endsWith(".json"))
|
|
4205
5236
|
continue;
|
|
4206
5237
|
try {
|
|
4207
|
-
const content =
|
|
5238
|
+
const content = readFileSync4(join10(partDir, file), "utf-8");
|
|
4208
5239
|
parts.push(JSON.parse(content));
|
|
4209
5240
|
} catch {
|
|
4210
5241
|
continue;
|
|
@@ -4234,8 +5265,8 @@ function messageHasContent(messageID) {
|
|
|
4234
5265
|
return parts.some(hasContent);
|
|
4235
5266
|
}
|
|
4236
5267
|
function injectTextPart(sessionID, messageID, text) {
|
|
4237
|
-
const partDir =
|
|
4238
|
-
if (!
|
|
5268
|
+
const partDir = join10(PART_STORAGE2, messageID);
|
|
5269
|
+
if (!existsSync7(partDir)) {
|
|
4239
5270
|
mkdirSync2(partDir, { recursive: true });
|
|
4240
5271
|
}
|
|
4241
5272
|
const partId = generatePartId2();
|
|
@@ -4248,7 +5279,7 @@ function injectTextPart(sessionID, messageID, text) {
|
|
|
4248
5279
|
synthetic: true
|
|
4249
5280
|
};
|
|
4250
5281
|
try {
|
|
4251
|
-
writeFileSync2(
|
|
5282
|
+
writeFileSync2(join10(partDir, `${partId}.json`), JSON.stringify(part, null, 2));
|
|
4252
5283
|
return true;
|
|
4253
5284
|
} catch {
|
|
4254
5285
|
return false;
|
|
@@ -4310,38 +5341,62 @@ function findMessagesWithOrphanThinking(sessionID) {
|
|
|
4310
5341
|
}
|
|
4311
5342
|
return result;
|
|
4312
5343
|
}
|
|
5344
|
+
function findLastThinkingContent(sessionID, beforeMessageID) {
|
|
5345
|
+
const messages = readMessages(sessionID);
|
|
5346
|
+
const currentIndex = messages.findIndex((m) => m.id === beforeMessageID);
|
|
5347
|
+
if (currentIndex === -1)
|
|
5348
|
+
return "";
|
|
5349
|
+
for (let i = currentIndex - 1;i >= 0; i--) {
|
|
5350
|
+
const msg = messages[i];
|
|
5351
|
+
if (msg.role !== "assistant")
|
|
5352
|
+
continue;
|
|
5353
|
+
const parts = readParts(msg.id);
|
|
5354
|
+
for (const part of parts) {
|
|
5355
|
+
if (THINKING_TYPES.has(part.type)) {
|
|
5356
|
+
const thinking = part.thinking;
|
|
5357
|
+
const reasoning = part.text;
|
|
5358
|
+
const content = thinking || reasoning;
|
|
5359
|
+
if (content && content.trim().length > 0) {
|
|
5360
|
+
return content;
|
|
5361
|
+
}
|
|
5362
|
+
}
|
|
5363
|
+
}
|
|
5364
|
+
}
|
|
5365
|
+
return "";
|
|
5366
|
+
}
|
|
4313
5367
|
function prependThinkingPart(sessionID, messageID) {
|
|
4314
|
-
const partDir =
|
|
4315
|
-
if (!
|
|
5368
|
+
const partDir = join10(PART_STORAGE2, messageID);
|
|
5369
|
+
if (!existsSync7(partDir)) {
|
|
4316
5370
|
mkdirSync2(partDir, { recursive: true });
|
|
4317
5371
|
}
|
|
5372
|
+
const previousThinking = findLastThinkingContent(sessionID, messageID);
|
|
4318
5373
|
const partId = `prt_0000000000_thinking`;
|
|
4319
5374
|
const part = {
|
|
4320
5375
|
id: partId,
|
|
4321
5376
|
sessionID,
|
|
4322
5377
|
messageID,
|
|
4323
5378
|
type: "thinking",
|
|
4324
|
-
thinking: "",
|
|
5379
|
+
thinking: previousThinking || "[Continuing from previous reasoning]",
|
|
4325
5380
|
synthetic: true
|
|
4326
5381
|
};
|
|
4327
5382
|
try {
|
|
4328
|
-
writeFileSync2(
|
|
5383
|
+
writeFileSync2(join10(partDir, `${partId}.json`), JSON.stringify(part, null, 2));
|
|
4329
5384
|
return true;
|
|
4330
5385
|
} catch {
|
|
4331
5386
|
return false;
|
|
4332
5387
|
}
|
|
4333
5388
|
}
|
|
4334
5389
|
function stripThinkingParts(messageID) {
|
|
4335
|
-
const partDir =
|
|
4336
|
-
if (!
|
|
5390
|
+
const partDir = join10(PART_STORAGE2, messageID);
|
|
5391
|
+
if (!existsSync7(partDir))
|
|
4337
5392
|
return false;
|
|
4338
5393
|
let anyRemoved = false;
|
|
4339
5394
|
for (const file of readdirSync3(partDir)) {
|
|
4340
5395
|
if (!file.endsWith(".json"))
|
|
4341
5396
|
continue;
|
|
4342
5397
|
try {
|
|
4343
|
-
const filePath =
|
|
4344
|
-
const content =
|
|
5398
|
+
const filePath = join10(partDir, file);
|
|
5399
|
+
const content = readFileSync4(filePath, "utf-8");
|
|
4345
5400
|
const part = JSON.parse(content);
|
|
4346
5401
|
if (THINKING_TYPES.has(part.type)) {
|
|
4347
5402
|
unlinkSync(filePath);
|
|
@@ -4354,16 +5409,16 @@ function stripThinkingParts(messageID) {
|
|
|
4354
5409
|
return anyRemoved;
|
|
4355
5410
|
}
|
|
4356
5411
|
function replaceEmptyTextParts(messageID, replacementText) {
|
|
4357
|
-
const partDir =
|
|
4358
|
-
if (!
|
|
5412
|
+
const partDir = join10(PART_STORAGE2, messageID);
|
|
5413
|
+
if (!existsSync7(partDir))
|
|
4359
5414
|
return false;
|
|
4360
5415
|
let anyReplaced = false;
|
|
4361
5416
|
for (const file of readdirSync3(partDir)) {
|
|
4362
5417
|
if (!file.endsWith(".json"))
|
|
4363
5418
|
continue;
|
|
4364
5419
|
try {
|
|
4365
|
-
const filePath =
|
|
4366
|
-
const content =
|
|
5420
|
+
const filePath = join10(partDir, file);
|
|
5421
|
+
const content = readFileSync4(filePath, "utf-8");
|
|
4367
5422
|
const part = JSON.parse(content);
|
|
4368
5423
|
if (part.type === "text") {
|
|
4369
5424
|
const textPart = part;
|
|
@@ -4637,21 +5692,21 @@ function createSessionRecoveryHook(ctx, options) {
|
|
|
4637
5692
|
};
|
|
4638
5693
|
}
|
|
4639
5694
|
// src/hooks/comment-checker/cli.ts
|
|
4640
|
-
var {spawn:
|
|
5695
|
+
var {spawn: spawn4 } = globalThis.Bun;
|
|
4641
5696
|
import { createRequire as createRequire2 } from "module";
|
|
4642
|
-
import { dirname, join as
|
|
4643
|
-
import { existsSync as
|
|
5697
|
+
import { dirname, join as join12 } from "path";
|
|
5698
|
+
import { existsSync as existsSync9 } from "fs";
|
|
4644
5699
|
import * as fs3 from "fs";
|
|
4645
5700
|
import { tmpdir as tmpdir3 } from "os";
|
|
4646
5701
|
|
|
4647
5702
|
// src/hooks/comment-checker/downloader.ts
|
|
4648
|
-
var {spawn:
|
|
4649
|
-
import { existsSync as
|
|
4650
|
-
import { join as
|
|
4651
|
-
import { homedir as
|
|
5703
|
+
var {spawn: spawn3 } = globalThis.Bun;
|
|
5704
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync3, chmodSync, unlinkSync as unlinkSync2, appendFileSync as appendFileSync2 } from "fs";
|
|
5705
|
+
import { join as join11 } from "path";
|
|
5706
|
+
import { homedir as homedir5, tmpdir as tmpdir2 } from "os";
|
|
4652
5707
|
import { createRequire } from "module";
|
|
4653
5708
|
var DEBUG = process.env.COMMENT_CHECKER_DEBUG === "1";
|
|
4654
|
-
var DEBUG_FILE =
|
|
5709
|
+
var DEBUG_FILE = join11(tmpdir2(), "comment-checker-debug.log");
|
|
4655
5710
|
function debugLog(...args) {
|
|
4656
5711
|
if (DEBUG) {
|
|
4657
5712
|
const msg = `[${new Date().toISOString()}] [comment-checker:downloader] ${args.map((a) => typeof a === "object" ? JSON.stringify(a, null, 2) : String(a)).join(" ")}
|
|
@@ -4669,15 +5724,15 @@ var PLATFORM_MAP = {
|
|
|
4669
5724
|
};
|
|
4670
5725
|
function getCacheDir() {
|
|
4671
5726
|
const xdgCache2 = process.env.XDG_CACHE_HOME;
|
|
4672
|
-
const base = xdgCache2 ||
|
|
4673
|
-
return
|
|
5727
|
+
const base = xdgCache2 || join11(homedir5(), ".cache");
|
|
5728
|
+
return join11(base, "oh-my-opencode", "bin");
|
|
4674
5729
|
}
|
|
4675
5730
|
function getBinaryName() {
|
|
4676
5731
|
return process.platform === "win32" ? "comment-checker.exe" : "comment-checker";
|
|
4677
5732
|
}
|
|
4678
5733
|
function getCachedBinaryPath() {
|
|
4679
|
-
const binaryPath =
|
|
4680
|
-
return
|
|
5734
|
+
const binaryPath = join11(getCacheDir(), getBinaryName());
|
|
5735
|
+
return existsSync8(binaryPath) ? binaryPath : null;
|
|
4681
5736
|
}
|
|
4682
5737
|
function getPackageVersion() {
|
|
4683
5738
|
try {
|
|
@@ -4690,7 +5745,7 @@ function getPackageVersion() {
|
|
|
4690
5745
|
}
|
|
4691
5746
|
async function extractTarGz(archivePath, destDir) {
|
|
4692
5747
|
debugLog("Extracting tar.gz:", archivePath, "to", destDir);
|
|
4693
|
-
const proc =
|
|
5748
|
+
const proc = spawn3(["tar", "-xzf", archivePath, "-C", destDir], {
|
|
4694
5749
|
stdout: "pipe",
|
|
4695
5750
|
stderr: "pipe"
|
|
4696
5751
|
});
|
|
@@ -4702,10 +5757,10 @@ async function extractTarGz(archivePath, destDir) {
|
|
|
4702
5757
|
}
|
|
4703
5758
|
async function extractZip(archivePath, destDir) {
|
|
4704
5759
|
debugLog("Extracting zip:", archivePath, "to", destDir);
|
|
4705
|
-
const proc = process.platform === "win32" ?
|
|
5760
|
+
const proc = process.platform === "win32" ? spawn3(["powershell", "-command", `Expand-Archive -Path '${archivePath}' -DestinationPath '${destDir}' -Force`], {
|
|
4706
5761
|
stdout: "pipe",
|
|
4707
5762
|
stderr: "pipe"
|
|
4708
|
-
}) :
|
|
5763
|
+
}) : spawn3(["unzip", "-o", archivePath, "-d", destDir], {
|
|
4709
5764
|
stdout: "pipe",
|
|
4710
5765
|
stderr: "pipe"
|
|
4711
5766
|
});
|
|
@@ -4724,8 +5779,8 @@ async function downloadCommentChecker() {
|
|
|
4724
5779
|
}
|
|
4725
5780
|
const cacheDir = getCacheDir();
|
|
4726
5781
|
const binaryName = getBinaryName();
|
|
4727
|
-
const binaryPath =
|
|
4728
|
-
if (
|
|
5782
|
+
const binaryPath = join11(cacheDir, binaryName);
|
|
5783
|
+
if (existsSync8(binaryPath)) {
|
|
4729
5784
|
debugLog("Binary already cached at:", binaryPath);
|
|
4730
5785
|
return binaryPath;
|
|
4731
5786
|
}
|
|
@@ -4736,14 +5791,14 @@ async function downloadCommentChecker() {
|
|
|
4736
5791
|
debugLog(`Downloading from: ${downloadUrl}`);
|
|
4737
5792
|
console.log(`[oh-my-opencode] Downloading comment-checker binary...`);
|
|
4738
5793
|
try {
|
|
4739
|
-
if (!
|
|
5794
|
+
if (!existsSync8(cacheDir)) {
|
|
4740
5795
|
mkdirSync3(cacheDir, { recursive: true });
|
|
4741
5796
|
}
|
|
4742
5797
|
const response = await fetch(downloadUrl, { redirect: "follow" });
|
|
4743
5798
|
if (!response.ok) {
|
|
4744
5799
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
4745
5800
|
}
|
|
4746
|
-
const archivePath =
|
|
5801
|
+
const archivePath = join11(cacheDir, assetName);
|
|
4747
5802
|
const arrayBuffer = await response.arrayBuffer();
|
|
4748
5803
|
await Bun.write(archivePath, arrayBuffer);
|
|
4749
5804
|
debugLog(`Downloaded archive to: ${archivePath}`);
|
|
@@ -4752,10 +5807,10 @@ async function downloadCommentChecker() {
|
|
|
4752
5807
|
} else {
|
|
4753
5808
|
await extractZip(archivePath, cacheDir);
|
|
4754
5809
|
}
|
|
4755
|
-
if (
|
|
5810
|
+
if (existsSync8(archivePath)) {
|
|
4756
5811
|
unlinkSync2(archivePath);
|
|
4757
5812
|
}
|
|
4758
|
-
if (process.platform !== "win32" &&
|
|
5813
|
+
if (process.platform !== "win32" && existsSync8(binaryPath)) {
|
|
4759
5814
|
chmodSync(binaryPath, 493);
|
|
4760
5815
|
}
|
|
4761
5816
|
debugLog(`Successfully downloaded binary to: ${binaryPath}`);
|
|
@@ -4779,7 +5834,7 @@ async function ensureCommentCheckerBinary() {
|
|
|
4779
5834
|
|
|
4780
5835
|
// src/hooks/comment-checker/cli.ts
|
|
4781
5836
|
var DEBUG2 = process.env.COMMENT_CHECKER_DEBUG === "1";
|
|
4782
|
-
var DEBUG_FILE2 =
|
|
5837
|
+
var DEBUG_FILE2 = join12(tmpdir3(), "comment-checker-debug.log");
|
|
4783
5838
|
function debugLog2(...args) {
|
|
4784
5839
|
if (DEBUG2) {
|
|
4785
5840
|
const msg = `[${new Date().toISOString()}] [comment-checker:cli] ${args.map((a) => typeof a === "object" ? JSON.stringify(a, null, 2) : String(a)).join(" ")}
|
|
@@ -4796,8 +5851,8 @@ function findCommentCheckerPathSync() {
|
|
|
4796
5851
|
const require2 = createRequire2(import.meta.url);
|
|
4797
5852
|
const cliPkgPath = require2.resolve("@code-yeongyu/comment-checker/package.json");
|
|
4798
5853
|
const cliDir = dirname(cliPkgPath);
|
|
4799
|
-
const binaryPath =
|
|
4800
|
-
if (
|
|
5854
|
+
const binaryPath = join12(cliDir, "bin", binaryName);
|
|
5855
|
+
if (existsSync9(binaryPath)) {
|
|
4801
5856
|
debugLog2("found binary in main package:", binaryPath);
|
|
4802
5857
|
return binaryPath;
|
|
4803
5858
|
}
|
|
@@ -4823,7 +5878,7 @@ async function getCommentCheckerPath() {
|
|
|
4823
5878
|
}
|
|
4824
5879
|
initPromise = (async () => {
|
|
4825
5880
|
const syncPath = findCommentCheckerPathSync();
|
|
4826
|
-
if (syncPath &&
|
|
5881
|
+
if (syncPath && existsSync9(syncPath)) {
|
|
4827
5882
|
resolvedCliPath = syncPath;
|
|
4828
5883
|
debugLog2("using sync-resolved path:", syncPath);
|
|
4829
5884
|
return syncPath;
|
|
@@ -4857,14 +5912,14 @@ async function runCommentChecker(input, cliPath) {
|
|
|
4857
5912
|
debugLog2("comment-checker binary not found");
|
|
4858
5913
|
return { hasComments: false, message: "" };
|
|
4859
5914
|
}
|
|
4860
|
-
if (!
|
|
5915
|
+
if (!existsSync9(binaryPath)) {
|
|
4861
5916
|
debugLog2("comment-checker binary does not exist:", binaryPath);
|
|
4862
5917
|
return { hasComments: false, message: "" };
|
|
4863
5918
|
}
|
|
4864
5919
|
const jsonInput = JSON.stringify(input);
|
|
4865
5920
|
debugLog2("running comment-checker with input:", jsonInput.substring(0, 200));
|
|
4866
5921
|
try {
|
|
4867
|
-
const proc =
|
|
5922
|
+
const proc = spawn4([binaryPath], {
|
|
4868
5923
|
stdin: "pipe",
|
|
4869
5924
|
stdout: "pipe",
|
|
4870
5925
|
stderr: "pipe"
|
|
@@ -4891,11 +5946,11 @@ async function runCommentChecker(input, cliPath) {
|
|
|
4891
5946
|
|
|
4892
5947
|
// src/hooks/comment-checker/index.ts
|
|
4893
5948
|
import * as fs4 from "fs";
|
|
4894
|
-
import { existsSync as
|
|
5949
|
+
import { existsSync as existsSync10 } from "fs";
|
|
4895
5950
|
import { tmpdir as tmpdir4 } from "os";
|
|
4896
|
-
import { join as
|
|
5951
|
+
import { join as join13 } from "path";
|
|
4897
5952
|
var DEBUG3 = process.env.COMMENT_CHECKER_DEBUG === "1";
|
|
4898
|
-
var DEBUG_FILE3 =
|
|
5953
|
+
var DEBUG_FILE3 = join13(tmpdir4(), "comment-checker-debug.log");
|
|
4899
5954
|
function debugLog3(...args) {
|
|
4900
5955
|
if (DEBUG3) {
|
|
4901
5956
|
const msg = `[${new Date().toISOString()}] [comment-checker:hook] ${args.map((a) => typeof a === "object" ? JSON.stringify(a, null, 2) : String(a)).join(" ")}
|
|
@@ -4971,7 +6026,7 @@ function createCommentCheckerHooks() {
|
|
|
4971
6026
|
}
|
|
4972
6027
|
try {
|
|
4973
6028
|
const cliPath = await cliPathPromise;
|
|
4974
|
-
if (!cliPath || !
|
|
6029
|
+
if (!cliPath || !existsSync10(cliPath)) {
|
|
4975
6030
|
debugLog3("CLI not available, skipping comment check");
|
|
4976
6031
|
return;
|
|
4977
6032
|
}
|
|
@@ -5043,35 +6098,35 @@ function createToolOutputTruncatorHook(ctx, options) {
|
|
|
5043
6098
|
};
|
|
5044
6099
|
}
|
|
5045
6100
|
// src/hooks/directory-agents-injector/index.ts
|
|
5046
|
-
import { existsSync as
|
|
5047
|
-
import { dirname as dirname2, join as
|
|
6101
|
+
import { existsSync as existsSync12, readFileSync as readFileSync6 } from "fs";
|
|
6102
|
+
import { dirname as dirname2, join as join16, resolve as resolve2 } from "path";
|
|
5048
6103
|
|
|
5049
6104
|
// src/hooks/directory-agents-injector/storage.ts
|
|
5050
6105
|
import {
|
|
5051
|
-
existsSync as
|
|
6106
|
+
existsSync as existsSync11,
|
|
5052
6107
|
mkdirSync as mkdirSync4,
|
|
5053
|
-
readFileSync as
|
|
6108
|
+
readFileSync as readFileSync5,
|
|
5054
6109
|
writeFileSync as writeFileSync3,
|
|
5055
6110
|
unlinkSync as unlinkSync3
|
|
5056
6111
|
} from "fs";
|
|
5057
|
-
import { join as
|
|
6112
|
+
import { join as join15 } from "path";
|
|
5058
6113
|
|
|
5059
6114
|
// src/hooks/directory-agents-injector/constants.ts
|
|
5060
|
-
import { join as
|
|
5061
|
-
var OPENCODE_STORAGE3 =
|
|
5062
|
-
var AGENTS_INJECTOR_STORAGE =
|
|
6115
|
+
import { join as join14 } from "path";
|
|
6116
|
+
var OPENCODE_STORAGE3 = join14(xdgData ?? "", "opencode", "storage");
|
|
6117
|
+
var AGENTS_INJECTOR_STORAGE = join14(OPENCODE_STORAGE3, "directory-agents");
|
|
5063
6118
|
var AGENTS_FILENAME = "AGENTS.md";
|
|
5064
6119
|
|
|
5065
6120
|
// src/hooks/directory-agents-injector/storage.ts
|
|
5066
6121
|
function getStoragePath(sessionID) {
|
|
5067
|
-
return
|
|
6122
|
+
return join15(AGENTS_INJECTOR_STORAGE, `${sessionID}.json`);
|
|
5068
6123
|
}
|
|
5069
6124
|
function loadInjectedPaths(sessionID) {
|
|
5070
6125
|
const filePath = getStoragePath(sessionID);
|
|
5071
|
-
if (!
|
|
6126
|
+
if (!existsSync11(filePath))
|
|
5072
6127
|
return new Set;
|
|
5073
6128
|
try {
|
|
5074
|
-
const content =
|
|
6129
|
+
const content = readFileSync5(filePath, "utf-8");
|
|
5075
6130
|
const data = JSON.parse(content);
|
|
5076
6131
|
return new Set(data.injectedPaths);
|
|
5077
6132
|
} catch {
|
|
@@ -5079,7 +6134,7 @@ function loadInjectedPaths(sessionID) {
|
|
|
5079
6134
|
}
|
|
5080
6135
|
}
|
|
5081
6136
|
function saveInjectedPaths(sessionID, paths) {
|
|
5082
|
-
if (!
|
|
6137
|
+
if (!existsSync11(AGENTS_INJECTOR_STORAGE)) {
|
|
5083
6138
|
mkdirSync4(AGENTS_INJECTOR_STORAGE, { recursive: true });
|
|
5084
6139
|
}
|
|
5085
6140
|
const data = {
|
|
@@ -5091,7 +6146,7 @@ function saveInjectedPaths(sessionID, paths) {
|
|
|
5091
6146
|
}
|
|
5092
6147
|
function clearInjectedPaths(sessionID) {
|
|
5093
6148
|
const filePath = getStoragePath(sessionID);
|
|
5094
|
-
if (
|
|
6149
|
+
if (existsSync11(filePath)) {
|
|
5095
6150
|
unlinkSync3(filePath);
|
|
5096
6151
|
}
|
|
5097
6152
|
}
|
|
@@ -5118,8 +6173,8 @@ function createDirectoryAgentsInjectorHook(ctx) {
|
|
|
5118
6173
|
const found = [];
|
|
5119
6174
|
let current = startDir;
|
|
5120
6175
|
while (true) {
|
|
5121
|
-
const agentsPath =
|
|
5122
|
-
if (
|
|
6176
|
+
const agentsPath = join16(current, AGENTS_FILENAME);
|
|
6177
|
+
if (existsSync12(agentsPath)) {
|
|
5123
6178
|
found.push(agentsPath);
|
|
5124
6179
|
}
|
|
5125
6180
|
if (current === ctx.directory)
|
|
@@ -5145,7 +6200,7 @@ function createDirectoryAgentsInjectorHook(ctx) {
|
|
|
5145
6200
|
if (cache.has(agentsDir))
|
|
5146
6201
|
continue;
|
|
5147
6202
|
try {
|
|
5148
|
-
const content =
|
|
6203
|
+
const content = readFileSync6(agentsPath, "utf-8");
|
|
5149
6204
|
const { result, truncated } = await truncator.truncate(sessionID, content);
|
|
5150
6205
|
const truncationNotice = truncated ? `
|
|
5151
6206
|
|
|
@@ -5215,35 +6270,35 @@ ${result}${truncationNotice}`;
|
|
|
5215
6270
|
};
|
|
5216
6271
|
}
|
|
5217
6272
|
// src/hooks/directory-readme-injector/index.ts
|
|
5218
|
-
import { existsSync as
|
|
5219
|
-
import { dirname as dirname3, join as
|
|
6273
|
+
import { existsSync as existsSync14, readFileSync as readFileSync8 } from "fs";
|
|
6274
|
+
import { dirname as dirname3, join as join19, resolve as resolve3 } from "path";
|
|
5220
6275
|
|
|
5221
6276
|
// src/hooks/directory-readme-injector/storage.ts
|
|
5222
6277
|
import {
|
|
5223
|
-
existsSync as
|
|
6278
|
+
existsSync as existsSync13,
|
|
5224
6279
|
mkdirSync as mkdirSync5,
|
|
5225
|
-
readFileSync as
|
|
6280
|
+
readFileSync as readFileSync7,
|
|
5226
6281
|
writeFileSync as writeFileSync4,
|
|
5227
6282
|
unlinkSync as unlinkSync4
|
|
5228
6283
|
} from "fs";
|
|
5229
|
-
import { join as
|
|
6284
|
+
import { join as join18 } from "path";
|
|
5230
6285
|
|
|
5231
6286
|
// src/hooks/directory-readme-injector/constants.ts
|
|
5232
|
-
import { join as
|
|
5233
|
-
var OPENCODE_STORAGE4 =
|
|
5234
|
-
var README_INJECTOR_STORAGE =
|
|
6287
|
+
import { join as join17 } from "path";
|
|
6288
|
+
var OPENCODE_STORAGE4 = join17(xdgData ?? "", "opencode", "storage");
|
|
6289
|
+
var README_INJECTOR_STORAGE = join17(OPENCODE_STORAGE4, "directory-readme");
|
|
5235
6290
|
var README_FILENAME = "README.md";
|
|
5236
6291
|
|
|
5237
6292
|
// src/hooks/directory-readme-injector/storage.ts
|
|
5238
6293
|
function getStoragePath2(sessionID) {
|
|
5239
|
-
return
|
|
6294
|
+
return join18(README_INJECTOR_STORAGE, `${sessionID}.json`);
|
|
5240
6295
|
}
|
|
5241
6296
|
function loadInjectedPaths2(sessionID) {
|
|
5242
6297
|
const filePath = getStoragePath2(sessionID);
|
|
5243
|
-
if (!
|
|
6298
|
+
if (!existsSync13(filePath))
|
|
5244
6299
|
return new Set;
|
|
5245
6300
|
try {
|
|
5246
|
-
const content =
|
|
6301
|
+
const content = readFileSync7(filePath, "utf-8");
|
|
5247
6302
|
const data = JSON.parse(content);
|
|
5248
6303
|
return new Set(data.injectedPaths);
|
|
5249
6304
|
} catch {
|
|
@@ -5251,7 +6306,7 @@ function loadInjectedPaths2(sessionID) {
|
|
|
5251
6306
|
}
|
|
5252
6307
|
}
|
|
5253
6308
|
function saveInjectedPaths2(sessionID, paths) {
|
|
5254
|
-
if (!
|
|
6309
|
+
if (!existsSync13(README_INJECTOR_STORAGE)) {
|
|
5255
6310
|
mkdirSync5(README_INJECTOR_STORAGE, { recursive: true });
|
|
5256
6311
|
}
|
|
5257
6312
|
const data = {
|
|
@@ -5263,7 +6318,7 @@ function saveInjectedPaths2(sessionID, paths) {
|
|
|
5263
6318
|
}
|
|
5264
6319
|
function clearInjectedPaths2(sessionID) {
|
|
5265
6320
|
const filePath = getStoragePath2(sessionID);
|
|
5266
|
-
if (
|
|
6321
|
+
if (existsSync13(filePath)) {
|
|
5267
6322
|
unlinkSync4(filePath);
|
|
5268
6323
|
}
|
|
5269
6324
|
}
|
|
@@ -5290,8 +6345,8 @@ function createDirectoryReadmeInjectorHook(ctx) {
|
|
|
5290
6345
|
const found = [];
|
|
5291
6346
|
let current = startDir;
|
|
5292
6347
|
while (true) {
|
|
5293
|
-
const readmePath =
|
|
5294
|
-
if (
|
|
6348
|
+
const readmePath = join19(current, README_FILENAME);
|
|
6349
|
+
if (existsSync14(readmePath)) {
|
|
5295
6350
|
found.push(readmePath);
|
|
5296
6351
|
}
|
|
5297
6352
|
if (current === ctx.directory)
|
|
@@ -5317,7 +6372,7 @@ function createDirectoryReadmeInjectorHook(ctx) {
|
|
|
5317
6372
|
if (cache.has(readmeDir))
|
|
5318
6373
|
continue;
|
|
5319
6374
|
try {
|
|
5320
|
-
const content =
|
|
6375
|
+
const content = readFileSync8(readmePath, "utf-8");
|
|
5321
6376
|
const { result, truncated } = await truncator.truncate(sessionID, content);
|
|
5322
6377
|
const truncationNotice = truncated ? `
|
|
5323
6378
|
|
|
@@ -5503,127 +6558,693 @@ function parseAnthropicTokenLimitError(err) {
|
|
|
5503
6558
|
if (isTokenLimitError(jsonStr)) {
|
|
5504
6559
|
textSources.push(jsonStr);
|
|
5505
6560
|
}
|
|
5506
|
-
} catch {}
|
|
5507
|
-
}
|
|
5508
|
-
const combinedText = textSources.join(" ");
|
|
5509
|
-
if (!isTokenLimitError(combinedText))
|
|
5510
|
-
return null;
|
|
5511
|
-
if (typeof responseBody === "string") {
|
|
5512
|
-
try {
|
|
5513
|
-
const jsonPatterns = [
|
|
5514
|
-
/data:\s*(\{[\s\S]*?\})\s*$/m,
|
|
5515
|
-
/(\{"type"\s*:\s*"error"[\s\S]*?\})/,
|
|
5516
|
-
/(\{[\s\S]*?"error"[\s\S]*?\})/
|
|
5517
|
-
];
|
|
5518
|
-
for (const pattern of jsonPatterns) {
|
|
5519
|
-
const dataMatch = responseBody.match(pattern);
|
|
5520
|
-
if (dataMatch) {
|
|
5521
|
-
try {
|
|
5522
|
-
const jsonData = JSON.parse(dataMatch[1]);
|
|
5523
|
-
const message = jsonData.error?.message || "";
|
|
5524
|
-
const tokens = extractTokensFromMessage(message);
|
|
5525
|
-
if (tokens) {
|
|
5526
|
-
return {
|
|
5527
|
-
currentTokens: tokens.current,
|
|
5528
|
-
maxTokens: tokens.max,
|
|
5529
|
-
requestId: jsonData.request_id,
|
|
5530
|
-
errorType: jsonData.error?.type || "token_limit_exceeded"
|
|
5531
|
-
};
|
|
6561
|
+
} catch {}
|
|
6562
|
+
}
|
|
6563
|
+
const combinedText = textSources.join(" ");
|
|
6564
|
+
if (!isTokenLimitError(combinedText))
|
|
6565
|
+
return null;
|
|
6566
|
+
if (typeof responseBody === "string") {
|
|
6567
|
+
try {
|
|
6568
|
+
const jsonPatterns = [
|
|
6569
|
+
/data:\s*(\{[\s\S]*?\})\s*$/m,
|
|
6570
|
+
/(\{"type"\s*:\s*"error"[\s\S]*?\})/,
|
|
6571
|
+
/(\{[\s\S]*?"error"[\s\S]*?\})/
|
|
6572
|
+
];
|
|
6573
|
+
for (const pattern of jsonPatterns) {
|
|
6574
|
+
const dataMatch = responseBody.match(pattern);
|
|
6575
|
+
if (dataMatch) {
|
|
6576
|
+
try {
|
|
6577
|
+
const jsonData = JSON.parse(dataMatch[1]);
|
|
6578
|
+
const message = jsonData.error?.message || "";
|
|
6579
|
+
const tokens = extractTokensFromMessage(message);
|
|
6580
|
+
if (tokens) {
|
|
6581
|
+
return {
|
|
6582
|
+
currentTokens: tokens.current,
|
|
6583
|
+
maxTokens: tokens.max,
|
|
6584
|
+
requestId: jsonData.request_id,
|
|
6585
|
+
errorType: jsonData.error?.type || "token_limit_exceeded"
|
|
6586
|
+
};
|
|
6587
|
+
}
|
|
6588
|
+
} catch {}
|
|
6589
|
+
}
|
|
6590
|
+
}
|
|
6591
|
+
const bedrockJson = JSON.parse(responseBody);
|
|
6592
|
+
if (typeof bedrockJson.message === "string" && isTokenLimitError(bedrockJson.message)) {
|
|
6593
|
+
return {
|
|
6594
|
+
currentTokens: 0,
|
|
6595
|
+
maxTokens: 0,
|
|
6596
|
+
errorType: "bedrock_input_too_long"
|
|
6597
|
+
};
|
|
6598
|
+
}
|
|
6599
|
+
} catch {}
|
|
6600
|
+
}
|
|
6601
|
+
for (const text of textSources) {
|
|
6602
|
+
const tokens = extractTokensFromMessage(text);
|
|
6603
|
+
if (tokens) {
|
|
6604
|
+
return {
|
|
6605
|
+
currentTokens: tokens.current,
|
|
6606
|
+
maxTokens: tokens.max,
|
|
6607
|
+
errorType: "token_limit_exceeded"
|
|
6608
|
+
};
|
|
6609
|
+
}
|
|
6610
|
+
}
|
|
6611
|
+
if (combinedText.toLowerCase().includes("non-empty content")) {
|
|
6612
|
+
return {
|
|
6613
|
+
currentTokens: 0,
|
|
6614
|
+
maxTokens: 0,
|
|
6615
|
+
errorType: "non-empty content",
|
|
6616
|
+
messageIndex: extractMessageIndex2(combinedText)
|
|
6617
|
+
};
|
|
6618
|
+
}
|
|
6619
|
+
if (isTokenLimitError(combinedText)) {
|
|
6620
|
+
return {
|
|
6621
|
+
currentTokens: 0,
|
|
6622
|
+
maxTokens: 0,
|
|
6623
|
+
errorType: "token_limit_exceeded_unknown"
|
|
6624
|
+
};
|
|
6625
|
+
}
|
|
6626
|
+
return null;
|
|
6627
|
+
}
|
|
6628
|
+
|
|
6629
|
+
// src/hooks/anthropic-auto-compact/types.ts
|
|
6630
|
+
var RETRY_CONFIG = {
|
|
6631
|
+
maxAttempts: 2,
|
|
6632
|
+
initialDelayMs: 2000,
|
|
6633
|
+
backoffFactor: 2,
|
|
6634
|
+
maxDelayMs: 30000
|
|
6635
|
+
};
|
|
6636
|
+
var FALLBACK_CONFIG = {
|
|
6637
|
+
maxRevertAttempts: 3,
|
|
6638
|
+
minMessagesRequired: 2
|
|
6639
|
+
};
|
|
6640
|
+
var TRUNCATE_CONFIG = {
|
|
6641
|
+
maxTruncateAttempts: 20,
|
|
6642
|
+
minOutputSizeToTruncate: 500,
|
|
6643
|
+
targetTokenRatio: 0.5,
|
|
6644
|
+
charsPerToken: 4
|
|
6645
|
+
};
|
|
6646
|
+
|
|
6647
|
+
// src/hooks/anthropic-auto-compact/pruning-deduplication.ts
|
|
6648
|
+
import { existsSync as existsSync15, readdirSync as readdirSync4, readFileSync as readFileSync9 } from "fs";
|
|
6649
|
+
import { join as join20 } from "path";
|
|
6650
|
+
|
|
6651
|
+
// src/hooks/anthropic-auto-compact/pruning-types.ts
|
|
6652
|
+
var CHARS_PER_TOKEN = 4;
|
|
6653
|
+
function estimateTokens2(text) {
|
|
6654
|
+
return Math.ceil(text.length / CHARS_PER_TOKEN);
|
|
6655
|
+
}
|
|
6656
|
+
|
|
6657
|
+
// src/hooks/anthropic-auto-compact/pruning-deduplication.ts
|
|
6658
|
+
var MESSAGE_STORAGE3 = join20(process.env.HOME || process.env.USERPROFILE || "", ".config", "opencode", "sessions");
|
|
6659
|
+
function createToolSignature(toolName, input) {
|
|
6660
|
+
const sortedInput = sortObject(input);
|
|
6661
|
+
return `${toolName}::${JSON.stringify(sortedInput)}`;
|
|
6662
|
+
}
|
|
6663
|
+
function sortObject(obj) {
|
|
6664
|
+
if (obj === null || obj === undefined)
|
|
6665
|
+
return obj;
|
|
6666
|
+
if (typeof obj !== "object")
|
|
6667
|
+
return obj;
|
|
6668
|
+
if (Array.isArray(obj))
|
|
6669
|
+
return obj.map(sortObject);
|
|
6670
|
+
const sorted = {};
|
|
6671
|
+
const keys = Object.keys(obj).sort();
|
|
6672
|
+
for (const key of keys) {
|
|
6673
|
+
sorted[key] = sortObject(obj[key]);
|
|
6674
|
+
}
|
|
6675
|
+
return sorted;
|
|
6676
|
+
}
|
|
6677
|
+
function getMessageDir3(sessionID) {
|
|
6678
|
+
if (!existsSync15(MESSAGE_STORAGE3))
|
|
6679
|
+
return null;
|
|
6680
|
+
const directPath = join20(MESSAGE_STORAGE3, sessionID);
|
|
6681
|
+
if (existsSync15(directPath))
|
|
6682
|
+
return directPath;
|
|
6683
|
+
for (const dir of readdirSync4(MESSAGE_STORAGE3)) {
|
|
6684
|
+
const sessionPath = join20(MESSAGE_STORAGE3, dir, sessionID);
|
|
6685
|
+
if (existsSync15(sessionPath))
|
|
6686
|
+
return sessionPath;
|
|
6687
|
+
}
|
|
6688
|
+
return null;
|
|
6689
|
+
}
|
|
6690
|
+
function readMessages2(sessionID) {
|
|
6691
|
+
const messageDir = getMessageDir3(sessionID);
|
|
6692
|
+
if (!messageDir)
|
|
6693
|
+
return [];
|
|
6694
|
+
const messages = [];
|
|
6695
|
+
try {
|
|
6696
|
+
const files = readdirSync4(messageDir).filter((f) => f.endsWith(".json"));
|
|
6697
|
+
for (const file of files) {
|
|
6698
|
+
const content = readFileSync9(join20(messageDir, file), "utf-8");
|
|
6699
|
+
const data = JSON.parse(content);
|
|
6700
|
+
if (data.parts) {
|
|
6701
|
+
messages.push(data);
|
|
6702
|
+
}
|
|
6703
|
+
}
|
|
6704
|
+
} catch {
|
|
6705
|
+
return [];
|
|
6706
|
+
}
|
|
6707
|
+
return messages;
|
|
6708
|
+
}
|
|
6709
|
+
function executeDeduplication(sessionID, state2, config, protectedTools) {
|
|
6710
|
+
if (!config.enabled)
|
|
6711
|
+
return 0;
|
|
6712
|
+
const messages = readMessages2(sessionID);
|
|
6713
|
+
const signatures = new Map;
|
|
6714
|
+
let currentTurn = 0;
|
|
6715
|
+
for (const msg of messages) {
|
|
6716
|
+
if (!msg.parts)
|
|
6717
|
+
continue;
|
|
6718
|
+
for (const part of msg.parts) {
|
|
6719
|
+
if (part.type === "step-start") {
|
|
6720
|
+
currentTurn++;
|
|
6721
|
+
continue;
|
|
6722
|
+
}
|
|
6723
|
+
if (part.type !== "tool" || !part.callID || !part.tool)
|
|
6724
|
+
continue;
|
|
6725
|
+
if (protectedTools.has(part.tool))
|
|
6726
|
+
continue;
|
|
6727
|
+
if (config.protectedTools?.includes(part.tool))
|
|
6728
|
+
continue;
|
|
6729
|
+
if (state2.toolIdsToPrune.has(part.callID))
|
|
6730
|
+
continue;
|
|
6731
|
+
const signature = createToolSignature(part.tool, part.state?.input);
|
|
6732
|
+
if (!signatures.has(signature)) {
|
|
6733
|
+
signatures.set(signature, []);
|
|
6734
|
+
}
|
|
6735
|
+
signatures.get(signature).push({
|
|
6736
|
+
toolName: part.tool,
|
|
6737
|
+
signature,
|
|
6738
|
+
callID: part.callID,
|
|
6739
|
+
turn: currentTurn
|
|
6740
|
+
});
|
|
6741
|
+
if (!state2.toolSignatures.has(signature)) {
|
|
6742
|
+
state2.toolSignatures.set(signature, []);
|
|
6743
|
+
}
|
|
6744
|
+
state2.toolSignatures.get(signature).push({
|
|
6745
|
+
toolName: part.tool,
|
|
6746
|
+
signature,
|
|
6747
|
+
callID: part.callID,
|
|
6748
|
+
turn: currentTurn
|
|
6749
|
+
});
|
|
6750
|
+
}
|
|
6751
|
+
}
|
|
6752
|
+
let prunedCount = 0;
|
|
6753
|
+
let tokensSaved = 0;
|
|
6754
|
+
for (const [signature, calls] of signatures) {
|
|
6755
|
+
if (calls.length > 1) {
|
|
6756
|
+
const toPrune = calls.slice(0, -1);
|
|
6757
|
+
for (const call of toPrune) {
|
|
6758
|
+
state2.toolIdsToPrune.add(call.callID);
|
|
6759
|
+
prunedCount++;
|
|
6760
|
+
const output = findToolOutput(messages, call.callID);
|
|
6761
|
+
if (output) {
|
|
6762
|
+
tokensSaved += estimateTokens2(output);
|
|
6763
|
+
}
|
|
6764
|
+
log("[pruning-deduplication] pruned duplicate", {
|
|
6765
|
+
tool: call.toolName,
|
|
6766
|
+
callID: call.callID,
|
|
6767
|
+
turn: call.turn,
|
|
6768
|
+
signature: signature.substring(0, 100)
|
|
6769
|
+
});
|
|
6770
|
+
}
|
|
6771
|
+
}
|
|
6772
|
+
}
|
|
6773
|
+
log("[pruning-deduplication] complete", {
|
|
6774
|
+
prunedCount,
|
|
6775
|
+
tokensSaved,
|
|
6776
|
+
uniqueSignatures: signatures.size
|
|
6777
|
+
});
|
|
6778
|
+
return prunedCount;
|
|
6779
|
+
}
|
|
6780
|
+
function findToolOutput(messages, callID) {
|
|
6781
|
+
for (const msg of messages) {
|
|
6782
|
+
if (!msg.parts)
|
|
6783
|
+
continue;
|
|
6784
|
+
for (const part of msg.parts) {
|
|
6785
|
+
if (part.type === "tool" && part.callID === callID && part.state?.output) {
|
|
6786
|
+
return part.state.output;
|
|
6787
|
+
}
|
|
6788
|
+
}
|
|
6789
|
+
}
|
|
6790
|
+
return null;
|
|
6791
|
+
}
|
|
6792
|
+
|
|
6793
|
+
// src/hooks/anthropic-auto-compact/pruning-supersede.ts
|
|
6794
|
+
import { existsSync as existsSync16, readdirSync as readdirSync5, readFileSync as readFileSync10 } from "fs";
|
|
6795
|
+
import { join as join21 } from "path";
|
|
6796
|
+
var MESSAGE_STORAGE4 = join21(process.env.HOME || process.env.USERPROFILE || "", ".config", "opencode", "sessions");
|
|
6797
|
+
function getMessageDir4(sessionID) {
|
|
6798
|
+
if (!existsSync16(MESSAGE_STORAGE4))
|
|
6799
|
+
return null;
|
|
6800
|
+
const directPath = join21(MESSAGE_STORAGE4, sessionID);
|
|
6801
|
+
if (existsSync16(directPath))
|
|
6802
|
+
return directPath;
|
|
6803
|
+
for (const dir of readdirSync5(MESSAGE_STORAGE4)) {
|
|
6804
|
+
const sessionPath = join21(MESSAGE_STORAGE4, dir, sessionID);
|
|
6805
|
+
if (existsSync16(sessionPath))
|
|
6806
|
+
return sessionPath;
|
|
6807
|
+
}
|
|
6808
|
+
return null;
|
|
6809
|
+
}
|
|
6810
|
+
function readMessages3(sessionID) {
|
|
6811
|
+
const messageDir = getMessageDir4(sessionID);
|
|
6812
|
+
if (!messageDir)
|
|
6813
|
+
return [];
|
|
6814
|
+
const messages = [];
|
|
6815
|
+
try {
|
|
6816
|
+
const files = readdirSync5(messageDir).filter((f) => f.endsWith(".json"));
|
|
6817
|
+
for (const file of files) {
|
|
6818
|
+
const content = readFileSync10(join21(messageDir, file), "utf-8");
|
|
6819
|
+
const data = JSON.parse(content);
|
|
6820
|
+
if (data.parts) {
|
|
6821
|
+
messages.push(data);
|
|
6822
|
+
}
|
|
6823
|
+
}
|
|
6824
|
+
} catch {
|
|
6825
|
+
return [];
|
|
6826
|
+
}
|
|
6827
|
+
return messages;
|
|
6828
|
+
}
|
|
6829
|
+
function extractFilePath(toolName, input) {
|
|
6830
|
+
if (!input || typeof input !== "object")
|
|
6831
|
+
return null;
|
|
6832
|
+
const inputObj = input;
|
|
6833
|
+
if (toolName === "write" || toolName === "edit" || toolName === "read") {
|
|
6834
|
+
if (typeof inputObj.filePath === "string") {
|
|
6835
|
+
return inputObj.filePath;
|
|
6836
|
+
}
|
|
6837
|
+
}
|
|
6838
|
+
return null;
|
|
6839
|
+
}
|
|
6840
|
+
function executeSupersedeWrites(sessionID, state2, config, protectedTools) {
|
|
6841
|
+
if (!config.enabled)
|
|
6842
|
+
return 0;
|
|
6843
|
+
const messages = readMessages3(sessionID);
|
|
6844
|
+
const writesByFile = new Map;
|
|
6845
|
+
const readsByFile = new Map;
|
|
6846
|
+
let currentTurn = 0;
|
|
6847
|
+
for (const msg of messages) {
|
|
6848
|
+
if (!msg.parts)
|
|
6849
|
+
continue;
|
|
6850
|
+
for (const part of msg.parts) {
|
|
6851
|
+
if (part.type === "step-start") {
|
|
6852
|
+
currentTurn++;
|
|
6853
|
+
continue;
|
|
6854
|
+
}
|
|
6855
|
+
if (part.type !== "tool" || !part.callID || !part.tool)
|
|
6856
|
+
continue;
|
|
6857
|
+
if (protectedTools.has(part.tool))
|
|
6858
|
+
continue;
|
|
6859
|
+
if (state2.toolIdsToPrune.has(part.callID))
|
|
6860
|
+
continue;
|
|
6861
|
+
const filePath = extractFilePath(part.tool, part.state?.input);
|
|
6862
|
+
if (!filePath)
|
|
6863
|
+
continue;
|
|
6864
|
+
if (part.tool === "write" || part.tool === "edit") {
|
|
6865
|
+
if (!writesByFile.has(filePath)) {
|
|
6866
|
+
writesByFile.set(filePath, []);
|
|
6867
|
+
}
|
|
6868
|
+
writesByFile.get(filePath).push({
|
|
6869
|
+
callID: part.callID,
|
|
6870
|
+
tool: part.tool,
|
|
6871
|
+
filePath,
|
|
6872
|
+
turn: currentTurn
|
|
6873
|
+
});
|
|
6874
|
+
if (!state2.fileOperations.has(filePath)) {
|
|
6875
|
+
state2.fileOperations.set(filePath, []);
|
|
6876
|
+
}
|
|
6877
|
+
state2.fileOperations.get(filePath).push({
|
|
6878
|
+
callID: part.callID,
|
|
6879
|
+
tool: part.tool,
|
|
6880
|
+
filePath,
|
|
6881
|
+
turn: currentTurn
|
|
6882
|
+
});
|
|
6883
|
+
} else if (part.tool === "read") {
|
|
6884
|
+
if (!readsByFile.has(filePath)) {
|
|
6885
|
+
readsByFile.set(filePath, []);
|
|
6886
|
+
}
|
|
6887
|
+
readsByFile.get(filePath).push(currentTurn);
|
|
6888
|
+
}
|
|
6889
|
+
}
|
|
6890
|
+
}
|
|
6891
|
+
let prunedCount = 0;
|
|
6892
|
+
let tokensSaved = 0;
|
|
6893
|
+
for (const [filePath, writes] of writesByFile) {
|
|
6894
|
+
const reads = readsByFile.get(filePath) || [];
|
|
6895
|
+
if (config.aggressive) {
|
|
6896
|
+
for (const write of writes) {
|
|
6897
|
+
const superseded = reads.some((readTurn) => readTurn > write.turn);
|
|
6898
|
+
if (superseded) {
|
|
6899
|
+
state2.toolIdsToPrune.add(write.callID);
|
|
6900
|
+
prunedCount++;
|
|
6901
|
+
const input = findToolInput(messages, write.callID);
|
|
6902
|
+
if (input) {
|
|
6903
|
+
tokensSaved += estimateTokens2(JSON.stringify(input));
|
|
6904
|
+
}
|
|
6905
|
+
log("[pruning-supersede] pruned superseded write", {
|
|
6906
|
+
tool: write.tool,
|
|
6907
|
+
callID: write.callID,
|
|
6908
|
+
turn: write.turn,
|
|
6909
|
+
filePath
|
|
6910
|
+
});
|
|
6911
|
+
}
|
|
6912
|
+
}
|
|
6913
|
+
} else {
|
|
6914
|
+
if (writes.length > 1) {
|
|
6915
|
+
for (const write of writes.slice(0, -1)) {
|
|
6916
|
+
const superseded = reads.some((readTurn) => readTurn > write.turn);
|
|
6917
|
+
if (superseded) {
|
|
6918
|
+
state2.toolIdsToPrune.add(write.callID);
|
|
6919
|
+
prunedCount++;
|
|
6920
|
+
const input = findToolInput(messages, write.callID);
|
|
6921
|
+
if (input) {
|
|
6922
|
+
tokensSaved += estimateTokens2(JSON.stringify(input));
|
|
5532
6923
|
}
|
|
5533
|
-
|
|
6924
|
+
log("[pruning-supersede] pruned superseded write (conservative)", {
|
|
6925
|
+
tool: write.tool,
|
|
6926
|
+
callID: write.callID,
|
|
6927
|
+
turn: write.turn,
|
|
6928
|
+
filePath
|
|
6929
|
+
});
|
|
6930
|
+
}
|
|
5534
6931
|
}
|
|
5535
6932
|
}
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
|
|
6933
|
+
}
|
|
6934
|
+
}
|
|
6935
|
+
log("[pruning-supersede] complete", {
|
|
6936
|
+
prunedCount,
|
|
6937
|
+
tokensSaved,
|
|
6938
|
+
filesTracked: writesByFile.size,
|
|
6939
|
+
mode: config.aggressive ? "aggressive" : "conservative"
|
|
6940
|
+
});
|
|
6941
|
+
return prunedCount;
|
|
6942
|
+
}
|
|
6943
|
+
function findToolInput(messages, callID) {
|
|
6944
|
+
for (const msg of messages) {
|
|
6945
|
+
if (!msg.parts)
|
|
6946
|
+
continue;
|
|
6947
|
+
for (const part of msg.parts) {
|
|
6948
|
+
if (part.type === "tool" && part.callID === callID && part.state?.input) {
|
|
6949
|
+
return part.state.input;
|
|
5543
6950
|
}
|
|
5544
|
-
}
|
|
6951
|
+
}
|
|
5545
6952
|
}
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
|
|
6953
|
+
return null;
|
|
6954
|
+
}
|
|
6955
|
+
|
|
6956
|
+
// src/hooks/anthropic-auto-compact/pruning-purge-errors.ts
|
|
6957
|
+
import { existsSync as existsSync17, readdirSync as readdirSync6, readFileSync as readFileSync11 } from "fs";
|
|
6958
|
+
import { join as join22 } from "path";
|
|
6959
|
+
var MESSAGE_STORAGE5 = join22(process.env.HOME || process.env.USERPROFILE || "", ".config", "opencode", "sessions");
|
|
6960
|
+
function getMessageDir5(sessionID) {
|
|
6961
|
+
if (!existsSync17(MESSAGE_STORAGE5))
|
|
6962
|
+
return null;
|
|
6963
|
+
const directPath = join22(MESSAGE_STORAGE5, sessionID);
|
|
6964
|
+
if (existsSync17(directPath))
|
|
6965
|
+
return directPath;
|
|
6966
|
+
for (const dir of readdirSync6(MESSAGE_STORAGE5)) {
|
|
6967
|
+
const sessionPath = join22(MESSAGE_STORAGE5, dir, sessionID);
|
|
6968
|
+
if (existsSync17(sessionPath))
|
|
6969
|
+
return sessionPath;
|
|
6970
|
+
}
|
|
6971
|
+
return null;
|
|
6972
|
+
}
|
|
6973
|
+
function readMessages4(sessionID) {
|
|
6974
|
+
const messageDir = getMessageDir5(sessionID);
|
|
6975
|
+
if (!messageDir)
|
|
6976
|
+
return [];
|
|
6977
|
+
const messages = [];
|
|
6978
|
+
try {
|
|
6979
|
+
const files = readdirSync6(messageDir).filter((f) => f.endsWith(".json"));
|
|
6980
|
+
for (const file of files) {
|
|
6981
|
+
const content = readFileSync11(join22(messageDir, file), "utf-8");
|
|
6982
|
+
const data = JSON.parse(content);
|
|
6983
|
+
if (data.parts) {
|
|
6984
|
+
messages.push(data);
|
|
6985
|
+
}
|
|
5554
6986
|
}
|
|
6987
|
+
} catch {
|
|
6988
|
+
return [];
|
|
5555
6989
|
}
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
|
|
6990
|
+
return messages;
|
|
6991
|
+
}
|
|
6992
|
+
function executePurgeErrors(sessionID, state2, config, protectedTools) {
|
|
6993
|
+
if (!config.enabled)
|
|
6994
|
+
return 0;
|
|
6995
|
+
const messages = readMessages4(sessionID);
|
|
6996
|
+
let currentTurn = 0;
|
|
6997
|
+
for (const msg of messages) {
|
|
6998
|
+
if (!msg.parts)
|
|
6999
|
+
continue;
|
|
7000
|
+
for (const part of msg.parts) {
|
|
7001
|
+
if (part.type === "step-start") {
|
|
7002
|
+
currentTurn++;
|
|
7003
|
+
}
|
|
7004
|
+
}
|
|
5563
7005
|
}
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
7006
|
+
state2.currentTurn = currentTurn;
|
|
7007
|
+
let turnCounter = 0;
|
|
7008
|
+
let prunedCount = 0;
|
|
7009
|
+
let tokensSaved = 0;
|
|
7010
|
+
for (const msg of messages) {
|
|
7011
|
+
if (!msg.parts)
|
|
7012
|
+
continue;
|
|
7013
|
+
for (const part of msg.parts) {
|
|
7014
|
+
if (part.type === "step-start") {
|
|
7015
|
+
turnCounter++;
|
|
7016
|
+
continue;
|
|
7017
|
+
}
|
|
7018
|
+
if (part.type !== "tool" || !part.callID || !part.tool)
|
|
7019
|
+
continue;
|
|
7020
|
+
if (protectedTools.has(part.tool))
|
|
7021
|
+
continue;
|
|
7022
|
+
if (config.protectedTools?.includes(part.tool))
|
|
7023
|
+
continue;
|
|
7024
|
+
if (state2.toolIdsToPrune.has(part.callID))
|
|
7025
|
+
continue;
|
|
7026
|
+
if (part.state?.status !== "error")
|
|
7027
|
+
continue;
|
|
7028
|
+
const turnAge = currentTurn - turnCounter;
|
|
7029
|
+
if (turnAge >= config.turns) {
|
|
7030
|
+
state2.toolIdsToPrune.add(part.callID);
|
|
7031
|
+
prunedCount++;
|
|
7032
|
+
const input = part.state.input;
|
|
7033
|
+
if (input) {
|
|
7034
|
+
tokensSaved += estimateTokens2(JSON.stringify(input));
|
|
7035
|
+
}
|
|
7036
|
+
const errorInfo = {
|
|
7037
|
+
callID: part.callID,
|
|
7038
|
+
toolName: part.tool,
|
|
7039
|
+
turn: turnCounter,
|
|
7040
|
+
errorAge: turnAge
|
|
7041
|
+
};
|
|
7042
|
+
state2.erroredTools.set(part.callID, errorInfo);
|
|
7043
|
+
log("[pruning-purge-errors] pruned old error", {
|
|
7044
|
+
tool: part.tool,
|
|
7045
|
+
callID: part.callID,
|
|
7046
|
+
turn: turnCounter,
|
|
7047
|
+
errorAge: turnAge,
|
|
7048
|
+
threshold: config.turns
|
|
7049
|
+
});
|
|
7050
|
+
}
|
|
7051
|
+
}
|
|
7052
|
+
}
|
|
7053
|
+
log("[pruning-purge-errors] complete", {
|
|
7054
|
+
prunedCount,
|
|
7055
|
+
tokensSaved,
|
|
7056
|
+
currentTurn,
|
|
7057
|
+
threshold: config.turns
|
|
7058
|
+
});
|
|
7059
|
+
return prunedCount;
|
|
7060
|
+
}
|
|
7061
|
+
|
|
7062
|
+
// src/hooks/anthropic-auto-compact/pruning-storage.ts
|
|
7063
|
+
import { existsSync as existsSync18, readdirSync as readdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync5 } from "fs";
|
|
7064
|
+
import { join as join23 } from "path";
|
|
7065
|
+
var MESSAGE_STORAGE6 = join23(process.env.HOME || process.env.USERPROFILE || "", ".config", "opencode", "sessions");
|
|
7066
|
+
function getMessageDir6(sessionID) {
|
|
7067
|
+
if (!existsSync18(MESSAGE_STORAGE6))
|
|
7068
|
+
return null;
|
|
7069
|
+
const directPath = join23(MESSAGE_STORAGE6, sessionID);
|
|
7070
|
+
if (existsSync18(directPath))
|
|
7071
|
+
return directPath;
|
|
7072
|
+
for (const dir of readdirSync7(MESSAGE_STORAGE6)) {
|
|
7073
|
+
const sessionPath = join23(MESSAGE_STORAGE6, dir, sessionID);
|
|
7074
|
+
if (existsSync18(sessionPath))
|
|
7075
|
+
return sessionPath;
|
|
5570
7076
|
}
|
|
5571
7077
|
return null;
|
|
5572
7078
|
}
|
|
7079
|
+
async function applyPruning(sessionID, state2) {
|
|
7080
|
+
const messageDir = getMessageDir6(sessionID);
|
|
7081
|
+
if (!messageDir) {
|
|
7082
|
+
log("[pruning-storage] message dir not found", { sessionID });
|
|
7083
|
+
return 0;
|
|
7084
|
+
}
|
|
7085
|
+
let totalTokensSaved = 0;
|
|
7086
|
+
let filesModified = 0;
|
|
7087
|
+
try {
|
|
7088
|
+
const files = readdirSync7(messageDir).filter((f) => f.endsWith(".json"));
|
|
7089
|
+
for (const file of files) {
|
|
7090
|
+
const filePath = join23(messageDir, file);
|
|
7091
|
+
const content = readFileSync12(filePath, "utf-8");
|
|
7092
|
+
const data = JSON.parse(content);
|
|
7093
|
+
if (!data.parts)
|
|
7094
|
+
continue;
|
|
7095
|
+
let modified = false;
|
|
7096
|
+
for (const part of data.parts) {
|
|
7097
|
+
if (part.type !== "tool" || !part.callID)
|
|
7098
|
+
continue;
|
|
7099
|
+
if (!state2.toolIdsToPrune.has(part.callID))
|
|
7100
|
+
continue;
|
|
7101
|
+
if (part.state?.input) {
|
|
7102
|
+
const inputStr = JSON.stringify(part.state.input);
|
|
7103
|
+
totalTokensSaved += estimateTokens2(inputStr);
|
|
7104
|
+
part.state.input = { __pruned: true, reason: "DCP" };
|
|
7105
|
+
modified = true;
|
|
7106
|
+
}
|
|
7107
|
+
if (part.state?.output) {
|
|
7108
|
+
totalTokensSaved += estimateTokens2(part.state.output);
|
|
7109
|
+
part.state.output = "[Content pruned by Dynamic Context Pruning]";
|
|
7110
|
+
modified = true;
|
|
7111
|
+
}
|
|
7112
|
+
}
|
|
7113
|
+
if (modified) {
|
|
7114
|
+
writeFileSync5(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
7115
|
+
filesModified++;
|
|
7116
|
+
}
|
|
7117
|
+
}
|
|
7118
|
+
} catch (error) {
|
|
7119
|
+
log("[pruning-storage] error applying pruning", {
|
|
7120
|
+
sessionID,
|
|
7121
|
+
error: String(error)
|
|
7122
|
+
});
|
|
7123
|
+
}
|
|
7124
|
+
log("[pruning-storage] applied pruning", {
|
|
7125
|
+
sessionID,
|
|
7126
|
+
filesModified,
|
|
7127
|
+
totalTokensSaved
|
|
7128
|
+
});
|
|
7129
|
+
return totalTokensSaved;
|
|
7130
|
+
}
|
|
5573
7131
|
|
|
5574
|
-
// src/hooks/anthropic-auto-compact/
|
|
5575
|
-
var
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
7132
|
+
// src/hooks/anthropic-auto-compact/pruning-executor.ts
|
|
7133
|
+
var DEFAULT_PROTECTED_TOOLS = new Set([
|
|
7134
|
+
"task",
|
|
7135
|
+
"todowrite",
|
|
7136
|
+
"todoread",
|
|
7137
|
+
"lsp_rename",
|
|
7138
|
+
"lsp_code_action_resolve",
|
|
7139
|
+
"session_read",
|
|
7140
|
+
"session_write",
|
|
7141
|
+
"session_search"
|
|
7142
|
+
]);
|
|
7143
|
+
function createPruningState() {
|
|
7144
|
+
return {
|
|
7145
|
+
toolIdsToPrune: new Set,
|
|
7146
|
+
currentTurn: 0,
|
|
7147
|
+
fileOperations: new Map,
|
|
7148
|
+
toolSignatures: new Map,
|
|
7149
|
+
erroredTools: new Map
|
|
7150
|
+
};
|
|
7151
|
+
}
|
|
7152
|
+
async function executeDynamicContextPruning(sessionID, config, client) {
|
|
7153
|
+
const state2 = createPruningState();
|
|
7154
|
+
const protectedTools = new Set([
|
|
7155
|
+
...DEFAULT_PROTECTED_TOOLS,
|
|
7156
|
+
...config.protected_tools || []
|
|
7157
|
+
]);
|
|
7158
|
+
log("[pruning-executor] starting DCP", {
|
|
7159
|
+
sessionID,
|
|
7160
|
+
notification: config.notification,
|
|
7161
|
+
turnProtection: config.turn_protection
|
|
7162
|
+
});
|
|
7163
|
+
let dedupCount = 0;
|
|
7164
|
+
let supersedeCount = 0;
|
|
7165
|
+
let purgeCount = 0;
|
|
7166
|
+
if (config.strategies?.deduplication?.enabled !== false) {
|
|
7167
|
+
dedupCount = executeDeduplication(sessionID, state2, { enabled: true }, protectedTools);
|
|
7168
|
+
}
|
|
7169
|
+
if (config.strategies?.supersede_writes?.enabled !== false) {
|
|
7170
|
+
supersedeCount = executeSupersedeWrites(sessionID, state2, {
|
|
7171
|
+
enabled: true,
|
|
7172
|
+
aggressive: config.strategies?.supersede_writes?.aggressive || false
|
|
7173
|
+
}, protectedTools);
|
|
7174
|
+
}
|
|
7175
|
+
if (config.strategies?.purge_errors?.enabled !== false) {
|
|
7176
|
+
purgeCount = executePurgeErrors(sessionID, state2, {
|
|
7177
|
+
enabled: true,
|
|
7178
|
+
turns: config.strategies?.purge_errors?.turns || 5
|
|
7179
|
+
}, protectedTools);
|
|
7180
|
+
}
|
|
7181
|
+
const totalPruned = state2.toolIdsToPrune.size;
|
|
7182
|
+
const tokensSaved = await applyPruning(sessionID, state2);
|
|
7183
|
+
log("[pruning-executor] DCP complete", {
|
|
7184
|
+
totalPruned,
|
|
7185
|
+
tokensSaved,
|
|
7186
|
+
deduplication: dedupCount,
|
|
7187
|
+
supersede: supersedeCount,
|
|
7188
|
+
purge: purgeCount
|
|
7189
|
+
});
|
|
7190
|
+
const result = {
|
|
7191
|
+
itemsPruned: totalPruned,
|
|
7192
|
+
totalTokensSaved: tokensSaved,
|
|
7193
|
+
strategies: {
|
|
7194
|
+
deduplication: dedupCount,
|
|
7195
|
+
supersedeWrites: supersedeCount,
|
|
7196
|
+
purgeErrors: purgeCount
|
|
7197
|
+
}
|
|
7198
|
+
};
|
|
7199
|
+
if (config.notification !== "off" && totalPruned > 0) {
|
|
7200
|
+
const message = config.notification === "detailed" ? `Pruned ${totalPruned} tool outputs (~${Math.round(tokensSaved / 1000)}k tokens). Dedup: ${dedupCount}, Supersede: ${supersedeCount}, Purge: ${purgeCount}` : `Pruned ${totalPruned} tool outputs (~${Math.round(tokensSaved / 1000)}k tokens)`;
|
|
7201
|
+
await client.tui.showToast({
|
|
7202
|
+
body: {
|
|
7203
|
+
title: "Dynamic Context Pruning",
|
|
7204
|
+
message,
|
|
7205
|
+
variant: "success",
|
|
7206
|
+
duration: 3000
|
|
7207
|
+
}
|
|
7208
|
+
}).catch(() => {});
|
|
7209
|
+
}
|
|
7210
|
+
return result;
|
|
7211
|
+
}
|
|
5591
7212
|
|
|
5592
7213
|
// src/hooks/anthropic-auto-compact/storage.ts
|
|
5593
|
-
import { existsSync as
|
|
5594
|
-
import { homedir as
|
|
5595
|
-
import { join as
|
|
5596
|
-
var OPENCODE_STORAGE5 =
|
|
5597
|
-
if (process.platform === "darwin" && !
|
|
5598
|
-
const localShare =
|
|
5599
|
-
if (
|
|
7214
|
+
import { existsSync as existsSync19, readdirSync as readdirSync8, readFileSync as readFileSync13, writeFileSync as writeFileSync6 } from "fs";
|
|
7215
|
+
import { homedir as homedir6 } from "os";
|
|
7216
|
+
import { join as join24 } from "path";
|
|
7217
|
+
var OPENCODE_STORAGE5 = join24(xdgData ?? "", "opencode", "storage");
|
|
7218
|
+
if (process.platform === "darwin" && !existsSync19(OPENCODE_STORAGE5)) {
|
|
7219
|
+
const localShare = join24(homedir6(), ".local", "share", "opencode", "storage");
|
|
7220
|
+
if (existsSync19(localShare)) {
|
|
5600
7221
|
OPENCODE_STORAGE5 = localShare;
|
|
5601
7222
|
}
|
|
5602
7223
|
}
|
|
5603
|
-
var
|
|
5604
|
-
var PART_STORAGE3 =
|
|
7224
|
+
var MESSAGE_STORAGE7 = join24(OPENCODE_STORAGE5, "message");
|
|
7225
|
+
var PART_STORAGE3 = join24(OPENCODE_STORAGE5, "part");
|
|
5605
7226
|
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.]";
|
|
5606
|
-
function
|
|
5607
|
-
if (!
|
|
7227
|
+
function getMessageDir7(sessionID) {
|
|
7228
|
+
if (!existsSync19(MESSAGE_STORAGE7))
|
|
5608
7229
|
return "";
|
|
5609
|
-
const directPath =
|
|
5610
|
-
if (
|
|
7230
|
+
const directPath = join24(MESSAGE_STORAGE7, sessionID);
|
|
7231
|
+
if (existsSync19(directPath)) {
|
|
5611
7232
|
return directPath;
|
|
5612
7233
|
}
|
|
5613
|
-
for (const dir of
|
|
5614
|
-
const sessionPath =
|
|
5615
|
-
if (
|
|
7234
|
+
for (const dir of readdirSync8(MESSAGE_STORAGE7)) {
|
|
7235
|
+
const sessionPath = join24(MESSAGE_STORAGE7, dir, sessionID);
|
|
7236
|
+
if (existsSync19(sessionPath)) {
|
|
5616
7237
|
return sessionPath;
|
|
5617
7238
|
}
|
|
5618
7239
|
}
|
|
5619
7240
|
return "";
|
|
5620
7241
|
}
|
|
5621
7242
|
function getMessageIds(sessionID) {
|
|
5622
|
-
const messageDir =
|
|
5623
|
-
if (!messageDir || !
|
|
7243
|
+
const messageDir = getMessageDir7(sessionID);
|
|
7244
|
+
if (!messageDir || !existsSync19(messageDir))
|
|
5624
7245
|
return [];
|
|
5625
7246
|
const messageIds = [];
|
|
5626
|
-
for (const file of
|
|
7247
|
+
for (const file of readdirSync8(messageDir)) {
|
|
5627
7248
|
if (!file.endsWith(".json"))
|
|
5628
7249
|
continue;
|
|
5629
7250
|
const messageId = file.replace(".json", "");
|
|
@@ -5635,15 +7256,15 @@ function findToolResultsBySize(sessionID) {
|
|
|
5635
7256
|
const messageIds = getMessageIds(sessionID);
|
|
5636
7257
|
const results = [];
|
|
5637
7258
|
for (const messageID of messageIds) {
|
|
5638
|
-
const partDir =
|
|
5639
|
-
if (!
|
|
7259
|
+
const partDir = join24(PART_STORAGE3, messageID);
|
|
7260
|
+
if (!existsSync19(partDir))
|
|
5640
7261
|
continue;
|
|
5641
|
-
for (const file of
|
|
7262
|
+
for (const file of readdirSync8(partDir)) {
|
|
5642
7263
|
if (!file.endsWith(".json"))
|
|
5643
7264
|
continue;
|
|
5644
7265
|
try {
|
|
5645
|
-
const partPath =
|
|
5646
|
-
const content =
|
|
7266
|
+
const partPath = join24(partDir, file);
|
|
7267
|
+
const content = readFileSync13(partPath, "utf-8");
|
|
5647
7268
|
const part = JSON.parse(content);
|
|
5648
7269
|
if (part.type === "tool" && part.state?.output && !part.truncated) {
|
|
5649
7270
|
results.push({
|
|
@@ -5667,7 +7288,7 @@ function findLargestToolResult(sessionID) {
|
|
|
5667
7288
|
}
|
|
5668
7289
|
function truncateToolResult(partPath) {
|
|
5669
7290
|
try {
|
|
5670
|
-
const content =
|
|
7291
|
+
const content = readFileSync13(partPath, "utf-8");
|
|
5671
7292
|
const part = JSON.parse(content);
|
|
5672
7293
|
if (!part.state?.output) {
|
|
5673
7294
|
return { success: false };
|
|
@@ -5681,7 +7302,7 @@ function truncateToolResult(partPath) {
|
|
|
5681
7302
|
part.state.time = { start: Date.now() };
|
|
5682
7303
|
}
|
|
5683
7304
|
part.state.time.compacted = Date.now();
|
|
5684
|
-
|
|
7305
|
+
writeFileSync6(partPath, JSON.stringify(part, null, 2));
|
|
5685
7306
|
return { success: true, toolName, originalSize };
|
|
5686
7307
|
} catch {
|
|
5687
7308
|
return { success: false };
|
|
@@ -5763,6 +7384,14 @@ function getOrCreateTruncateState(autoCompactState, sessionID) {
|
|
|
5763
7384
|
}
|
|
5764
7385
|
return state2;
|
|
5765
7386
|
}
|
|
7387
|
+
function getOrCreateDcpState(autoCompactState, sessionID) {
|
|
7388
|
+
let state2 = autoCompactState.dcpStateBySession.get(sessionID);
|
|
7389
|
+
if (!state2) {
|
|
7390
|
+
state2 = { attempted: false, itemsPruned: 0 };
|
|
7391
|
+
autoCompactState.dcpStateBySession.set(sessionID, state2);
|
|
7392
|
+
}
|
|
7393
|
+
return state2;
|
|
7394
|
+
}
|
|
5766
7395
|
async function getLastMessagePair(sessionID, client, directory) {
|
|
5767
7396
|
try {
|
|
5768
7397
|
const resp = await client.session.messages({
|
|
@@ -5835,6 +7464,7 @@ function clearSessionState(autoCompactState, sessionID) {
|
|
|
5835
7464
|
autoCompactState.retryStateBySession.delete(sessionID);
|
|
5836
7465
|
autoCompactState.fallbackStateBySession.delete(sessionID);
|
|
5837
7466
|
autoCompactState.truncateStateBySession.delete(sessionID);
|
|
7467
|
+
autoCompactState.dcpStateBySession.delete(sessionID);
|
|
5838
7468
|
autoCompactState.emptyContentAttemptBySession.delete(sessionID);
|
|
5839
7469
|
autoCompactState.compactionInProgress.delete(sessionID);
|
|
5840
7470
|
}
|
|
@@ -6085,6 +7715,45 @@ async function executeCompact(sessionID, msg, autoCompactState, client, director
|
|
|
6085
7715
|
}).catch(() => {});
|
|
6086
7716
|
}
|
|
6087
7717
|
}
|
|
7718
|
+
const dcpState = getOrCreateDcpState(autoCompactState, sessionID);
|
|
7719
|
+
if (experimental?.dcp_on_compaction_failure && !dcpState.attempted) {
|
|
7720
|
+
dcpState.attempted = true;
|
|
7721
|
+
log("[auto-compact] attempting DCP after summarize failed", { sessionID });
|
|
7722
|
+
const dcpConfig = experimental.dynamic_context_pruning ?? {
|
|
7723
|
+
enabled: true,
|
|
7724
|
+
notification: "detailed",
|
|
7725
|
+
protected_tools: ["task", "todowrite", "todoread", "lsp_rename", "lsp_code_action_resolve"]
|
|
7726
|
+
};
|
|
7727
|
+
try {
|
|
7728
|
+
const pruningResult = await executeDynamicContextPruning(sessionID, dcpConfig, client);
|
|
7729
|
+
if (pruningResult.itemsPruned > 0) {
|
|
7730
|
+
dcpState.itemsPruned = pruningResult.itemsPruned;
|
|
7731
|
+
log("[auto-compact] DCP successful, retrying compaction", {
|
|
7732
|
+
itemsPruned: pruningResult.itemsPruned,
|
|
7733
|
+
tokensSaved: pruningResult.totalTokensSaved
|
|
7734
|
+
});
|
|
7735
|
+
await client.tui.showToast({
|
|
7736
|
+
body: {
|
|
7737
|
+
title: "Dynamic Context Pruning",
|
|
7738
|
+
message: `Pruned ${pruningResult.itemsPruned} items (~${Math.round(pruningResult.totalTokensSaved / 1000)}k tokens). Retrying compaction...`,
|
|
7739
|
+
variant: "success",
|
|
7740
|
+
duration: 3000
|
|
7741
|
+
}
|
|
7742
|
+
}).catch(() => {});
|
|
7743
|
+
retryState.attempt = 0;
|
|
7744
|
+
setTimeout(() => {
|
|
7745
|
+
executeCompact(sessionID, msg, autoCompactState, client, directory, experimental);
|
|
7746
|
+
}, 500);
|
|
7747
|
+
return;
|
|
7748
|
+
} else {
|
|
7749
|
+
log("[auto-compact] DCP did not prune any items, continuing to revert", { sessionID });
|
|
7750
|
+
}
|
|
7751
|
+
} catch (error) {
|
|
7752
|
+
log("[auto-compact] DCP failed, continuing to revert", {
|
|
7753
|
+
error: String(error)
|
|
7754
|
+
});
|
|
7755
|
+
}
|
|
7756
|
+
}
|
|
6088
7757
|
const fallbackState = getOrCreateFallbackState(autoCompactState, sessionID);
|
|
6089
7758
|
if (fallbackState.revertAttempt < FALLBACK_CONFIG.maxRevertAttempts) {
|
|
6090
7759
|
const pair = await getLastMessagePair(sessionID, client, directory);
|
|
@@ -6157,6 +7826,7 @@ function createAutoCompactState() {
|
|
|
6157
7826
|
retryStateBySession: new Map,
|
|
6158
7827
|
fallbackStateBySession: new Map,
|
|
6159
7828
|
truncateStateBySession: new Map,
|
|
7829
|
+
dcpStateBySession: new Map,
|
|
6160
7830
|
emptyContentAttemptBySession: new Map,
|
|
6161
7831
|
compactionInProgress: new Set
|
|
6162
7832
|
};
|
|
@@ -6174,6 +7844,7 @@ function createAnthropicAutoCompactHook(ctx, options) {
|
|
|
6174
7844
|
autoCompactState.retryStateBySession.delete(sessionInfo.id);
|
|
6175
7845
|
autoCompactState.fallbackStateBySession.delete(sessionInfo.id);
|
|
6176
7846
|
autoCompactState.truncateStateBySession.delete(sessionInfo.id);
|
|
7847
|
+
autoCompactState.dcpStateBySession.delete(sessionInfo.id);
|
|
6177
7848
|
autoCompactState.emptyContentAttemptBySession.delete(sessionInfo.id);
|
|
6178
7849
|
autoCompactState.compactionInProgress.delete(sessionInfo.id);
|
|
6179
7850
|
}
|
|
@@ -6255,8 +7926,8 @@ function createAnthropicAutoCompactHook(ctx, options) {
|
|
|
6255
7926
|
};
|
|
6256
7927
|
}
|
|
6257
7928
|
// src/hooks/preemptive-compaction/index.ts
|
|
6258
|
-
import { existsSync as
|
|
6259
|
-
import { join as
|
|
7929
|
+
import { existsSync as existsSync20, readdirSync as readdirSync9 } from "fs";
|
|
7930
|
+
import { join as join25 } from "path";
|
|
6260
7931
|
|
|
6261
7932
|
// src/hooks/preemptive-compaction/constants.ts
|
|
6262
7933
|
var DEFAULT_THRESHOLD = 0.85;
|
|
@@ -6269,15 +7940,15 @@ var CLAUDE_DEFAULT_CONTEXT_LIMIT = 200000;
|
|
|
6269
7940
|
function isSupportedModel(modelID) {
|
|
6270
7941
|
return CLAUDE_MODEL_PATTERN.test(modelID);
|
|
6271
7942
|
}
|
|
6272
|
-
function
|
|
6273
|
-
if (!
|
|
7943
|
+
function getMessageDir8(sessionID) {
|
|
7944
|
+
if (!existsSync20(MESSAGE_STORAGE))
|
|
6274
7945
|
return null;
|
|
6275
|
-
const directPath =
|
|
6276
|
-
if (
|
|
7946
|
+
const directPath = join25(MESSAGE_STORAGE, sessionID);
|
|
7947
|
+
if (existsSync20(directPath))
|
|
6277
7948
|
return directPath;
|
|
6278
|
-
for (const dir of
|
|
6279
|
-
const sessionPath =
|
|
6280
|
-
if (
|
|
7949
|
+
for (const dir of readdirSync9(MESSAGE_STORAGE)) {
|
|
7950
|
+
const sessionPath = join25(MESSAGE_STORAGE, dir, sessionID);
|
|
7951
|
+
if (existsSync20(sessionPath))
|
|
6281
7952
|
return sessionPath;
|
|
6282
7953
|
}
|
|
6283
7954
|
return null;
|
|
@@ -6371,7 +8042,7 @@ function createPreemptiveCompactionHook(ctx, options) {
|
|
|
6371
8042
|
state2.compactionInProgress.delete(sessionID);
|
|
6372
8043
|
setTimeout(async () => {
|
|
6373
8044
|
try {
|
|
6374
|
-
const messageDir =
|
|
8045
|
+
const messageDir = getMessageDir8(sessionID);
|
|
6375
8046
|
const storedMessage = messageDir ? findNearestMessageWithFields(messageDir) : null;
|
|
6376
8047
|
await ctx.client.session.promptAsync({
|
|
6377
8048
|
path: { id: sessionID },
|
|
@@ -6427,7 +8098,7 @@ function createPreemptiveCompactionHook(ctx, options) {
|
|
|
6427
8098
|
return;
|
|
6428
8099
|
const lastAssistant = assistants[assistants.length - 1];
|
|
6429
8100
|
if (!lastAssistant.providerID || !lastAssistant.modelID) {
|
|
6430
|
-
const messageDir =
|
|
8101
|
+
const messageDir = getMessageDir8(sessionID);
|
|
6431
8102
|
const storedMessage = messageDir ? findNearestMessageWithFields(messageDir) : null;
|
|
6432
8103
|
if (storedMessage?.model?.providerID && storedMessage?.model?.modelID) {
|
|
6433
8104
|
lastAssistant.providerID = storedMessage.model.providerID;
|
|
@@ -6761,9 +8432,8 @@ function createThinkModeHook() {
|
|
|
6761
8432
|
};
|
|
6762
8433
|
}
|
|
6763
8434
|
// src/hooks/claude-code-hooks/config.ts
|
|
6764
|
-
import {
|
|
6765
|
-
import {
|
|
6766
|
-
import { existsSync as existsSync16 } from "fs";
|
|
8435
|
+
import { join as join26 } from "path";
|
|
8436
|
+
import { existsSync as existsSync21 } from "fs";
|
|
6767
8437
|
function normalizeHookMatcher(raw) {
|
|
6768
8438
|
return {
|
|
6769
8439
|
matcher: raw.matcher ?? raw.pattern ?? "*",
|
|
@@ -6787,13 +8457,13 @@ function normalizeHooksConfig(raw) {
|
|
|
6787
8457
|
return result;
|
|
6788
8458
|
}
|
|
6789
8459
|
function getClaudeSettingsPaths(customPath) {
|
|
6790
|
-
const
|
|
8460
|
+
const claudeConfigDir = getClaudeConfigDir();
|
|
6791
8461
|
const paths = [
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
|
|
8462
|
+
join26(claudeConfigDir, "settings.json"),
|
|
8463
|
+
join26(process.cwd(), ".claude", "settings.json"),
|
|
8464
|
+
join26(process.cwd(), ".claude", "settings.local.json")
|
|
6795
8465
|
];
|
|
6796
|
-
if (customPath &&
|
|
8466
|
+
if (customPath && existsSync21(customPath)) {
|
|
6797
8467
|
paths.unshift(customPath);
|
|
6798
8468
|
}
|
|
6799
8469
|
return paths;
|
|
@@ -6818,7 +8488,7 @@ async function loadClaudeHooksConfig(customSettingsPath) {
|
|
|
6818
8488
|
const paths = getClaudeSettingsPaths(customSettingsPath);
|
|
6819
8489
|
let mergedConfig = {};
|
|
6820
8490
|
for (const settingsPath of paths) {
|
|
6821
|
-
if (
|
|
8491
|
+
if (existsSync21(settingsPath)) {
|
|
6822
8492
|
try {
|
|
6823
8493
|
const content = await Bun.file(settingsPath).text();
|
|
6824
8494
|
const settings = JSON.parse(content);
|
|
@@ -6835,15 +8505,15 @@ async function loadClaudeHooksConfig(customSettingsPath) {
|
|
|
6835
8505
|
}
|
|
6836
8506
|
|
|
6837
8507
|
// src/hooks/claude-code-hooks/config-loader.ts
|
|
6838
|
-
import { existsSync as
|
|
8508
|
+
import { existsSync as existsSync22 } from "fs";
|
|
6839
8509
|
import { homedir as homedir7 } from "os";
|
|
6840
|
-
import { join as
|
|
6841
|
-
var USER_CONFIG_PATH =
|
|
8510
|
+
import { join as join27 } from "path";
|
|
8511
|
+
var USER_CONFIG_PATH = join27(homedir7(), ".config", "opencode", "opencode-cc-plugin.json");
|
|
6842
8512
|
function getProjectConfigPath() {
|
|
6843
|
-
return
|
|
8513
|
+
return join27(process.cwd(), ".opencode", "opencode-cc-plugin.json");
|
|
6844
8514
|
}
|
|
6845
8515
|
async function loadConfigFromPath(path5) {
|
|
6846
|
-
if (!
|
|
8516
|
+
if (!existsSync22(path5)) {
|
|
6847
8517
|
return null;
|
|
6848
8518
|
}
|
|
6849
8519
|
try {
|
|
@@ -7023,16 +8693,16 @@ async function executePreToolUseHooks(ctx, config, extendedConfig) {
|
|
|
7023
8693
|
}
|
|
7024
8694
|
|
|
7025
8695
|
// src/hooks/claude-code-hooks/transcript.ts
|
|
7026
|
-
import { join as
|
|
7027
|
-
import { mkdirSync as mkdirSync6, appendFileSync as appendFileSync5, existsSync as
|
|
7028
|
-
import {
|
|
8696
|
+
import { join as join28 } from "path";
|
|
8697
|
+
import { mkdirSync as mkdirSync6, appendFileSync as appendFileSync5, existsSync as existsSync23, writeFileSync as writeFileSync7, unlinkSync as unlinkSync5 } from "fs";
|
|
8698
|
+
import { tmpdir as tmpdir5 } from "os";
|
|
7029
8699
|
import { randomUUID } from "crypto";
|
|
7030
|
-
var TRANSCRIPT_DIR =
|
|
8700
|
+
var TRANSCRIPT_DIR = join28(getClaudeConfigDir(), "transcripts");
|
|
7031
8701
|
function getTranscriptPath(sessionId) {
|
|
7032
|
-
return
|
|
8702
|
+
return join28(TRANSCRIPT_DIR, `${sessionId}.jsonl`);
|
|
7033
8703
|
}
|
|
7034
8704
|
function ensureTranscriptDir() {
|
|
7035
|
-
if (!
|
|
8705
|
+
if (!existsSync23(TRANSCRIPT_DIR)) {
|
|
7036
8706
|
mkdirSync6(TRANSCRIPT_DIR, { recursive: true });
|
|
7037
8707
|
}
|
|
7038
8708
|
}
|
|
@@ -7119,8 +8789,8 @@ async function buildTranscriptFromSession(client, sessionId, directory, currentT
|
|
|
7119
8789
|
}
|
|
7120
8790
|
};
|
|
7121
8791
|
entries.push(JSON.stringify(currentEntry));
|
|
7122
|
-
const tempPath =
|
|
7123
|
-
|
|
8792
|
+
const tempPath = join28(tmpdir5(), `opencode-transcript-${sessionId}-${randomUUID()}.jsonl`);
|
|
8793
|
+
writeFileSync7(tempPath, entries.join(`
|
|
7124
8794
|
`) + `
|
|
7125
8795
|
`);
|
|
7126
8796
|
return tempPath;
|
|
@@ -7139,8 +8809,8 @@ async function buildTranscriptFromSession(client, sessionId, directory, currentT
|
|
|
7139
8809
|
]
|
|
7140
8810
|
}
|
|
7141
8811
|
};
|
|
7142
|
-
const tempPath =
|
|
7143
|
-
|
|
8812
|
+
const tempPath = join28(tmpdir5(), `opencode-transcript-${sessionId}-${randomUUID()}.jsonl`);
|
|
8813
|
+
writeFileSync7(tempPath, JSON.stringify(currentEntry) + `
|
|
7144
8814
|
`);
|
|
7145
8815
|
return tempPath;
|
|
7146
8816
|
} catch {
|
|
@@ -7351,11 +9021,10 @@ ${USER_PROMPT_SUBMIT_TAG_CLOSE}`);
|
|
|
7351
9021
|
}
|
|
7352
9022
|
|
|
7353
9023
|
// src/hooks/claude-code-hooks/todo.ts
|
|
7354
|
-
import { join as
|
|
7355
|
-
|
|
7356
|
-
var TODO_DIR = join24(homedir9(), ".claude", "todos");
|
|
9024
|
+
import { join as join29 } from "path";
|
|
9025
|
+
var TODO_DIR = join29(getClaudeConfigDir(), "todos");
|
|
7357
9026
|
function getTodoPath(sessionId) {
|
|
7358
|
-
return
|
|
9027
|
+
return join29(TODO_DIR, `${sessionId}-agent-${sessionId}.json`);
|
|
7359
9028
|
}
|
|
7360
9029
|
|
|
7361
9030
|
// src/hooks/claude-code-hooks/stop.ts
|
|
@@ -7779,23 +9448,23 @@ ${result.message}`;
|
|
|
7779
9448
|
};
|
|
7780
9449
|
}
|
|
7781
9450
|
// src/hooks/rules-injector/index.ts
|
|
7782
|
-
import { readFileSync as
|
|
7783
|
-
import { homedir as
|
|
9451
|
+
import { readFileSync as readFileSync15 } from "fs";
|
|
9452
|
+
import { homedir as homedir8 } from "os";
|
|
7784
9453
|
import { relative as relative3, resolve as resolve4 } from "path";
|
|
7785
9454
|
|
|
7786
9455
|
// src/hooks/rules-injector/finder.ts
|
|
7787
9456
|
import {
|
|
7788
|
-
existsSync as
|
|
7789
|
-
readdirSync as
|
|
9457
|
+
existsSync as existsSync24,
|
|
9458
|
+
readdirSync as readdirSync10,
|
|
7790
9459
|
realpathSync,
|
|
7791
9460
|
statSync as statSync2
|
|
7792
9461
|
} from "fs";
|
|
7793
|
-
import { dirname as dirname4, join as
|
|
9462
|
+
import { dirname as dirname4, join as join31, relative } from "path";
|
|
7794
9463
|
|
|
7795
9464
|
// src/hooks/rules-injector/constants.ts
|
|
7796
|
-
import { join as
|
|
7797
|
-
var OPENCODE_STORAGE6 =
|
|
7798
|
-
var RULES_INJECTOR_STORAGE =
|
|
9465
|
+
import { join as join30 } from "path";
|
|
9466
|
+
var OPENCODE_STORAGE6 = join30(xdgData ?? "", "opencode", "storage");
|
|
9467
|
+
var RULES_INJECTOR_STORAGE = join30(OPENCODE_STORAGE6, "rules-injector");
|
|
7799
9468
|
var PROJECT_MARKERS = [
|
|
7800
9469
|
".git",
|
|
7801
9470
|
"pyproject.toml",
|
|
@@ -7822,8 +9491,8 @@ function findProjectRoot(startPath) {
|
|
|
7822
9491
|
}
|
|
7823
9492
|
while (true) {
|
|
7824
9493
|
for (const marker of PROJECT_MARKERS) {
|
|
7825
|
-
const markerPath =
|
|
7826
|
-
if (
|
|
9494
|
+
const markerPath = join31(current, marker);
|
|
9495
|
+
if (existsSync24(markerPath)) {
|
|
7827
9496
|
return current;
|
|
7828
9497
|
}
|
|
7829
9498
|
}
|
|
@@ -7835,12 +9504,12 @@ function findProjectRoot(startPath) {
|
|
|
7835
9504
|
}
|
|
7836
9505
|
}
|
|
7837
9506
|
function findRuleFilesRecursive(dir, results) {
|
|
7838
|
-
if (!
|
|
9507
|
+
if (!existsSync24(dir))
|
|
7839
9508
|
return;
|
|
7840
9509
|
try {
|
|
7841
|
-
const entries =
|
|
9510
|
+
const entries = readdirSync10(dir, { withFileTypes: true });
|
|
7842
9511
|
for (const entry of entries) {
|
|
7843
|
-
const fullPath =
|
|
9512
|
+
const fullPath = join31(dir, entry.name);
|
|
7844
9513
|
if (entry.isDirectory()) {
|
|
7845
9514
|
findRuleFilesRecursive(fullPath, results);
|
|
7846
9515
|
} else if (entry.isFile()) {
|
|
@@ -7866,7 +9535,7 @@ function findRuleFiles(projectRoot, homeDir, currentFile) {
|
|
|
7866
9535
|
let distance = 0;
|
|
7867
9536
|
while (true) {
|
|
7868
9537
|
for (const [parent, subdir] of PROJECT_RULE_SUBDIRS) {
|
|
7869
|
-
const ruleDir =
|
|
9538
|
+
const ruleDir = join31(currentDir, parent, subdir);
|
|
7870
9539
|
const files = [];
|
|
7871
9540
|
findRuleFilesRecursive(ruleDir, files);
|
|
7872
9541
|
for (const filePath of files) {
|
|
@@ -7890,7 +9559,7 @@ function findRuleFiles(projectRoot, homeDir, currentFile) {
|
|
|
7890
9559
|
currentDir = parentDir;
|
|
7891
9560
|
distance++;
|
|
7892
9561
|
}
|
|
7893
|
-
const userRuleDir =
|
|
9562
|
+
const userRuleDir = join31(homeDir, USER_RULE_DIR);
|
|
7894
9563
|
const userFiles = [];
|
|
7895
9564
|
findRuleFilesRecursive(userRuleDir, userFiles);
|
|
7896
9565
|
for (const filePath of userFiles) {
|
|
@@ -8079,22 +9748,22 @@ function mergeGlobs(existing, newValue) {
|
|
|
8079
9748
|
|
|
8080
9749
|
// src/hooks/rules-injector/storage.ts
|
|
8081
9750
|
import {
|
|
8082
|
-
existsSync as
|
|
9751
|
+
existsSync as existsSync25,
|
|
8083
9752
|
mkdirSync as mkdirSync7,
|
|
8084
|
-
readFileSync as
|
|
8085
|
-
writeFileSync as
|
|
9753
|
+
readFileSync as readFileSync14,
|
|
9754
|
+
writeFileSync as writeFileSync8,
|
|
8086
9755
|
unlinkSync as unlinkSync6
|
|
8087
9756
|
} from "fs";
|
|
8088
|
-
import { join as
|
|
9757
|
+
import { join as join32 } from "path";
|
|
8089
9758
|
function getStoragePath3(sessionID) {
|
|
8090
|
-
return
|
|
9759
|
+
return join32(RULES_INJECTOR_STORAGE, `${sessionID}.json`);
|
|
8091
9760
|
}
|
|
8092
9761
|
function loadInjectedRules(sessionID) {
|
|
8093
9762
|
const filePath = getStoragePath3(sessionID);
|
|
8094
|
-
if (!
|
|
9763
|
+
if (!existsSync25(filePath))
|
|
8095
9764
|
return { contentHashes: new Set, realPaths: new Set };
|
|
8096
9765
|
try {
|
|
8097
|
-
const content =
|
|
9766
|
+
const content = readFileSync14(filePath, "utf-8");
|
|
8098
9767
|
const data = JSON.parse(content);
|
|
8099
9768
|
return {
|
|
8100
9769
|
contentHashes: new Set(data.injectedHashes),
|
|
@@ -8105,7 +9774,7 @@ function loadInjectedRules(sessionID) {
|
|
|
8105
9774
|
}
|
|
8106
9775
|
}
|
|
8107
9776
|
function saveInjectedRules(sessionID, data) {
|
|
8108
|
-
if (!
|
|
9777
|
+
if (!existsSync25(RULES_INJECTOR_STORAGE)) {
|
|
8109
9778
|
mkdirSync7(RULES_INJECTOR_STORAGE, { recursive: true });
|
|
8110
9779
|
}
|
|
8111
9780
|
const storageData = {
|
|
@@ -8114,11 +9783,11 @@ function saveInjectedRules(sessionID, data) {
|
|
|
8114
9783
|
injectedRealPaths: [...data.realPaths],
|
|
8115
9784
|
updatedAt: Date.now()
|
|
8116
9785
|
};
|
|
8117
|
-
|
|
9786
|
+
writeFileSync8(getStoragePath3(sessionID), JSON.stringify(storageData, null, 2));
|
|
8118
9787
|
}
|
|
8119
9788
|
function clearInjectedRules(sessionID) {
|
|
8120
9789
|
const filePath = getStoragePath3(sessionID);
|
|
8121
|
-
if (
|
|
9790
|
+
if (existsSync25(filePath)) {
|
|
8122
9791
|
unlinkSync6(filePath);
|
|
8123
9792
|
}
|
|
8124
9793
|
}
|
|
@@ -8148,14 +9817,14 @@ function createRulesInjectorHook(ctx) {
|
|
|
8148
9817
|
return;
|
|
8149
9818
|
const projectRoot = findProjectRoot(resolved);
|
|
8150
9819
|
const cache2 = getSessionCache(sessionID);
|
|
8151
|
-
const home =
|
|
9820
|
+
const home = homedir8();
|
|
8152
9821
|
const ruleFileCandidates = findRuleFiles(projectRoot, home, resolved);
|
|
8153
9822
|
const toInject = [];
|
|
8154
9823
|
for (const candidate of ruleFileCandidates) {
|
|
8155
9824
|
if (isDuplicateByRealPath(candidate.realPath, cache2.realPaths))
|
|
8156
9825
|
continue;
|
|
8157
9826
|
try {
|
|
8158
|
-
const rawContent =
|
|
9827
|
+
const rawContent = readFileSync15(candidate.path, "utf-8");
|
|
8159
9828
|
const { metadata, body } = parseRuleFrontmatter(rawContent);
|
|
8160
9829
|
const matchResult = shouldApplyRule(metadata, resolved, projectRoot);
|
|
8161
9830
|
if (!matchResult.applies)
|
|
@@ -8559,11 +10228,11 @@ v${latestVersion} available. Restart OpenCode to apply.` : `OpenCode is now on S
|
|
|
8559
10228
|
if (props?.info?.parentID)
|
|
8560
10229
|
return;
|
|
8561
10230
|
hasChecked = true;
|
|
8562
|
-
setTimeout(() => {
|
|
10231
|
+
setTimeout(async () => {
|
|
8563
10232
|
const cachedVersion = getCachedVersion();
|
|
8564
10233
|
const localDevVersion = getLocalDevVersion(ctx.directory);
|
|
8565
10234
|
const displayVersion = localDevVersion ?? cachedVersion;
|
|
8566
|
-
showConfigErrorsIfAny(ctx)
|
|
10235
|
+
await showConfigErrorsIfAny(ctx);
|
|
8567
10236
|
if (localDevVersion) {
|
|
8568
10237
|
if (showStartupToast) {
|
|
8569
10238
|
showLocalDevToast(ctx, displayVersion, isSisyphusEnabled).catch(() => {});
|
|
@@ -8693,18 +10362,18 @@ async function showLocalDevToast(ctx, version, isSisyphusEnabled) {
|
|
|
8693
10362
|
}
|
|
8694
10363
|
// src/hooks/agent-usage-reminder/storage.ts
|
|
8695
10364
|
import {
|
|
8696
|
-
existsSync as
|
|
10365
|
+
existsSync as existsSync28,
|
|
8697
10366
|
mkdirSync as mkdirSync8,
|
|
8698
|
-
readFileSync as
|
|
8699
|
-
writeFileSync as
|
|
10367
|
+
readFileSync as readFileSync18,
|
|
10368
|
+
writeFileSync as writeFileSync11,
|
|
8700
10369
|
unlinkSync as unlinkSync7
|
|
8701
10370
|
} from "fs";
|
|
8702
|
-
import { join as
|
|
10371
|
+
import { join as join37 } from "path";
|
|
8703
10372
|
|
|
8704
10373
|
// src/hooks/agent-usage-reminder/constants.ts
|
|
8705
|
-
import { join as
|
|
8706
|
-
var OPENCODE_STORAGE7 =
|
|
8707
|
-
var AGENT_USAGE_REMINDER_STORAGE =
|
|
10374
|
+
import { join as join36 } from "path";
|
|
10375
|
+
var OPENCODE_STORAGE7 = join36(xdgData ?? "", "opencode", "storage");
|
|
10376
|
+
var AGENT_USAGE_REMINDER_STORAGE = join36(OPENCODE_STORAGE7, "agent-usage-reminder");
|
|
8708
10377
|
var TARGET_TOOLS = new Set([
|
|
8709
10378
|
"grep",
|
|
8710
10379
|
"safe_grep",
|
|
@@ -8749,29 +10418,29 @@ ALWAYS prefer: Multiple parallel background_task calls > Direct tool calls
|
|
|
8749
10418
|
|
|
8750
10419
|
// src/hooks/agent-usage-reminder/storage.ts
|
|
8751
10420
|
function getStoragePath4(sessionID) {
|
|
8752
|
-
return
|
|
10421
|
+
return join37(AGENT_USAGE_REMINDER_STORAGE, `${sessionID}.json`);
|
|
8753
10422
|
}
|
|
8754
10423
|
function loadAgentUsageState(sessionID) {
|
|
8755
10424
|
const filePath = getStoragePath4(sessionID);
|
|
8756
|
-
if (!
|
|
10425
|
+
if (!existsSync28(filePath))
|
|
8757
10426
|
return null;
|
|
8758
10427
|
try {
|
|
8759
|
-
const content =
|
|
10428
|
+
const content = readFileSync18(filePath, "utf-8");
|
|
8760
10429
|
return JSON.parse(content);
|
|
8761
10430
|
} catch {
|
|
8762
10431
|
return null;
|
|
8763
10432
|
}
|
|
8764
10433
|
}
|
|
8765
10434
|
function saveAgentUsageState(state2) {
|
|
8766
|
-
if (!
|
|
10435
|
+
if (!existsSync28(AGENT_USAGE_REMINDER_STORAGE)) {
|
|
8767
10436
|
mkdirSync8(AGENT_USAGE_REMINDER_STORAGE, { recursive: true });
|
|
8768
10437
|
}
|
|
8769
10438
|
const filePath = getStoragePath4(state2.sessionID);
|
|
8770
|
-
|
|
10439
|
+
writeFileSync11(filePath, JSON.stringify(state2, null, 2));
|
|
8771
10440
|
}
|
|
8772
10441
|
function clearAgentUsageState(sessionID) {
|
|
8773
10442
|
const filePath = getStoragePath4(sessionID);
|
|
8774
|
-
if (
|
|
10443
|
+
if (existsSync28(filePath)) {
|
|
8775
10444
|
unlinkSync7(filePath);
|
|
8776
10445
|
}
|
|
8777
10446
|
}
|
|
@@ -8873,12 +10542,23 @@ TELL THE USER WHAT AGENTS YOU WILL LEVERAGE NOW TO SATISFY USER'S REQUEST.
|
|
|
8873
10542
|
3. Always Use Plan agent with gathered context to create detailed work breakdown
|
|
8874
10543
|
4. Execute with continuous verification against original requirements
|
|
8875
10544
|
|
|
10545
|
+
## TDD (if test infrastructure exists)
|
|
10546
|
+
|
|
10547
|
+
1. Write spec (requirements)
|
|
10548
|
+
2. Write tests (failing)
|
|
10549
|
+
3. RED: tests fail
|
|
10550
|
+
4. Implement minimal code
|
|
10551
|
+
5. GREEN: tests pass
|
|
10552
|
+
6. Refactor if needed (must stay green)
|
|
10553
|
+
7. Next feature, repeat
|
|
10554
|
+
|
|
8876
10555
|
## ZERO TOLERANCE FAILURES
|
|
8877
10556
|
- **NO Scope Reduction**: Never make "demo", "skeleton", "simplified", "basic" versions - deliver FULL implementation
|
|
8878
10557
|
- **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.
|
|
8879
10558
|
- **NO Partial Completion**: Never stop at 60-80% saying "you can extend this..." - finish 100%
|
|
8880
10559
|
- **NO Assumed Shortcuts**: Never skip requirements you deem "optional" or "can be added later"
|
|
8881
10560
|
- **NO Premature Stopping**: Never declare done until ALL TODOs are completed and verified
|
|
10561
|
+
- **NO TEST DELETION**: Never delete or skip failing tests to make the build pass. Fix the code, not the tests.
|
|
8882
10562
|
|
|
8883
10563
|
THE USER ASKED FOR X. DELIVER EXACTLY X. NOT A SUBSET. NOT A DEMO. NOT A STARTING POINT.
|
|
8884
10564
|
|
|
@@ -9066,18 +10746,18 @@ function createNonInteractiveEnvHook(_ctx) {
|
|
|
9066
10746
|
}
|
|
9067
10747
|
// src/hooks/interactive-bash-session/storage.ts
|
|
9068
10748
|
import {
|
|
9069
|
-
existsSync as
|
|
10749
|
+
existsSync as existsSync29,
|
|
9070
10750
|
mkdirSync as mkdirSync9,
|
|
9071
|
-
readFileSync as
|
|
9072
|
-
writeFileSync as
|
|
10751
|
+
readFileSync as readFileSync19,
|
|
10752
|
+
writeFileSync as writeFileSync12,
|
|
9073
10753
|
unlinkSync as unlinkSync8
|
|
9074
10754
|
} from "fs";
|
|
9075
|
-
import { join as
|
|
10755
|
+
import { join as join39 } from "path";
|
|
9076
10756
|
|
|
9077
10757
|
// src/hooks/interactive-bash-session/constants.ts
|
|
9078
|
-
import { join as
|
|
9079
|
-
var OPENCODE_STORAGE8 =
|
|
9080
|
-
var INTERACTIVE_BASH_SESSION_STORAGE =
|
|
10758
|
+
import { join as join38 } from "path";
|
|
10759
|
+
var OPENCODE_STORAGE8 = join38(xdgData ?? "", "opencode", "storage");
|
|
10760
|
+
var INTERACTIVE_BASH_SESSION_STORAGE = join38(OPENCODE_STORAGE8, "interactive-bash-session");
|
|
9081
10761
|
var OMO_SESSION_PREFIX = "omo-";
|
|
9082
10762
|
function buildSessionReminderMessage(sessions) {
|
|
9083
10763
|
if (sessions.length === 0)
|
|
@@ -9089,14 +10769,14 @@ function buildSessionReminderMessage(sessions) {
|
|
|
9089
10769
|
|
|
9090
10770
|
// src/hooks/interactive-bash-session/storage.ts
|
|
9091
10771
|
function getStoragePath5(sessionID) {
|
|
9092
|
-
return
|
|
10772
|
+
return join39(INTERACTIVE_BASH_SESSION_STORAGE, `${sessionID}.json`);
|
|
9093
10773
|
}
|
|
9094
10774
|
function loadInteractiveBashSessionState(sessionID) {
|
|
9095
10775
|
const filePath = getStoragePath5(sessionID);
|
|
9096
|
-
if (!
|
|
10776
|
+
if (!existsSync29(filePath))
|
|
9097
10777
|
return null;
|
|
9098
10778
|
try {
|
|
9099
|
-
const content =
|
|
10779
|
+
const content = readFileSync19(filePath, "utf-8");
|
|
9100
10780
|
const serialized = JSON.parse(content);
|
|
9101
10781
|
return {
|
|
9102
10782
|
sessionID: serialized.sessionID,
|
|
@@ -9108,7 +10788,7 @@ function loadInteractiveBashSessionState(sessionID) {
|
|
|
9108
10788
|
}
|
|
9109
10789
|
}
|
|
9110
10790
|
function saveInteractiveBashSessionState(state2) {
|
|
9111
|
-
if (!
|
|
10791
|
+
if (!existsSync29(INTERACTIVE_BASH_SESSION_STORAGE)) {
|
|
9112
10792
|
mkdirSync9(INTERACTIVE_BASH_SESSION_STORAGE, { recursive: true });
|
|
9113
10793
|
}
|
|
9114
10794
|
const filePath = getStoragePath5(state2.sessionID);
|
|
@@ -9117,11 +10797,11 @@ function saveInteractiveBashSessionState(state2) {
|
|
|
9117
10797
|
tmuxSessions: Array.from(state2.tmuxSessions),
|
|
9118
10798
|
updatedAt: state2.updatedAt
|
|
9119
10799
|
};
|
|
9120
|
-
|
|
10800
|
+
writeFileSync12(filePath, JSON.stringify(serialized, null, 2));
|
|
9121
10801
|
}
|
|
9122
10802
|
function clearInteractiveBashSessionState(sessionID) {
|
|
9123
10803
|
const filePath = getStoragePath5(sessionID);
|
|
9124
|
-
if (
|
|
10804
|
+
if (existsSync29(filePath)) {
|
|
9125
10805
|
unlinkSync8(filePath);
|
|
9126
10806
|
}
|
|
9127
10807
|
}
|
|
@@ -9365,6 +11045,89 @@ function createEmptyMessageSanitizerHook() {
|
|
|
9365
11045
|
}
|
|
9366
11046
|
};
|
|
9367
11047
|
}
|
|
11048
|
+
// src/hooks/thinking-block-validator/index.ts
|
|
11049
|
+
function isExtendedThinkingModel(modelID) {
|
|
11050
|
+
if (!modelID)
|
|
11051
|
+
return false;
|
|
11052
|
+
const lower = modelID.toLowerCase();
|
|
11053
|
+
if (lower.includes("thinking") || lower.endsWith("-high")) {
|
|
11054
|
+
return true;
|
|
11055
|
+
}
|
|
11056
|
+
return lower.includes("claude-sonnet-4") || lower.includes("claude-opus-4") || lower.includes("claude-3");
|
|
11057
|
+
}
|
|
11058
|
+
function hasToolParts(parts) {
|
|
11059
|
+
if (!parts || parts.length === 0)
|
|
11060
|
+
return false;
|
|
11061
|
+
return parts.some((part) => {
|
|
11062
|
+
const type = part.type;
|
|
11063
|
+
return type === "tool" || type === "tool_use";
|
|
11064
|
+
});
|
|
11065
|
+
}
|
|
11066
|
+
function startsWithThinkingBlock(parts) {
|
|
11067
|
+
if (!parts || parts.length === 0)
|
|
11068
|
+
return false;
|
|
11069
|
+
const firstPart = parts[0];
|
|
11070
|
+
const type = firstPart.type;
|
|
11071
|
+
return type === "thinking" || type === "reasoning";
|
|
11072
|
+
}
|
|
11073
|
+
function findPreviousThinkingContent(messages, currentIndex) {
|
|
11074
|
+
for (let i = currentIndex - 1;i >= 0; i--) {
|
|
11075
|
+
const msg = messages[i];
|
|
11076
|
+
if (msg.info.role !== "assistant")
|
|
11077
|
+
continue;
|
|
11078
|
+
if (!msg.parts)
|
|
11079
|
+
continue;
|
|
11080
|
+
for (const part of msg.parts) {
|
|
11081
|
+
const type = part.type;
|
|
11082
|
+
if (type === "thinking" || type === "reasoning") {
|
|
11083
|
+
const thinking = part.thinking || part.text;
|
|
11084
|
+
if (thinking && typeof thinking === "string" && thinking.trim().length > 0) {
|
|
11085
|
+
return thinking;
|
|
11086
|
+
}
|
|
11087
|
+
}
|
|
11088
|
+
}
|
|
11089
|
+
}
|
|
11090
|
+
return "";
|
|
11091
|
+
}
|
|
11092
|
+
function prependThinkingBlock(message, thinkingContent) {
|
|
11093
|
+
if (!message.parts) {
|
|
11094
|
+
message.parts = [];
|
|
11095
|
+
}
|
|
11096
|
+
const thinkingPart = {
|
|
11097
|
+
type: "thinking",
|
|
11098
|
+
id: `prt_0000000000_synthetic_thinking`,
|
|
11099
|
+
sessionID: message.info.sessionID || "",
|
|
11100
|
+
messageID: message.info.id,
|
|
11101
|
+
thinking: thinkingContent,
|
|
11102
|
+
synthetic: true
|
|
11103
|
+
};
|
|
11104
|
+
message.parts.unshift(thinkingPart);
|
|
11105
|
+
}
|
|
11106
|
+
function createThinkingBlockValidatorHook() {
|
|
11107
|
+
return {
|
|
11108
|
+
"experimental.chat.messages.transform": async (_input, output) => {
|
|
11109
|
+
const { messages } = output;
|
|
11110
|
+
if (!messages || messages.length === 0) {
|
|
11111
|
+
return;
|
|
11112
|
+
}
|
|
11113
|
+
const lastUserMessage = messages.findLast((m) => m.info.role === "user");
|
|
11114
|
+
const modelID = lastUserMessage?.info?.modelID || "";
|
|
11115
|
+
if (!isExtendedThinkingModel(modelID)) {
|
|
11116
|
+
return;
|
|
11117
|
+
}
|
|
11118
|
+
for (let i = 0;i < messages.length; i++) {
|
|
11119
|
+
const msg = messages[i];
|
|
11120
|
+
if (msg.info.role !== "assistant")
|
|
11121
|
+
continue;
|
|
11122
|
+
if (hasToolParts(msg.parts) && !startsWithThinkingBlock(msg.parts)) {
|
|
11123
|
+
const previousThinking = findPreviousThinkingContent(messages, i);
|
|
11124
|
+
const thinkingContent = previousThinking || "[Continuing from previous reasoning]";
|
|
11125
|
+
prependThinkingBlock(msg, thinkingContent);
|
|
11126
|
+
}
|
|
11127
|
+
}
|
|
11128
|
+
}
|
|
11129
|
+
};
|
|
11130
|
+
}
|
|
9368
11131
|
// src/auth/antigravity/constants.ts
|
|
9369
11132
|
var ANTIGRAVITY_CLIENT_ID = "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com";
|
|
9370
11133
|
var ANTIGRAVITY_CLIENT_SECRET = "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";
|
|
@@ -11065,22 +12828,21 @@ async function createGoogleAntigravityAuthPlugin({
|
|
|
11065
12828
|
};
|
|
11066
12829
|
}
|
|
11067
12830
|
// src/features/claude-code-command-loader/loader.ts
|
|
11068
|
-
import { existsSync as
|
|
11069
|
-
import {
|
|
11070
|
-
import { join as join35, basename } from "path";
|
|
12831
|
+
import { existsSync as existsSync30, readdirSync as readdirSync11, readFileSync as readFileSync20 } from "fs";
|
|
12832
|
+
import { join as join40, basename } from "path";
|
|
11071
12833
|
function loadCommandsFromDir(commandsDir, scope) {
|
|
11072
|
-
if (!
|
|
12834
|
+
if (!existsSync30(commandsDir)) {
|
|
11073
12835
|
return [];
|
|
11074
12836
|
}
|
|
11075
|
-
const entries =
|
|
12837
|
+
const entries = readdirSync11(commandsDir, { withFileTypes: true });
|
|
11076
12838
|
const commands = [];
|
|
11077
12839
|
for (const entry of entries) {
|
|
11078
12840
|
if (!isMarkdownFile(entry))
|
|
11079
12841
|
continue;
|
|
11080
|
-
const commandPath =
|
|
12842
|
+
const commandPath = join40(commandsDir, entry.name);
|
|
11081
12843
|
const commandName = basename(entry.name, ".md");
|
|
11082
12844
|
try {
|
|
11083
|
-
const content =
|
|
12845
|
+
const content = readFileSync20(commandPath, "utf-8");
|
|
11084
12846
|
const { data, body } = parseFrontmatter(content);
|
|
11085
12847
|
const wrappedTemplate = `<command-instruction>
|
|
11086
12848
|
${body.trim()}
|
|
@@ -11120,99 +12882,29 @@ function commandsToRecord(commands) {
|
|
|
11120
12882
|
return result;
|
|
11121
12883
|
}
|
|
11122
12884
|
function loadUserCommands() {
|
|
11123
|
-
const userCommandsDir =
|
|
12885
|
+
const userCommandsDir = join40(getClaudeConfigDir(), "commands");
|
|
11124
12886
|
const commands = loadCommandsFromDir(userCommandsDir, "user");
|
|
11125
12887
|
return commandsToRecord(commands);
|
|
11126
12888
|
}
|
|
11127
12889
|
function loadProjectCommands() {
|
|
11128
|
-
const projectCommandsDir =
|
|
12890
|
+
const projectCommandsDir = join40(process.cwd(), ".claude", "commands");
|
|
11129
12891
|
const commands = loadCommandsFromDir(projectCommandsDir, "project");
|
|
11130
12892
|
return commandsToRecord(commands);
|
|
11131
12893
|
}
|
|
11132
12894
|
function loadOpencodeGlobalCommands() {
|
|
11133
|
-
const
|
|
12895
|
+
const { homedir: homedir10 } = __require("os");
|
|
12896
|
+
const opencodeCommandsDir = join40(homedir10(), ".config", "opencode", "command");
|
|
11134
12897
|
const commands = loadCommandsFromDir(opencodeCommandsDir, "opencode");
|
|
11135
12898
|
return commandsToRecord(commands);
|
|
11136
12899
|
}
|
|
11137
12900
|
function loadOpencodeProjectCommands() {
|
|
11138
|
-
const opencodeProjectDir =
|
|
12901
|
+
const opencodeProjectDir = join40(process.cwd(), ".opencode", "command");
|
|
11139
12902
|
const commands = loadCommandsFromDir(opencodeProjectDir, "opencode-project");
|
|
11140
12903
|
return commandsToRecord(commands);
|
|
11141
12904
|
}
|
|
11142
|
-
// src/features/claude-code-skill-loader/loader.ts
|
|
11143
|
-
import { existsSync as existsSync26, readdirSync as readdirSync8, readFileSync as readFileSync16 } from "fs";
|
|
11144
|
-
import { homedir as homedir13 } from "os";
|
|
11145
|
-
import { join as join36 } from "path";
|
|
11146
|
-
function loadSkillsFromDir(skillsDir, scope) {
|
|
11147
|
-
if (!existsSync26(skillsDir)) {
|
|
11148
|
-
return [];
|
|
11149
|
-
}
|
|
11150
|
-
const entries = readdirSync8(skillsDir, { withFileTypes: true });
|
|
11151
|
-
const skills = [];
|
|
11152
|
-
for (const entry of entries) {
|
|
11153
|
-
if (entry.name.startsWith("."))
|
|
11154
|
-
continue;
|
|
11155
|
-
const skillPath = join36(skillsDir, entry.name);
|
|
11156
|
-
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
11157
|
-
continue;
|
|
11158
|
-
const resolvedPath = resolveSymlink(skillPath);
|
|
11159
|
-
const skillMdPath = join36(resolvedPath, "SKILL.md");
|
|
11160
|
-
if (!existsSync26(skillMdPath))
|
|
11161
|
-
continue;
|
|
11162
|
-
try {
|
|
11163
|
-
const content = readFileSync16(skillMdPath, "utf-8");
|
|
11164
|
-
const { data, body } = parseFrontmatter(content);
|
|
11165
|
-
const skillName = data.name || entry.name;
|
|
11166
|
-
const originalDescription = data.description || "";
|
|
11167
|
-
const formattedDescription = `(${scope} - Skill) ${originalDescription}`;
|
|
11168
|
-
const wrappedTemplate = `<skill-instruction>
|
|
11169
|
-
Base directory for this skill: ${resolvedPath}/
|
|
11170
|
-
File references (@path) in this skill are relative to this directory.
|
|
11171
|
-
|
|
11172
|
-
${body.trim()}
|
|
11173
|
-
</skill-instruction>
|
|
11174
|
-
|
|
11175
|
-
<user-request>
|
|
11176
|
-
$ARGUMENTS
|
|
11177
|
-
</user-request>`;
|
|
11178
|
-
const definition = {
|
|
11179
|
-
name: skillName,
|
|
11180
|
-
description: formattedDescription,
|
|
11181
|
-
template: wrappedTemplate,
|
|
11182
|
-
model: sanitizeModelField(data.model)
|
|
11183
|
-
};
|
|
11184
|
-
skills.push({
|
|
11185
|
-
name: skillName,
|
|
11186
|
-
path: resolvedPath,
|
|
11187
|
-
definition,
|
|
11188
|
-
scope
|
|
11189
|
-
});
|
|
11190
|
-
} catch {
|
|
11191
|
-
continue;
|
|
11192
|
-
}
|
|
11193
|
-
}
|
|
11194
|
-
return skills;
|
|
11195
|
-
}
|
|
11196
|
-
function loadUserSkillsAsCommands() {
|
|
11197
|
-
const userSkillsDir = join36(homedir13(), ".claude", "skills");
|
|
11198
|
-
const skills = loadSkillsFromDir(userSkillsDir, "user");
|
|
11199
|
-
return skills.reduce((acc, skill) => {
|
|
11200
|
-
acc[skill.name] = skill.definition;
|
|
11201
|
-
return acc;
|
|
11202
|
-
}, {});
|
|
11203
|
-
}
|
|
11204
|
-
function loadProjectSkillsAsCommands() {
|
|
11205
|
-
const projectSkillsDir = join36(process.cwd(), ".claude", "skills");
|
|
11206
|
-
const skills = loadSkillsFromDir(projectSkillsDir, "project");
|
|
11207
|
-
return skills.reduce((acc, skill) => {
|
|
11208
|
-
acc[skill.name] = skill.definition;
|
|
11209
|
-
return acc;
|
|
11210
|
-
}, {});
|
|
11211
|
-
}
|
|
11212
12905
|
// src/features/claude-code-agent-loader/loader.ts
|
|
11213
|
-
import { existsSync as
|
|
11214
|
-
import {
|
|
11215
|
-
import { join as join37, basename as basename2 } from "path";
|
|
12906
|
+
import { existsSync as existsSync31, readdirSync as readdirSync12, readFileSync as readFileSync21 } from "fs";
|
|
12907
|
+
import { join as join41, basename as basename2 } from "path";
|
|
11216
12908
|
function parseToolsConfig(toolsStr) {
|
|
11217
12909
|
if (!toolsStr)
|
|
11218
12910
|
return;
|
|
@@ -11226,18 +12918,18 @@ function parseToolsConfig(toolsStr) {
|
|
|
11226
12918
|
return result;
|
|
11227
12919
|
}
|
|
11228
12920
|
function loadAgentsFromDir(agentsDir, scope) {
|
|
11229
|
-
if (!
|
|
12921
|
+
if (!existsSync31(agentsDir)) {
|
|
11230
12922
|
return [];
|
|
11231
12923
|
}
|
|
11232
|
-
const entries =
|
|
12924
|
+
const entries = readdirSync12(agentsDir, { withFileTypes: true });
|
|
11233
12925
|
const agents = [];
|
|
11234
12926
|
for (const entry of entries) {
|
|
11235
12927
|
if (!isMarkdownFile(entry))
|
|
11236
12928
|
continue;
|
|
11237
|
-
const agentPath =
|
|
12929
|
+
const agentPath = join41(agentsDir, entry.name);
|
|
11238
12930
|
const agentName = basename2(entry.name, ".md");
|
|
11239
12931
|
try {
|
|
11240
|
-
const content =
|
|
12932
|
+
const content = readFileSync21(agentPath, "utf-8");
|
|
11241
12933
|
const { data, body } = parseFrontmatter(content);
|
|
11242
12934
|
const name = data.name || agentName;
|
|
11243
12935
|
const originalDescription = data.description || "";
|
|
@@ -11264,7 +12956,7 @@ function loadAgentsFromDir(agentsDir, scope) {
|
|
|
11264
12956
|
return agents;
|
|
11265
12957
|
}
|
|
11266
12958
|
function loadUserAgents() {
|
|
11267
|
-
const userAgentsDir =
|
|
12959
|
+
const userAgentsDir = join41(getClaudeConfigDir(), "agents");
|
|
11268
12960
|
const agents = loadAgentsFromDir(userAgentsDir, "user");
|
|
11269
12961
|
const result = {};
|
|
11270
12962
|
for (const agent of agents) {
|
|
@@ -11273,7 +12965,7 @@ function loadUserAgents() {
|
|
|
11273
12965
|
return result;
|
|
11274
12966
|
}
|
|
11275
12967
|
function loadProjectAgents() {
|
|
11276
|
-
const projectAgentsDir =
|
|
12968
|
+
const projectAgentsDir = join41(process.cwd(), ".claude", "agents");
|
|
11277
12969
|
const agents = loadAgentsFromDir(projectAgentsDir, "project");
|
|
11278
12970
|
const result = {};
|
|
11279
12971
|
for (const agent of agents) {
|
|
@@ -11282,9 +12974,8 @@ function loadProjectAgents() {
|
|
|
11282
12974
|
return result;
|
|
11283
12975
|
}
|
|
11284
12976
|
// src/features/claude-code-mcp-loader/loader.ts
|
|
11285
|
-
import { existsSync as
|
|
11286
|
-
import {
|
|
11287
|
-
import { join as join38 } from "path";
|
|
12977
|
+
import { existsSync as existsSync32 } from "fs";
|
|
12978
|
+
import { join as join42 } from "path";
|
|
11288
12979
|
|
|
11289
12980
|
// src/features/claude-code-mcp-loader/env-expander.ts
|
|
11290
12981
|
function expandEnvVars(value) {
|
|
@@ -11350,16 +13041,16 @@ function transformMcpServer(name, server) {
|
|
|
11350
13041
|
|
|
11351
13042
|
// src/features/claude-code-mcp-loader/loader.ts
|
|
11352
13043
|
function getMcpConfigPaths() {
|
|
11353
|
-
const
|
|
13044
|
+
const claudeConfigDir = getClaudeConfigDir();
|
|
11354
13045
|
const cwd = process.cwd();
|
|
11355
13046
|
return [
|
|
11356
|
-
{ path:
|
|
11357
|
-
{ path:
|
|
11358
|
-
{ path:
|
|
13047
|
+
{ path: join42(claudeConfigDir, ".mcp.json"), scope: "user" },
|
|
13048
|
+
{ path: join42(cwd, ".mcp.json"), scope: "project" },
|
|
13049
|
+
{ path: join42(cwd, ".claude", ".mcp.json"), scope: "local" }
|
|
11359
13050
|
];
|
|
11360
13051
|
}
|
|
11361
13052
|
async function loadMcpConfigFile(filePath) {
|
|
11362
|
-
if (!
|
|
13053
|
+
if (!existsSync32(filePath)) {
|
|
11363
13054
|
return null;
|
|
11364
13055
|
}
|
|
11365
13056
|
try {
|
|
@@ -11399,6 +13090,363 @@ async function loadMcpConfigs() {
|
|
|
11399
13090
|
}
|
|
11400
13091
|
return { servers, loadedServers };
|
|
11401
13092
|
}
|
|
13093
|
+
// src/features/claude-code-plugin-loader/loader.ts
|
|
13094
|
+
import { existsSync as existsSync33, readdirSync as readdirSync13, readFileSync as readFileSync22 } from "fs";
|
|
13095
|
+
import { homedir as homedir10 } from "os";
|
|
13096
|
+
import { join as join43, basename as basename3 } from "path";
|
|
13097
|
+
var CLAUDE_PLUGIN_ROOT_VAR = "${CLAUDE_PLUGIN_ROOT}";
|
|
13098
|
+
function getPluginsBaseDir() {
|
|
13099
|
+
if (process.env.CLAUDE_PLUGINS_HOME) {
|
|
13100
|
+
return process.env.CLAUDE_PLUGINS_HOME;
|
|
13101
|
+
}
|
|
13102
|
+
return join43(homedir10(), ".claude", "plugins");
|
|
13103
|
+
}
|
|
13104
|
+
function getInstalledPluginsPath() {
|
|
13105
|
+
return join43(getPluginsBaseDir(), "installed_plugins.json");
|
|
13106
|
+
}
|
|
13107
|
+
function resolvePluginPath(path8, pluginRoot) {
|
|
13108
|
+
return path8.replace(CLAUDE_PLUGIN_ROOT_VAR, pluginRoot);
|
|
13109
|
+
}
|
|
13110
|
+
function resolvePluginPaths(obj, pluginRoot) {
|
|
13111
|
+
if (obj === null || obj === undefined)
|
|
13112
|
+
return obj;
|
|
13113
|
+
if (typeof obj === "string") {
|
|
13114
|
+
return resolvePluginPath(obj, pluginRoot);
|
|
13115
|
+
}
|
|
13116
|
+
if (Array.isArray(obj)) {
|
|
13117
|
+
return obj.map((item) => resolvePluginPaths(item, pluginRoot));
|
|
13118
|
+
}
|
|
13119
|
+
if (typeof obj === "object") {
|
|
13120
|
+
const result = {};
|
|
13121
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
13122
|
+
result[key] = resolvePluginPaths(value, pluginRoot);
|
|
13123
|
+
}
|
|
13124
|
+
return result;
|
|
13125
|
+
}
|
|
13126
|
+
return obj;
|
|
13127
|
+
}
|
|
13128
|
+
function loadInstalledPlugins() {
|
|
13129
|
+
const dbPath = getInstalledPluginsPath();
|
|
13130
|
+
if (!existsSync33(dbPath)) {
|
|
13131
|
+
return null;
|
|
13132
|
+
}
|
|
13133
|
+
try {
|
|
13134
|
+
const content = readFileSync22(dbPath, "utf-8");
|
|
13135
|
+
return JSON.parse(content);
|
|
13136
|
+
} catch (error) {
|
|
13137
|
+
log("Failed to load installed plugins database", error);
|
|
13138
|
+
return null;
|
|
13139
|
+
}
|
|
13140
|
+
}
|
|
13141
|
+
function getClaudeSettingsPath() {
|
|
13142
|
+
if (process.env.CLAUDE_SETTINGS_PATH) {
|
|
13143
|
+
return process.env.CLAUDE_SETTINGS_PATH;
|
|
13144
|
+
}
|
|
13145
|
+
return join43(homedir10(), ".claude", "settings.json");
|
|
13146
|
+
}
|
|
13147
|
+
function loadClaudeSettings() {
|
|
13148
|
+
const settingsPath = getClaudeSettingsPath();
|
|
13149
|
+
if (!existsSync33(settingsPath)) {
|
|
13150
|
+
return null;
|
|
13151
|
+
}
|
|
13152
|
+
try {
|
|
13153
|
+
const content = readFileSync22(settingsPath, "utf-8");
|
|
13154
|
+
return JSON.parse(content);
|
|
13155
|
+
} catch (error) {
|
|
13156
|
+
log("Failed to load Claude settings", error);
|
|
13157
|
+
return null;
|
|
13158
|
+
}
|
|
13159
|
+
}
|
|
13160
|
+
function loadPluginManifest(installPath) {
|
|
13161
|
+
const manifestPath = join43(installPath, ".claude-plugin", "plugin.json");
|
|
13162
|
+
if (!existsSync33(manifestPath)) {
|
|
13163
|
+
return null;
|
|
13164
|
+
}
|
|
13165
|
+
try {
|
|
13166
|
+
const content = readFileSync22(manifestPath, "utf-8");
|
|
13167
|
+
return JSON.parse(content);
|
|
13168
|
+
} catch (error) {
|
|
13169
|
+
log(`Failed to load plugin manifest from ${manifestPath}`, error);
|
|
13170
|
+
return null;
|
|
13171
|
+
}
|
|
13172
|
+
}
|
|
13173
|
+
function derivePluginNameFromKey(pluginKey) {
|
|
13174
|
+
const atIndex = pluginKey.indexOf("@");
|
|
13175
|
+
if (atIndex > 0) {
|
|
13176
|
+
return pluginKey.substring(0, atIndex);
|
|
13177
|
+
}
|
|
13178
|
+
return pluginKey;
|
|
13179
|
+
}
|
|
13180
|
+
function isPluginEnabled(pluginKey, settingsEnabledPlugins, overrideEnabledPlugins) {
|
|
13181
|
+
if (overrideEnabledPlugins && pluginKey in overrideEnabledPlugins) {
|
|
13182
|
+
return overrideEnabledPlugins[pluginKey];
|
|
13183
|
+
}
|
|
13184
|
+
if (settingsEnabledPlugins && pluginKey in settingsEnabledPlugins) {
|
|
13185
|
+
return settingsEnabledPlugins[pluginKey];
|
|
13186
|
+
}
|
|
13187
|
+
return true;
|
|
13188
|
+
}
|
|
13189
|
+
function discoverInstalledPlugins(options) {
|
|
13190
|
+
const db = loadInstalledPlugins();
|
|
13191
|
+
const settings = loadClaudeSettings();
|
|
13192
|
+
const plugins = [];
|
|
13193
|
+
const errors = [];
|
|
13194
|
+
if (!db || !db.plugins) {
|
|
13195
|
+
return { plugins, errors };
|
|
13196
|
+
}
|
|
13197
|
+
const settingsEnabledPlugins = settings?.enabledPlugins;
|
|
13198
|
+
const overrideEnabledPlugins = options?.enabledPluginsOverride;
|
|
13199
|
+
for (const [pluginKey, installations] of Object.entries(db.plugins)) {
|
|
13200
|
+
if (!installations || installations.length === 0)
|
|
13201
|
+
continue;
|
|
13202
|
+
if (!isPluginEnabled(pluginKey, settingsEnabledPlugins, overrideEnabledPlugins)) {
|
|
13203
|
+
log(`Plugin disabled: ${pluginKey}`);
|
|
13204
|
+
continue;
|
|
13205
|
+
}
|
|
13206
|
+
const installation = installations[0];
|
|
13207
|
+
const { installPath, scope, version } = installation;
|
|
13208
|
+
if (!existsSync33(installPath)) {
|
|
13209
|
+
errors.push({
|
|
13210
|
+
pluginKey,
|
|
13211
|
+
installPath,
|
|
13212
|
+
error: "Plugin installation path does not exist"
|
|
13213
|
+
});
|
|
13214
|
+
continue;
|
|
13215
|
+
}
|
|
13216
|
+
const manifest = loadPluginManifest(installPath);
|
|
13217
|
+
const pluginName = manifest?.name || derivePluginNameFromKey(pluginKey);
|
|
13218
|
+
const loadedPlugin = {
|
|
13219
|
+
name: pluginName,
|
|
13220
|
+
version: version || manifest?.version || "unknown",
|
|
13221
|
+
scope,
|
|
13222
|
+
installPath,
|
|
13223
|
+
pluginKey,
|
|
13224
|
+
manifest: manifest ?? undefined
|
|
13225
|
+
};
|
|
13226
|
+
if (existsSync33(join43(installPath, "commands"))) {
|
|
13227
|
+
loadedPlugin.commandsDir = join43(installPath, "commands");
|
|
13228
|
+
}
|
|
13229
|
+
if (existsSync33(join43(installPath, "agents"))) {
|
|
13230
|
+
loadedPlugin.agentsDir = join43(installPath, "agents");
|
|
13231
|
+
}
|
|
13232
|
+
if (existsSync33(join43(installPath, "skills"))) {
|
|
13233
|
+
loadedPlugin.skillsDir = join43(installPath, "skills");
|
|
13234
|
+
}
|
|
13235
|
+
const hooksPath = join43(installPath, "hooks", "hooks.json");
|
|
13236
|
+
if (existsSync33(hooksPath)) {
|
|
13237
|
+
loadedPlugin.hooksPath = hooksPath;
|
|
13238
|
+
}
|
|
13239
|
+
const mcpPath = join43(installPath, ".mcp.json");
|
|
13240
|
+
if (existsSync33(mcpPath)) {
|
|
13241
|
+
loadedPlugin.mcpPath = mcpPath;
|
|
13242
|
+
}
|
|
13243
|
+
plugins.push(loadedPlugin);
|
|
13244
|
+
log(`Discovered plugin: ${pluginName}@${version} (${scope})`, { installPath, hasManifest: !!manifest });
|
|
13245
|
+
}
|
|
13246
|
+
return { plugins, errors };
|
|
13247
|
+
}
|
|
13248
|
+
function loadPluginCommands(plugins) {
|
|
13249
|
+
const commands = {};
|
|
13250
|
+
for (const plugin2 of plugins) {
|
|
13251
|
+
if (!plugin2.commandsDir || !existsSync33(plugin2.commandsDir))
|
|
13252
|
+
continue;
|
|
13253
|
+
const entries = readdirSync13(plugin2.commandsDir, { withFileTypes: true });
|
|
13254
|
+
for (const entry of entries) {
|
|
13255
|
+
if (!isMarkdownFile(entry))
|
|
13256
|
+
continue;
|
|
13257
|
+
const commandPath = join43(plugin2.commandsDir, entry.name);
|
|
13258
|
+
const commandName = basename3(entry.name, ".md");
|
|
13259
|
+
const namespacedName = `${plugin2.name}:${commandName}`;
|
|
13260
|
+
try {
|
|
13261
|
+
const content = readFileSync22(commandPath, "utf-8");
|
|
13262
|
+
const { data, body } = parseFrontmatter(content);
|
|
13263
|
+
const wrappedTemplate = `<command-instruction>
|
|
13264
|
+
${body.trim()}
|
|
13265
|
+
</command-instruction>
|
|
13266
|
+
|
|
13267
|
+
<user-request>
|
|
13268
|
+
$ARGUMENTS
|
|
13269
|
+
</user-request>`;
|
|
13270
|
+
const formattedDescription = `(plugin: ${plugin2.name}) ${data.description || ""}`;
|
|
13271
|
+
commands[namespacedName] = {
|
|
13272
|
+
name: namespacedName,
|
|
13273
|
+
description: formattedDescription,
|
|
13274
|
+
template: wrappedTemplate,
|
|
13275
|
+
agent: data.agent,
|
|
13276
|
+
model: sanitizeModelField(data.model, "claude-code"),
|
|
13277
|
+
subtask: data.subtask,
|
|
13278
|
+
argumentHint: data["argument-hint"]
|
|
13279
|
+
};
|
|
13280
|
+
log(`Loaded plugin command: ${namespacedName}`, { path: commandPath });
|
|
13281
|
+
} catch (error) {
|
|
13282
|
+
log(`Failed to load plugin command: ${commandPath}`, error);
|
|
13283
|
+
}
|
|
13284
|
+
}
|
|
13285
|
+
}
|
|
13286
|
+
return commands;
|
|
13287
|
+
}
|
|
13288
|
+
function loadPluginSkillsAsCommands(plugins) {
|
|
13289
|
+
const skills = {};
|
|
13290
|
+
for (const plugin2 of plugins) {
|
|
13291
|
+
if (!plugin2.skillsDir || !existsSync33(plugin2.skillsDir))
|
|
13292
|
+
continue;
|
|
13293
|
+
const entries = readdirSync13(plugin2.skillsDir, { withFileTypes: true });
|
|
13294
|
+
for (const entry of entries) {
|
|
13295
|
+
if (entry.name.startsWith("."))
|
|
13296
|
+
continue;
|
|
13297
|
+
const skillPath = join43(plugin2.skillsDir, entry.name);
|
|
13298
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
13299
|
+
continue;
|
|
13300
|
+
const resolvedPath = resolveSymlink(skillPath);
|
|
13301
|
+
const skillMdPath = join43(resolvedPath, "SKILL.md");
|
|
13302
|
+
if (!existsSync33(skillMdPath))
|
|
13303
|
+
continue;
|
|
13304
|
+
try {
|
|
13305
|
+
const content = readFileSync22(skillMdPath, "utf-8");
|
|
13306
|
+
const { data, body } = parseFrontmatter(content);
|
|
13307
|
+
const skillName = data.name || entry.name;
|
|
13308
|
+
const namespacedName = `${plugin2.name}:${skillName}`;
|
|
13309
|
+
const originalDescription = data.description || "";
|
|
13310
|
+
const formattedDescription = `(plugin: ${plugin2.name} - Skill) ${originalDescription}`;
|
|
13311
|
+
const wrappedTemplate = `<skill-instruction>
|
|
13312
|
+
Base directory for this skill: ${resolvedPath}/
|
|
13313
|
+
File references (@path) in this skill are relative to this directory.
|
|
13314
|
+
|
|
13315
|
+
${body.trim()}
|
|
13316
|
+
</skill-instruction>
|
|
13317
|
+
|
|
13318
|
+
<user-request>
|
|
13319
|
+
$ARGUMENTS
|
|
13320
|
+
</user-request>`;
|
|
13321
|
+
skills[namespacedName] = {
|
|
13322
|
+
name: namespacedName,
|
|
13323
|
+
description: formattedDescription,
|
|
13324
|
+
template: wrappedTemplate,
|
|
13325
|
+
model: sanitizeModelField(data.model)
|
|
13326
|
+
};
|
|
13327
|
+
log(`Loaded plugin skill: ${namespacedName}`, { path: resolvedPath });
|
|
13328
|
+
} catch (error) {
|
|
13329
|
+
log(`Failed to load plugin skill: ${skillPath}`, error);
|
|
13330
|
+
}
|
|
13331
|
+
}
|
|
13332
|
+
}
|
|
13333
|
+
return skills;
|
|
13334
|
+
}
|
|
13335
|
+
function parseToolsConfig2(toolsStr) {
|
|
13336
|
+
if (!toolsStr)
|
|
13337
|
+
return;
|
|
13338
|
+
const tools2 = toolsStr.split(",").map((t) => t.trim()).filter(Boolean);
|
|
13339
|
+
if (tools2.length === 0)
|
|
13340
|
+
return;
|
|
13341
|
+
const result = {};
|
|
13342
|
+
for (const tool of tools2) {
|
|
13343
|
+
result[tool.toLowerCase()] = true;
|
|
13344
|
+
}
|
|
13345
|
+
return result;
|
|
13346
|
+
}
|
|
13347
|
+
function loadPluginAgents(plugins) {
|
|
13348
|
+
const agents = {};
|
|
13349
|
+
for (const plugin2 of plugins) {
|
|
13350
|
+
if (!plugin2.agentsDir || !existsSync33(plugin2.agentsDir))
|
|
13351
|
+
continue;
|
|
13352
|
+
const entries = readdirSync13(plugin2.agentsDir, { withFileTypes: true });
|
|
13353
|
+
for (const entry of entries) {
|
|
13354
|
+
if (!isMarkdownFile(entry))
|
|
13355
|
+
continue;
|
|
13356
|
+
const agentPath = join43(plugin2.agentsDir, entry.name);
|
|
13357
|
+
const agentName = basename3(entry.name, ".md");
|
|
13358
|
+
const namespacedName = `${plugin2.name}:${agentName}`;
|
|
13359
|
+
try {
|
|
13360
|
+
const content = readFileSync22(agentPath, "utf-8");
|
|
13361
|
+
const { data, body } = parseFrontmatter(content);
|
|
13362
|
+
const name = data.name || agentName;
|
|
13363
|
+
const originalDescription = data.description || "";
|
|
13364
|
+
const formattedDescription = `(plugin: ${plugin2.name}) ${originalDescription}`;
|
|
13365
|
+
const config = {
|
|
13366
|
+
description: formattedDescription,
|
|
13367
|
+
mode: "subagent",
|
|
13368
|
+
prompt: body.trim()
|
|
13369
|
+
};
|
|
13370
|
+
const toolsConfig = parseToolsConfig2(data.tools);
|
|
13371
|
+
if (toolsConfig) {
|
|
13372
|
+
config.tools = toolsConfig;
|
|
13373
|
+
}
|
|
13374
|
+
agents[namespacedName] = config;
|
|
13375
|
+
log(`Loaded plugin agent: ${namespacedName}`, { path: agentPath });
|
|
13376
|
+
} catch (error) {
|
|
13377
|
+
log(`Failed to load plugin agent: ${agentPath}`, error);
|
|
13378
|
+
}
|
|
13379
|
+
}
|
|
13380
|
+
}
|
|
13381
|
+
return agents;
|
|
13382
|
+
}
|
|
13383
|
+
async function loadPluginMcpServers(plugins) {
|
|
13384
|
+
const servers = {};
|
|
13385
|
+
for (const plugin2 of plugins) {
|
|
13386
|
+
if (!plugin2.mcpPath || !existsSync33(plugin2.mcpPath))
|
|
13387
|
+
continue;
|
|
13388
|
+
try {
|
|
13389
|
+
const content = await Bun.file(plugin2.mcpPath).text();
|
|
13390
|
+
let config = JSON.parse(content);
|
|
13391
|
+
config = resolvePluginPaths(config, plugin2.installPath);
|
|
13392
|
+
config = expandEnvVarsInObject(config);
|
|
13393
|
+
if (!config.mcpServers)
|
|
13394
|
+
continue;
|
|
13395
|
+
for (const [name, serverConfig] of Object.entries(config.mcpServers)) {
|
|
13396
|
+
if (serverConfig.disabled) {
|
|
13397
|
+
log(`Skipping disabled MCP server "${name}" from plugin ${plugin2.name}`);
|
|
13398
|
+
continue;
|
|
13399
|
+
}
|
|
13400
|
+
try {
|
|
13401
|
+
const transformed = transformMcpServer(name, serverConfig);
|
|
13402
|
+
const namespacedName = `${plugin2.name}:${name}`;
|
|
13403
|
+
servers[namespacedName] = transformed;
|
|
13404
|
+
log(`Loaded plugin MCP server: ${namespacedName}`, { path: plugin2.mcpPath });
|
|
13405
|
+
} catch (error) {
|
|
13406
|
+
log(`Failed to transform plugin MCP server "${name}"`, error);
|
|
13407
|
+
}
|
|
13408
|
+
}
|
|
13409
|
+
} catch (error) {
|
|
13410
|
+
log(`Failed to load plugin MCP config: ${plugin2.mcpPath}`, error);
|
|
13411
|
+
}
|
|
13412
|
+
}
|
|
13413
|
+
return servers;
|
|
13414
|
+
}
|
|
13415
|
+
function loadPluginHooksConfigs(plugins) {
|
|
13416
|
+
const configs = [];
|
|
13417
|
+
for (const plugin2 of plugins) {
|
|
13418
|
+
if (!plugin2.hooksPath || !existsSync33(plugin2.hooksPath))
|
|
13419
|
+
continue;
|
|
13420
|
+
try {
|
|
13421
|
+
const content = readFileSync22(plugin2.hooksPath, "utf-8");
|
|
13422
|
+
let config = JSON.parse(content);
|
|
13423
|
+
config = resolvePluginPaths(config, plugin2.installPath);
|
|
13424
|
+
configs.push(config);
|
|
13425
|
+
log(`Loaded plugin hooks config from ${plugin2.name}`, { path: plugin2.hooksPath });
|
|
13426
|
+
} catch (error) {
|
|
13427
|
+
log(`Failed to load plugin hooks config: ${plugin2.hooksPath}`, error);
|
|
13428
|
+
}
|
|
13429
|
+
}
|
|
13430
|
+
return configs;
|
|
13431
|
+
}
|
|
13432
|
+
async function loadAllPluginComponents(options) {
|
|
13433
|
+
const { plugins, errors } = discoverInstalledPlugins(options);
|
|
13434
|
+
const commands = loadPluginCommands(plugins);
|
|
13435
|
+
const skills = loadPluginSkillsAsCommands(plugins);
|
|
13436
|
+
const agents = loadPluginAgents(plugins);
|
|
13437
|
+
const mcpServers = await loadPluginMcpServers(plugins);
|
|
13438
|
+
const hooksConfigs = loadPluginHooksConfigs(plugins);
|
|
13439
|
+
log(`Loaded ${plugins.length} plugins with ${Object.keys(commands).length} commands, ${Object.keys(skills).length} skills, ${Object.keys(agents).length} agents, ${Object.keys(mcpServers).length} MCP servers`);
|
|
13440
|
+
return {
|
|
13441
|
+
commands,
|
|
13442
|
+
skills,
|
|
13443
|
+
agents,
|
|
13444
|
+
mcpServers,
|
|
13445
|
+
hooksConfigs,
|
|
13446
|
+
plugins,
|
|
13447
|
+
errors
|
|
13448
|
+
};
|
|
13449
|
+
}
|
|
11402
13450
|
// src/tools/lsp/constants.ts
|
|
11403
13451
|
var SYMBOL_KIND_MAP = {
|
|
11404
13452
|
1: "File",
|
|
@@ -11694,14 +13742,14 @@ var EXT_TO_LANG = {
|
|
|
11694
13742
|
".gql": "graphql"
|
|
11695
13743
|
};
|
|
11696
13744
|
// src/tools/lsp/config.ts
|
|
11697
|
-
import { existsSync as
|
|
11698
|
-
import { join as
|
|
11699
|
-
import { homedir as
|
|
13745
|
+
import { existsSync as existsSync34, readFileSync as readFileSync23 } from "fs";
|
|
13746
|
+
import { join as join44 } from "path";
|
|
13747
|
+
import { homedir as homedir11 } from "os";
|
|
11700
13748
|
function loadJsonFile(path8) {
|
|
11701
|
-
if (!
|
|
13749
|
+
if (!existsSync34(path8))
|
|
11702
13750
|
return null;
|
|
11703
13751
|
try {
|
|
11704
|
-
return JSON.parse(
|
|
13752
|
+
return JSON.parse(readFileSync23(path8, "utf-8"));
|
|
11705
13753
|
} catch {
|
|
11706
13754
|
return null;
|
|
11707
13755
|
}
|
|
@@ -11709,9 +13757,9 @@ function loadJsonFile(path8) {
|
|
|
11709
13757
|
function getConfigPaths2() {
|
|
11710
13758
|
const cwd = process.cwd();
|
|
11711
13759
|
return {
|
|
11712
|
-
project:
|
|
11713
|
-
user:
|
|
11714
|
-
opencode:
|
|
13760
|
+
project: join44(cwd, ".opencode", "oh-my-opencode.json"),
|
|
13761
|
+
user: join44(homedir11(), ".config", "opencode", "oh-my-opencode.json"),
|
|
13762
|
+
opencode: join44(homedir11(), ".config", "opencode", "opencode.json")
|
|
11715
13763
|
};
|
|
11716
13764
|
}
|
|
11717
13765
|
function loadAllConfigs() {
|
|
@@ -11807,21 +13855,21 @@ function isServerInstalled(command) {
|
|
|
11807
13855
|
const pathSeparator = isWindows2 ? ";" : ":";
|
|
11808
13856
|
const paths = pathEnv.split(pathSeparator);
|
|
11809
13857
|
for (const p of paths) {
|
|
11810
|
-
if (
|
|
13858
|
+
if (existsSync34(join44(p, cmd)) || existsSync34(join44(p, cmd + ext))) {
|
|
11811
13859
|
return true;
|
|
11812
13860
|
}
|
|
11813
13861
|
}
|
|
11814
13862
|
const cwd = process.cwd();
|
|
11815
13863
|
const additionalPaths = [
|
|
11816
|
-
|
|
11817
|
-
|
|
11818
|
-
|
|
11819
|
-
|
|
11820
|
-
|
|
11821
|
-
|
|
13864
|
+
join44(cwd, "node_modules", ".bin", cmd),
|
|
13865
|
+
join44(cwd, "node_modules", ".bin", cmd + ext),
|
|
13866
|
+
join44(homedir11(), ".config", "opencode", "bin", cmd),
|
|
13867
|
+
join44(homedir11(), ".config", "opencode", "bin", cmd + ext),
|
|
13868
|
+
join44(homedir11(), ".config", "opencode", "node_modules", ".bin", cmd),
|
|
13869
|
+
join44(homedir11(), ".config", "opencode", "node_modules", ".bin", cmd + ext)
|
|
11822
13870
|
];
|
|
11823
13871
|
for (const p of additionalPaths) {
|
|
11824
|
-
if (
|
|
13872
|
+
if (existsSync34(p)) {
|
|
11825
13873
|
return true;
|
|
11826
13874
|
}
|
|
11827
13875
|
}
|
|
@@ -11870,8 +13918,8 @@ function getAllServers() {
|
|
|
11870
13918
|
return result;
|
|
11871
13919
|
}
|
|
11872
13920
|
// src/tools/lsp/client.ts
|
|
11873
|
-
var {spawn:
|
|
11874
|
-
import { readFileSync as
|
|
13921
|
+
var {spawn: spawn5 } = globalThis.Bun;
|
|
13922
|
+
import { readFileSync as readFileSync24 } from "fs";
|
|
11875
13923
|
import { extname, resolve as resolve5 } from "path";
|
|
11876
13924
|
class LSPServerManager {
|
|
11877
13925
|
static instance;
|
|
@@ -12037,7 +14085,7 @@ class LSPClient {
|
|
|
12037
14085
|
this.server = server;
|
|
12038
14086
|
}
|
|
12039
14087
|
async start() {
|
|
12040
|
-
this.proc =
|
|
14088
|
+
this.proc = spawn5(this.server.command, {
|
|
12041
14089
|
stdin: "pipe",
|
|
12042
14090
|
stdout: "pipe",
|
|
12043
14091
|
stderr: "pipe",
|
|
@@ -12301,7 +14349,7 @@ ${msg}`);
|
|
|
12301
14349
|
const absPath = resolve5(filePath);
|
|
12302
14350
|
if (this.openedFiles.has(absPath))
|
|
12303
14351
|
return;
|
|
12304
|
-
const text =
|
|
14352
|
+
const text = readFileSync24(absPath, "utf-8");
|
|
12305
14353
|
const ext = extname(absPath);
|
|
12306
14354
|
const languageId = getLanguageId(ext);
|
|
12307
14355
|
this.notify("textDocument/didOpen", {
|
|
@@ -12416,16 +14464,16 @@ ${msg}`);
|
|
|
12416
14464
|
}
|
|
12417
14465
|
// src/tools/lsp/utils.ts
|
|
12418
14466
|
import { extname as extname2, resolve as resolve6 } from "path";
|
|
12419
|
-
import { existsSync as
|
|
14467
|
+
import { existsSync as existsSync35, readFileSync as readFileSync25, writeFileSync as writeFileSync13 } from "fs";
|
|
12420
14468
|
function findWorkspaceRoot(filePath) {
|
|
12421
14469
|
let dir = resolve6(filePath);
|
|
12422
|
-
if (!
|
|
14470
|
+
if (!existsSync35(dir) || !__require("fs").statSync(dir).isDirectory()) {
|
|
12423
14471
|
dir = __require("path").dirname(dir);
|
|
12424
14472
|
}
|
|
12425
14473
|
const markers = [".git", "package.json", "pyproject.toml", "Cargo.toml", "go.mod", "pom.xml", "build.gradle"];
|
|
12426
14474
|
while (dir !== "/") {
|
|
12427
14475
|
for (const marker of markers) {
|
|
12428
|
-
if (
|
|
14476
|
+
if (existsSync35(__require("path").join(dir, marker))) {
|
|
12429
14477
|
return dir;
|
|
12430
14478
|
}
|
|
12431
14479
|
}
|
|
@@ -12583,7 +14631,7 @@ function formatCodeActions(actions) {
|
|
|
12583
14631
|
}
|
|
12584
14632
|
function applyTextEditsToFile(filePath, edits) {
|
|
12585
14633
|
try {
|
|
12586
|
-
let content =
|
|
14634
|
+
let content = readFileSync25(filePath, "utf-8");
|
|
12587
14635
|
const lines = content.split(`
|
|
12588
14636
|
`);
|
|
12589
14637
|
const sortedEdits = [...edits].sort((a, b) => {
|
|
@@ -12608,7 +14656,7 @@ function applyTextEditsToFile(filePath, edits) {
|
|
|
12608
14656
|
`));
|
|
12609
14657
|
}
|
|
12610
14658
|
}
|
|
12611
|
-
|
|
14659
|
+
writeFileSync13(filePath, lines.join(`
|
|
12612
14660
|
`), "utf-8");
|
|
12613
14661
|
return { success: true, editCount: edits.length };
|
|
12614
14662
|
} catch (err) {
|
|
@@ -12639,7 +14687,7 @@ function applyWorkspaceEdit(edit) {
|
|
|
12639
14687
|
if (change.kind === "create") {
|
|
12640
14688
|
try {
|
|
12641
14689
|
const filePath = change.uri.replace("file://", "");
|
|
12642
|
-
|
|
14690
|
+
writeFileSync13(filePath, "", "utf-8");
|
|
12643
14691
|
result.filesModified.push(filePath);
|
|
12644
14692
|
} catch (err) {
|
|
12645
14693
|
result.success = false;
|
|
@@ -12649,8 +14697,8 @@ function applyWorkspaceEdit(edit) {
|
|
|
12649
14697
|
try {
|
|
12650
14698
|
const oldPath = change.oldUri.replace("file://", "");
|
|
12651
14699
|
const newPath = change.newUri.replace("file://", "");
|
|
12652
|
-
const content =
|
|
12653
|
-
|
|
14700
|
+
const content = readFileSync25(oldPath, "utf-8");
|
|
14701
|
+
writeFileSync13(newPath, content, "utf-8");
|
|
12654
14702
|
__require("fs").unlinkSync(oldPath);
|
|
12655
14703
|
result.filesModified.push(newPath);
|
|
12656
14704
|
} catch (err) {
|
|
@@ -12759,7 +14807,7 @@ __export(exports_external, {
|
|
|
12759
14807
|
pipe: () => pipe,
|
|
12760
14808
|
partialRecord: () => partialRecord,
|
|
12761
14809
|
parseAsync: () => parseAsync2,
|
|
12762
|
-
parse: () =>
|
|
14810
|
+
parse: () => parse5,
|
|
12763
14811
|
overwrite: () => _overwrite,
|
|
12764
14812
|
optional: () => optional,
|
|
12765
14813
|
object: () => object,
|
|
@@ -12949,7 +14997,7 @@ __export(exports_core2, {
|
|
|
12949
14997
|
regexes: () => exports_regexes,
|
|
12950
14998
|
prettifyError: () => prettifyError,
|
|
12951
14999
|
parseAsync: () => parseAsync,
|
|
12952
|
-
parse: () =>
|
|
15000
|
+
parse: () => parse3,
|
|
12953
15001
|
locales: () => exports_locales,
|
|
12954
15002
|
isValidJWT: () => isValidJWT,
|
|
12955
15003
|
isValidBase64URL: () => isValidBase64URL,
|
|
@@ -14048,7 +16096,7 @@ var _parse = (_Err) => (schema, value, _ctx, _params) => {
|
|
|
14048
16096
|
}
|
|
14049
16097
|
return result.value;
|
|
14050
16098
|
};
|
|
14051
|
-
var
|
|
16099
|
+
var parse3 = /* @__PURE__ */ _parse($ZodRealError);
|
|
14052
16100
|
var _parseAsync = (_Err) => async (schema, value, _ctx, params) => {
|
|
14053
16101
|
const ctx = _ctx ? Object.assign(_ctx, { async: true }) : { async: true };
|
|
14054
16102
|
let result = schema._zod.run({ value, issues: [] }, ctx);
|
|
@@ -16566,10 +18614,10 @@ var $ZodFunction = /* @__PURE__ */ $constructor("$ZodFunction", (inst, def) => {
|
|
|
16566
18614
|
throw new Error("implement() must be called with a function");
|
|
16567
18615
|
}
|
|
16568
18616
|
return function(...args) {
|
|
16569
|
-
const parsedArgs = inst._def.input ?
|
|
18617
|
+
const parsedArgs = inst._def.input ? parse3(inst._def.input, args) : args;
|
|
16570
18618
|
const result = Reflect.apply(func, this, parsedArgs);
|
|
16571
18619
|
if (inst._def.output) {
|
|
16572
|
-
return
|
|
18620
|
+
return parse3(inst._def.output, result);
|
|
16573
18621
|
}
|
|
16574
18622
|
return result;
|
|
16575
18623
|
};
|
|
@@ -23078,13 +25126,13 @@ function _stringbool(Classes, _params) {
|
|
|
23078
25126
|
});
|
|
23079
25127
|
return codec;
|
|
23080
25128
|
}
|
|
23081
|
-
function _stringFormat(Class2,
|
|
25129
|
+
function _stringFormat(Class2, format2, fnOrRegex, _params = {}) {
|
|
23082
25130
|
const params = normalizeParams(_params);
|
|
23083
25131
|
const def = {
|
|
23084
25132
|
...normalizeParams(_params),
|
|
23085
25133
|
check: "string_format",
|
|
23086
25134
|
type: "string",
|
|
23087
|
-
format,
|
|
25135
|
+
format: format2,
|
|
23088
25136
|
fn: typeof fnOrRegex === "function" ? fnOrRegex : (val) => fnOrRegex.test(val),
|
|
23089
25137
|
...params
|
|
23090
25138
|
};
|
|
@@ -23146,13 +25194,13 @@ class JSONSchemaGenerator {
|
|
|
23146
25194
|
case "string": {
|
|
23147
25195
|
const json = _json;
|
|
23148
25196
|
json.type = "string";
|
|
23149
|
-
const { minimum, maximum, format, patterns, contentEncoding } = schema._zod.bag;
|
|
25197
|
+
const { minimum, maximum, format: format2, patterns, contentEncoding } = schema._zod.bag;
|
|
23150
25198
|
if (typeof minimum === "number")
|
|
23151
25199
|
json.minLength = minimum;
|
|
23152
25200
|
if (typeof maximum === "number")
|
|
23153
25201
|
json.maxLength = maximum;
|
|
23154
|
-
if (
|
|
23155
|
-
json.format = formatMap[
|
|
25202
|
+
if (format2) {
|
|
25203
|
+
json.format = formatMap[format2] ?? format2;
|
|
23156
25204
|
if (json.format === "")
|
|
23157
25205
|
delete json.format;
|
|
23158
25206
|
}
|
|
@@ -23175,8 +25223,8 @@ class JSONSchemaGenerator {
|
|
|
23175
25223
|
}
|
|
23176
25224
|
case "number": {
|
|
23177
25225
|
const json = _json;
|
|
23178
|
-
const { minimum, maximum, format, multipleOf, exclusiveMaximum, exclusiveMinimum } = schema._zod.bag;
|
|
23179
|
-
if (typeof
|
|
25226
|
+
const { minimum, maximum, format: format2, multipleOf, exclusiveMaximum, exclusiveMinimum } = schema._zod.bag;
|
|
25227
|
+
if (typeof format2 === "string" && format2.includes("int"))
|
|
23180
25228
|
json.type = "integer";
|
|
23181
25229
|
else
|
|
23182
25230
|
json.type = "number";
|
|
@@ -23977,7 +26025,7 @@ var ZodRealError = $constructor("ZodError", initializer2, {
|
|
|
23977
26025
|
});
|
|
23978
26026
|
|
|
23979
26027
|
// node_modules/zod/v4/classic/parse.js
|
|
23980
|
-
var
|
|
26028
|
+
var parse5 = /* @__PURE__ */ _parse(ZodRealError);
|
|
23981
26029
|
var parseAsync2 = /* @__PURE__ */ _parseAsync(ZodRealError);
|
|
23982
26030
|
var safeParse2 = /* @__PURE__ */ _safeParse(ZodRealError);
|
|
23983
26031
|
var safeParseAsync2 = /* @__PURE__ */ _safeParseAsync(ZodRealError);
|
|
@@ -24011,7 +26059,7 @@ var ZodType = /* @__PURE__ */ $constructor("ZodType", (inst, def) => {
|
|
|
24011
26059
|
reg.add(inst, meta);
|
|
24012
26060
|
return inst;
|
|
24013
26061
|
};
|
|
24014
|
-
inst.parse = (data, params) =>
|
|
26062
|
+
inst.parse = (data, params) => parse5(inst, data, params, { callee: inst.parse });
|
|
24015
26063
|
inst.safeParse = (data, params) => safeParse2(inst, data, params);
|
|
24016
26064
|
inst.parseAsync = async (data, params) => parseAsync2(inst, data, params, { callee: inst.parseAsync });
|
|
24017
26065
|
inst.safeParseAsync = async (data, params) => safeParseAsync2(inst, data, params);
|
|
@@ -24276,8 +26324,8 @@ var ZodCustomStringFormat = /* @__PURE__ */ $constructor("ZodCustomStringFormat"
|
|
|
24276
26324
|
$ZodCustomStringFormat.init(inst, def);
|
|
24277
26325
|
ZodStringFormat.init(inst, def);
|
|
24278
26326
|
});
|
|
24279
|
-
function stringFormat(
|
|
24280
|
-
return _stringFormat(ZodCustomStringFormat,
|
|
26327
|
+
function stringFormat(format2, fnOrRegex, _params = {}) {
|
|
26328
|
+
return _stringFormat(ZodCustomStringFormat, format2, fnOrRegex, _params);
|
|
24281
26329
|
}
|
|
24282
26330
|
function hostname2(_params) {
|
|
24283
26331
|
return _stringFormat(ZodCustomStringFormat, "hostname", exports_regexes.hostname, _params);
|
|
@@ -24287,11 +26335,11 @@ function hex2(_params) {
|
|
|
24287
26335
|
}
|
|
24288
26336
|
function hash(alg, params) {
|
|
24289
26337
|
const enc = params?.enc ?? "hex";
|
|
24290
|
-
const
|
|
24291
|
-
const regex = exports_regexes[
|
|
26338
|
+
const format2 = `${alg}_${enc}`;
|
|
26339
|
+
const regex = exports_regexes[format2];
|
|
24292
26340
|
if (!regex)
|
|
24293
|
-
throw new Error(`Unrecognized hash format: ${
|
|
24294
|
-
return _stringFormat(ZodCustomStringFormat,
|
|
26341
|
+
throw new Error(`Unrecognized hash format: ${format2}`);
|
|
26342
|
+
return _stringFormat(ZodCustomStringFormat, format2, regex, params);
|
|
24295
26343
|
}
|
|
24296
26344
|
var ZodNumber = /* @__PURE__ */ $constructor("ZodNumber", (inst, def) => {
|
|
24297
26345
|
$ZodNumber.init(inst, def);
|
|
@@ -25350,14 +27398,14 @@ var lsp_code_action_resolve = tool({
|
|
|
25350
27398
|
});
|
|
25351
27399
|
// src/tools/ast-grep/constants.ts
|
|
25352
27400
|
import { createRequire as createRequire4 } from "module";
|
|
25353
|
-
import { dirname as dirname6, join as
|
|
25354
|
-
import { existsSync as
|
|
27401
|
+
import { dirname as dirname6, join as join46 } from "path";
|
|
27402
|
+
import { existsSync as existsSync37, statSync as statSync4 } from "fs";
|
|
25355
27403
|
|
|
25356
27404
|
// src/tools/ast-grep/downloader.ts
|
|
25357
|
-
var {spawn:
|
|
25358
|
-
import { existsSync as
|
|
25359
|
-
import { join as
|
|
25360
|
-
import { homedir as
|
|
27405
|
+
var {spawn: spawn6 } = globalThis.Bun;
|
|
27406
|
+
import { existsSync as existsSync36, mkdirSync as mkdirSync10, chmodSync as chmodSync2, unlinkSync as unlinkSync9 } from "fs";
|
|
27407
|
+
import { join as join45 } from "path";
|
|
27408
|
+
import { homedir as homedir12 } from "os";
|
|
25361
27409
|
import { createRequire as createRequire3 } from "module";
|
|
25362
27410
|
var REPO2 = "ast-grep/ast-grep";
|
|
25363
27411
|
var DEFAULT_VERSION = "0.40.0";
|
|
@@ -25382,26 +27430,26 @@ var PLATFORM_MAP2 = {
|
|
|
25382
27430
|
function getCacheDir3() {
|
|
25383
27431
|
if (process.platform === "win32") {
|
|
25384
27432
|
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
25385
|
-
const base2 = localAppData ||
|
|
25386
|
-
return
|
|
27433
|
+
const base2 = localAppData || join45(homedir12(), "AppData", "Local");
|
|
27434
|
+
return join45(base2, "oh-my-opencode", "bin");
|
|
25387
27435
|
}
|
|
25388
27436
|
const xdgCache2 = process.env.XDG_CACHE_HOME;
|
|
25389
|
-
const base = xdgCache2 ||
|
|
25390
|
-
return
|
|
27437
|
+
const base = xdgCache2 || join45(homedir12(), ".cache");
|
|
27438
|
+
return join45(base, "oh-my-opencode", "bin");
|
|
25391
27439
|
}
|
|
25392
27440
|
function getBinaryName3() {
|
|
25393
27441
|
return process.platform === "win32" ? "sg.exe" : "sg";
|
|
25394
27442
|
}
|
|
25395
27443
|
function getCachedBinaryPath2() {
|
|
25396
|
-
const binaryPath =
|
|
25397
|
-
return
|
|
27444
|
+
const binaryPath = join45(getCacheDir3(), getBinaryName3());
|
|
27445
|
+
return existsSync36(binaryPath) ? binaryPath : null;
|
|
25398
27446
|
}
|
|
25399
27447
|
async function extractZip2(archivePath, destDir) {
|
|
25400
|
-
const proc = process.platform === "win32" ?
|
|
27448
|
+
const proc = process.platform === "win32" ? spawn6([
|
|
25401
27449
|
"powershell",
|
|
25402
27450
|
"-command",
|
|
25403
27451
|
`Expand-Archive -Path '${archivePath}' -DestinationPath '${destDir}' -Force`
|
|
25404
|
-
], { stdout: "pipe", stderr: "pipe" }) :
|
|
27452
|
+
], { stdout: "pipe", stderr: "pipe" }) : spawn6(["unzip", "-o", archivePath, "-d", destDir], { stdout: "pipe", stderr: "pipe" });
|
|
25405
27453
|
const exitCode = await proc.exited;
|
|
25406
27454
|
if (exitCode !== 0) {
|
|
25407
27455
|
const stderr = await new Response(proc.stderr).text();
|
|
@@ -25420,8 +27468,8 @@ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
|
|
|
25420
27468
|
}
|
|
25421
27469
|
const cacheDir = getCacheDir3();
|
|
25422
27470
|
const binaryName = getBinaryName3();
|
|
25423
|
-
const binaryPath =
|
|
25424
|
-
if (
|
|
27471
|
+
const binaryPath = join45(cacheDir, binaryName);
|
|
27472
|
+
if (existsSync36(binaryPath)) {
|
|
25425
27473
|
return binaryPath;
|
|
25426
27474
|
}
|
|
25427
27475
|
const { arch, os: os6 } = platformInfo;
|
|
@@ -25429,21 +27477,21 @@ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
|
|
|
25429
27477
|
const downloadUrl = `https://github.com/${REPO2}/releases/download/${version2}/${assetName}`;
|
|
25430
27478
|
console.log(`[oh-my-opencode] Downloading ast-grep binary...`);
|
|
25431
27479
|
try {
|
|
25432
|
-
if (!
|
|
27480
|
+
if (!existsSync36(cacheDir)) {
|
|
25433
27481
|
mkdirSync10(cacheDir, { recursive: true });
|
|
25434
27482
|
}
|
|
25435
27483
|
const response2 = await fetch(downloadUrl, { redirect: "follow" });
|
|
25436
27484
|
if (!response2.ok) {
|
|
25437
27485
|
throw new Error(`HTTP ${response2.status}: ${response2.statusText}`);
|
|
25438
27486
|
}
|
|
25439
|
-
const archivePath =
|
|
27487
|
+
const archivePath = join45(cacheDir, assetName);
|
|
25440
27488
|
const arrayBuffer = await response2.arrayBuffer();
|
|
25441
27489
|
await Bun.write(archivePath, arrayBuffer);
|
|
25442
27490
|
await extractZip2(archivePath, cacheDir);
|
|
25443
|
-
if (
|
|
27491
|
+
if (existsSync36(archivePath)) {
|
|
25444
27492
|
unlinkSync9(archivePath);
|
|
25445
27493
|
}
|
|
25446
|
-
if (process.platform !== "win32" &&
|
|
27494
|
+
if (process.platform !== "win32" && existsSync36(binaryPath)) {
|
|
25447
27495
|
chmodSync2(binaryPath, 493);
|
|
25448
27496
|
}
|
|
25449
27497
|
console.log(`[oh-my-opencode] ast-grep binary ready.`);
|
|
@@ -25494,8 +27542,8 @@ function findSgCliPathSync() {
|
|
|
25494
27542
|
const require2 = createRequire4(import.meta.url);
|
|
25495
27543
|
const cliPkgPath = require2.resolve("@ast-grep/cli/package.json");
|
|
25496
27544
|
const cliDir = dirname6(cliPkgPath);
|
|
25497
|
-
const sgPath =
|
|
25498
|
-
if (
|
|
27545
|
+
const sgPath = join46(cliDir, binaryName);
|
|
27546
|
+
if (existsSync37(sgPath) && isValidBinary(sgPath)) {
|
|
25499
27547
|
return sgPath;
|
|
25500
27548
|
}
|
|
25501
27549
|
} catch {}
|
|
@@ -25506,8 +27554,8 @@ function findSgCliPathSync() {
|
|
|
25506
27554
|
const pkgPath = require2.resolve(`${platformPkg}/package.json`);
|
|
25507
27555
|
const pkgDir = dirname6(pkgPath);
|
|
25508
27556
|
const astGrepName = process.platform === "win32" ? "ast-grep.exe" : "ast-grep";
|
|
25509
|
-
const binaryPath =
|
|
25510
|
-
if (
|
|
27557
|
+
const binaryPath = join46(pkgDir, astGrepName);
|
|
27558
|
+
if (existsSync37(binaryPath) && isValidBinary(binaryPath)) {
|
|
25511
27559
|
return binaryPath;
|
|
25512
27560
|
}
|
|
25513
27561
|
} catch {}
|
|
@@ -25515,7 +27563,7 @@ function findSgCliPathSync() {
|
|
|
25515
27563
|
if (process.platform === "darwin") {
|
|
25516
27564
|
const homebrewPaths = ["/opt/homebrew/bin/sg", "/usr/local/bin/sg"];
|
|
25517
27565
|
for (const path8 of homebrewPaths) {
|
|
25518
|
-
if (
|
|
27566
|
+
if (existsSync37(path8) && isValidBinary(path8)) {
|
|
25519
27567
|
return path8;
|
|
25520
27568
|
}
|
|
25521
27569
|
}
|
|
@@ -25570,12 +27618,12 @@ var DEFAULT_MAX_OUTPUT_BYTES = 1 * 1024 * 1024;
|
|
|
25570
27618
|
var DEFAULT_MAX_MATCHES = 500;
|
|
25571
27619
|
|
|
25572
27620
|
// src/tools/ast-grep/cli.ts
|
|
25573
|
-
var {spawn:
|
|
25574
|
-
import { existsSync as
|
|
27621
|
+
var {spawn: spawn7 } = globalThis.Bun;
|
|
27622
|
+
import { existsSync as existsSync38 } from "fs";
|
|
25575
27623
|
var resolvedCliPath3 = null;
|
|
25576
27624
|
var initPromise2 = null;
|
|
25577
27625
|
async function getAstGrepPath() {
|
|
25578
|
-
if (resolvedCliPath3 !== null &&
|
|
27626
|
+
if (resolvedCliPath3 !== null && existsSync38(resolvedCliPath3)) {
|
|
25579
27627
|
return resolvedCliPath3;
|
|
25580
27628
|
}
|
|
25581
27629
|
if (initPromise2) {
|
|
@@ -25583,7 +27631,7 @@ async function getAstGrepPath() {
|
|
|
25583
27631
|
}
|
|
25584
27632
|
initPromise2 = (async () => {
|
|
25585
27633
|
const syncPath = findSgCliPathSync();
|
|
25586
|
-
if (syncPath &&
|
|
27634
|
+
if (syncPath && existsSync38(syncPath)) {
|
|
25587
27635
|
resolvedCliPath3 = syncPath;
|
|
25588
27636
|
setSgCliPath(syncPath);
|
|
25589
27637
|
return syncPath;
|
|
@@ -25617,14 +27665,14 @@ async function runSg(options) {
|
|
|
25617
27665
|
const paths = options.paths && options.paths.length > 0 ? options.paths : ["."];
|
|
25618
27666
|
args.push(...paths);
|
|
25619
27667
|
let cliPath = getSgCliPath();
|
|
25620
|
-
if (!
|
|
27668
|
+
if (!existsSync38(cliPath) && cliPath !== "sg") {
|
|
25621
27669
|
const downloadedPath = await getAstGrepPath();
|
|
25622
27670
|
if (downloadedPath) {
|
|
25623
27671
|
cliPath = downloadedPath;
|
|
25624
27672
|
}
|
|
25625
27673
|
}
|
|
25626
27674
|
const timeout = DEFAULT_TIMEOUT_MS;
|
|
25627
|
-
const proc =
|
|
27675
|
+
const proc = spawn7([cliPath, ...args], {
|
|
25628
27676
|
stdout: "pipe",
|
|
25629
27677
|
stderr: "pipe"
|
|
25630
27678
|
});
|
|
@@ -25878,27 +27926,27 @@ var ast_grep_replace = tool({
|
|
|
25878
27926
|
}
|
|
25879
27927
|
});
|
|
25880
27928
|
// src/tools/grep/cli.ts
|
|
25881
|
-
var {spawn:
|
|
27929
|
+
var {spawn: spawn8 } = globalThis.Bun;
|
|
25882
27930
|
|
|
25883
27931
|
// src/tools/grep/constants.ts
|
|
25884
|
-
import { existsSync as
|
|
25885
|
-
import { join as
|
|
27932
|
+
import { existsSync as existsSync40 } from "fs";
|
|
27933
|
+
import { join as join48, dirname as dirname7 } from "path";
|
|
25886
27934
|
import { spawnSync } from "child_process";
|
|
25887
27935
|
|
|
25888
27936
|
// src/tools/grep/downloader.ts
|
|
25889
|
-
import { existsSync as
|
|
25890
|
-
import { join as
|
|
27937
|
+
import { existsSync as existsSync39, mkdirSync as mkdirSync11, chmodSync as chmodSync3, unlinkSync as unlinkSync10, readdirSync as readdirSync14 } from "fs";
|
|
27938
|
+
import { join as join47 } from "path";
|
|
25891
27939
|
function getInstallDir() {
|
|
25892
27940
|
const homeDir = process.env.HOME || process.env.USERPROFILE || ".";
|
|
25893
|
-
return
|
|
27941
|
+
return join47(homeDir, ".cache", "oh-my-opencode", "bin");
|
|
25894
27942
|
}
|
|
25895
27943
|
function getRgPath() {
|
|
25896
27944
|
const isWindows2 = process.platform === "win32";
|
|
25897
|
-
return
|
|
27945
|
+
return join47(getInstallDir(), isWindows2 ? "rg.exe" : "rg");
|
|
25898
27946
|
}
|
|
25899
27947
|
function getInstalledRipgrepPath() {
|
|
25900
27948
|
const rgPath = getRgPath();
|
|
25901
|
-
return
|
|
27949
|
+
return existsSync39(rgPath) ? rgPath : null;
|
|
25902
27950
|
}
|
|
25903
27951
|
|
|
25904
27952
|
// src/tools/grep/constants.ts
|
|
@@ -25921,13 +27969,13 @@ function getOpenCodeBundledRg() {
|
|
|
25921
27969
|
const isWindows2 = process.platform === "win32";
|
|
25922
27970
|
const rgName = isWindows2 ? "rg.exe" : "rg";
|
|
25923
27971
|
const candidates = [
|
|
25924
|
-
|
|
25925
|
-
|
|
25926
|
-
|
|
25927
|
-
|
|
27972
|
+
join48(execDir, rgName),
|
|
27973
|
+
join48(execDir, "bin", rgName),
|
|
27974
|
+
join48(execDir, "..", "bin", rgName),
|
|
27975
|
+
join48(execDir, "..", "libexec", rgName)
|
|
25928
27976
|
];
|
|
25929
27977
|
for (const candidate of candidates) {
|
|
25930
|
-
if (
|
|
27978
|
+
if (existsSync40(candidate)) {
|
|
25931
27979
|
return candidate;
|
|
25932
27980
|
}
|
|
25933
27981
|
}
|
|
@@ -26073,7 +28121,7 @@ async function runRg(options) {
|
|
|
26073
28121
|
}
|
|
26074
28122
|
const paths = options.paths?.length ? options.paths : ["."];
|
|
26075
28123
|
args.push(...paths);
|
|
26076
|
-
const proc =
|
|
28124
|
+
const proc = spawn8([cli.path, ...args], {
|
|
26077
28125
|
stdout: "pipe",
|
|
26078
28126
|
stderr: "pipe"
|
|
26079
28127
|
});
|
|
@@ -26175,7 +28223,7 @@ var grep = tool({
|
|
|
26175
28223
|
});
|
|
26176
28224
|
|
|
26177
28225
|
// src/tools/glob/cli.ts
|
|
26178
|
-
var {spawn:
|
|
28226
|
+
var {spawn: spawn9 } = globalThis.Bun;
|
|
26179
28227
|
|
|
26180
28228
|
// src/tools/glob/constants.ts
|
|
26181
28229
|
var DEFAULT_TIMEOUT_MS3 = 60000;
|
|
@@ -26232,7 +28280,7 @@ async function runRgFiles(options) {
|
|
|
26232
28280
|
args.push(...paths);
|
|
26233
28281
|
}
|
|
26234
28282
|
const cwd = paths[0] || ".";
|
|
26235
|
-
const proc =
|
|
28283
|
+
const proc = spawn9([cli.path, ...args], {
|
|
26236
28284
|
stdout: "pipe",
|
|
26237
28285
|
stderr: "pipe",
|
|
26238
28286
|
cwd: isRg ? undefined : cwd
|
|
@@ -26330,22 +28378,21 @@ var glob = tool({
|
|
|
26330
28378
|
}
|
|
26331
28379
|
});
|
|
26332
28380
|
// src/tools/slashcommand/tools.ts
|
|
26333
|
-
import { existsSync as
|
|
26334
|
-
import {
|
|
26335
|
-
import { join as join44, basename as basename3, dirname as dirname8 } from "path";
|
|
28381
|
+
import { existsSync as existsSync41, readdirSync as readdirSync15, readFileSync as readFileSync26 } from "fs";
|
|
28382
|
+
import { join as join49, basename as basename4, dirname as dirname8 } from "path";
|
|
26336
28383
|
function discoverCommandsFromDir(commandsDir, scope) {
|
|
26337
|
-
if (!
|
|
28384
|
+
if (!existsSync41(commandsDir)) {
|
|
26338
28385
|
return [];
|
|
26339
28386
|
}
|
|
26340
|
-
const entries =
|
|
28387
|
+
const entries = readdirSync15(commandsDir, { withFileTypes: true });
|
|
26341
28388
|
const commands = [];
|
|
26342
28389
|
for (const entry of entries) {
|
|
26343
28390
|
if (!isMarkdownFile(entry))
|
|
26344
28391
|
continue;
|
|
26345
|
-
const commandPath =
|
|
26346
|
-
const commandName =
|
|
28392
|
+
const commandPath = join49(commandsDir, entry.name);
|
|
28393
|
+
const commandName = basename4(entry.name, ".md");
|
|
26347
28394
|
try {
|
|
26348
|
-
const content =
|
|
28395
|
+
const content = readFileSync26(commandPath, "utf-8");
|
|
26349
28396
|
const { data, body } = parseFrontmatter(content);
|
|
26350
28397
|
const isOpencodeSource = scope === "opencode" || scope === "opencode-project";
|
|
26351
28398
|
const metadata = {
|
|
@@ -26370,10 +28417,11 @@ function discoverCommandsFromDir(commandsDir, scope) {
|
|
|
26370
28417
|
return commands;
|
|
26371
28418
|
}
|
|
26372
28419
|
function discoverCommandsSync() {
|
|
26373
|
-
const
|
|
26374
|
-
const
|
|
26375
|
-
const
|
|
26376
|
-
const
|
|
28420
|
+
const { homedir: homedir13 } = __require("os");
|
|
28421
|
+
const userCommandsDir = join49(getClaudeConfigDir(), "commands");
|
|
28422
|
+
const projectCommandsDir = join49(process.cwd(), ".claude", "commands");
|
|
28423
|
+
const opencodeGlobalDir = join49(homedir13(), ".config", "opencode", "command");
|
|
28424
|
+
const opencodeProjectDir = join49(process.cwd(), ".opencode", "command");
|
|
26377
28425
|
const userCommands = discoverCommandsFromDir(userCommandsDir, "user");
|
|
26378
28426
|
const opencodeGlobalCommands = discoverCommandsFromDir(opencodeGlobalDir, "opencode");
|
|
26379
28427
|
const projectCommands = discoverCommandsFromDir(projectCommandsDir, "project");
|
|
@@ -26457,7 +28505,7 @@ Commands are loaded from (priority order, highest wins):
|
|
|
26457
28505
|
- .opencode/command/ (opencode-project - OpenCode project-specific commands)
|
|
26458
28506
|
- ./.claude/commands/ (project - Claude Code project-specific commands)
|
|
26459
28507
|
- ~/.config/opencode/command/ (opencode - OpenCode global commands)
|
|
26460
|
-
- ~/.claude/commands/ (user - Claude Code global commands)
|
|
28508
|
+
- $CLAUDE_CONFIG_DIR/commands/ or ~/.claude/commands/ (user - Claude Code global commands)
|
|
26461
28509
|
|
|
26462
28510
|
Each command is a markdown file with:
|
|
26463
28511
|
- YAML frontmatter: description, argument-hint, model, agent, subtask (optional)
|
|
@@ -26497,13 +28545,12 @@ Try a different command name.`;
|
|
|
26497
28545
|
}
|
|
26498
28546
|
});
|
|
26499
28547
|
// src/tools/session-manager/constants.ts
|
|
26500
|
-
import { join as
|
|
26501
|
-
import { homedir as homedir19 } from "os";
|
|
28548
|
+
import { join as join50 } from "path";
|
|
26502
28549
|
var OPENCODE_STORAGE9 = getOpenCodeStorageDir();
|
|
26503
|
-
var
|
|
26504
|
-
var PART_STORAGE4 =
|
|
26505
|
-
var TODO_DIR2 =
|
|
26506
|
-
var TRANSCRIPT_DIR2 =
|
|
28550
|
+
var MESSAGE_STORAGE8 = join50(OPENCODE_STORAGE9, "message");
|
|
28551
|
+
var PART_STORAGE4 = join50(OPENCODE_STORAGE9, "part");
|
|
28552
|
+
var TODO_DIR2 = join50(getClaudeConfigDir(), "todos");
|
|
28553
|
+
var TRANSCRIPT_DIR2 = join50(getClaudeConfigDir(), "transcripts");
|
|
26507
28554
|
var SESSION_LIST_DESCRIPTION = `List all OpenCode sessions with optional filtering.
|
|
26508
28555
|
|
|
26509
28556
|
Returns a list of available session IDs with metadata including message count, date range, and agents used.
|
|
@@ -26576,18 +28623,18 @@ Has Todos: Yes (12 items, 8 completed)
|
|
|
26576
28623
|
Has Transcript: Yes (234 entries)`;
|
|
26577
28624
|
|
|
26578
28625
|
// src/tools/session-manager/storage.ts
|
|
26579
|
-
import { existsSync as
|
|
26580
|
-
import { join as
|
|
28626
|
+
import { existsSync as existsSync42, readdirSync as readdirSync16, readFileSync as readFileSync27 } from "fs";
|
|
28627
|
+
import { join as join51 } from "path";
|
|
26581
28628
|
function getAllSessions() {
|
|
26582
|
-
if (!
|
|
28629
|
+
if (!existsSync42(MESSAGE_STORAGE8))
|
|
26583
28630
|
return [];
|
|
26584
28631
|
const sessions = [];
|
|
26585
28632
|
function scanDirectory(dir) {
|
|
26586
28633
|
try {
|
|
26587
|
-
for (const entry of
|
|
28634
|
+
for (const entry of readdirSync16(dir, { withFileTypes: true })) {
|
|
26588
28635
|
if (entry.isDirectory()) {
|
|
26589
|
-
const sessionPath =
|
|
26590
|
-
const files =
|
|
28636
|
+
const sessionPath = join51(dir, entry.name);
|
|
28637
|
+
const files = readdirSync16(sessionPath);
|
|
26591
28638
|
if (files.some((f) => f.endsWith(".json"))) {
|
|
26592
28639
|
sessions.push(entry.name);
|
|
26593
28640
|
} else {
|
|
@@ -26599,37 +28646,37 @@ function getAllSessions() {
|
|
|
26599
28646
|
return;
|
|
26600
28647
|
}
|
|
26601
28648
|
}
|
|
26602
|
-
scanDirectory(
|
|
28649
|
+
scanDirectory(MESSAGE_STORAGE8);
|
|
26603
28650
|
return [...new Set(sessions)];
|
|
26604
28651
|
}
|
|
26605
|
-
function
|
|
26606
|
-
if (!
|
|
28652
|
+
function getMessageDir9(sessionID) {
|
|
28653
|
+
if (!existsSync42(MESSAGE_STORAGE8))
|
|
26607
28654
|
return "";
|
|
26608
|
-
const directPath =
|
|
26609
|
-
if (
|
|
28655
|
+
const directPath = join51(MESSAGE_STORAGE8, sessionID);
|
|
28656
|
+
if (existsSync42(directPath)) {
|
|
26610
28657
|
return directPath;
|
|
26611
28658
|
}
|
|
26612
|
-
for (const dir of
|
|
26613
|
-
const sessionPath =
|
|
26614
|
-
if (
|
|
28659
|
+
for (const dir of readdirSync16(MESSAGE_STORAGE8)) {
|
|
28660
|
+
const sessionPath = join51(MESSAGE_STORAGE8, dir, sessionID);
|
|
28661
|
+
if (existsSync42(sessionPath)) {
|
|
26615
28662
|
return sessionPath;
|
|
26616
28663
|
}
|
|
26617
28664
|
}
|
|
26618
28665
|
return "";
|
|
26619
28666
|
}
|
|
26620
28667
|
function sessionExists(sessionID) {
|
|
26621
|
-
return
|
|
28668
|
+
return getMessageDir9(sessionID) !== "";
|
|
26622
28669
|
}
|
|
26623
28670
|
function readSessionMessages(sessionID) {
|
|
26624
|
-
const messageDir =
|
|
26625
|
-
if (!messageDir || !
|
|
28671
|
+
const messageDir = getMessageDir9(sessionID);
|
|
28672
|
+
if (!messageDir || !existsSync42(messageDir))
|
|
26626
28673
|
return [];
|
|
26627
28674
|
const messages = [];
|
|
26628
|
-
for (const file2 of
|
|
28675
|
+
for (const file2 of readdirSync16(messageDir)) {
|
|
26629
28676
|
if (!file2.endsWith(".json"))
|
|
26630
28677
|
continue;
|
|
26631
28678
|
try {
|
|
26632
|
-
const content =
|
|
28679
|
+
const content = readFileSync27(join51(messageDir, file2), "utf-8");
|
|
26633
28680
|
const meta = JSON.parse(content);
|
|
26634
28681
|
const parts = readParts2(meta.id);
|
|
26635
28682
|
messages.push({
|
|
@@ -26652,15 +28699,15 @@ function readSessionMessages(sessionID) {
|
|
|
26652
28699
|
});
|
|
26653
28700
|
}
|
|
26654
28701
|
function readParts2(messageID) {
|
|
26655
|
-
const partDir =
|
|
26656
|
-
if (!
|
|
28702
|
+
const partDir = join51(PART_STORAGE4, messageID);
|
|
28703
|
+
if (!existsSync42(partDir))
|
|
26657
28704
|
return [];
|
|
26658
28705
|
const parts = [];
|
|
26659
|
-
for (const file2 of
|
|
28706
|
+
for (const file2 of readdirSync16(partDir)) {
|
|
26660
28707
|
if (!file2.endsWith(".json"))
|
|
26661
28708
|
continue;
|
|
26662
28709
|
try {
|
|
26663
|
-
const content =
|
|
28710
|
+
const content = readFileSync27(join51(partDir, file2), "utf-8");
|
|
26664
28711
|
parts.push(JSON.parse(content));
|
|
26665
28712
|
} catch {
|
|
26666
28713
|
continue;
|
|
@@ -26669,12 +28716,12 @@ function readParts2(messageID) {
|
|
|
26669
28716
|
return parts.sort((a, b) => a.id.localeCompare(b.id));
|
|
26670
28717
|
}
|
|
26671
28718
|
function readSessionTodos(sessionID) {
|
|
26672
|
-
if (!
|
|
28719
|
+
if (!existsSync42(TODO_DIR2))
|
|
26673
28720
|
return [];
|
|
26674
|
-
const todoFiles =
|
|
28721
|
+
const todoFiles = readdirSync16(TODO_DIR2).filter((f) => f.includes(sessionID) && f.endsWith(".json"));
|
|
26675
28722
|
for (const file2 of todoFiles) {
|
|
26676
28723
|
try {
|
|
26677
|
-
const content =
|
|
28724
|
+
const content = readFileSync27(join51(TODO_DIR2, file2), "utf-8");
|
|
26678
28725
|
const data = JSON.parse(content);
|
|
26679
28726
|
if (Array.isArray(data)) {
|
|
26680
28727
|
return data.map((item) => ({
|
|
@@ -26691,13 +28738,13 @@ function readSessionTodos(sessionID) {
|
|
|
26691
28738
|
return [];
|
|
26692
28739
|
}
|
|
26693
28740
|
function readSessionTranscript(sessionID) {
|
|
26694
|
-
if (!
|
|
28741
|
+
if (!existsSync42(TRANSCRIPT_DIR2))
|
|
26695
28742
|
return 0;
|
|
26696
|
-
const transcriptFile =
|
|
26697
|
-
if (!
|
|
28743
|
+
const transcriptFile = join51(TRANSCRIPT_DIR2, `${sessionID}.jsonl`);
|
|
28744
|
+
if (!existsSync42(transcriptFile))
|
|
26698
28745
|
return 0;
|
|
26699
28746
|
try {
|
|
26700
|
-
const content =
|
|
28747
|
+
const content = readFileSync27(transcriptFile, "utf-8");
|
|
26701
28748
|
return content.trim().split(`
|
|
26702
28749
|
`).filter(Boolean).length;
|
|
26703
28750
|
} catch {
|
|
@@ -26991,14 +29038,14 @@ var INTERACTIVE_BASH_DESCRIPTION = `Execute tmux commands. Use "omo-{name}" sess
|
|
|
26991
29038
|
Blocked (use bash instead): capture-pane, save-buffer, show-buffer, pipe-pane.`;
|
|
26992
29039
|
|
|
26993
29040
|
// src/tools/interactive-bash/utils.ts
|
|
26994
|
-
var {spawn:
|
|
29041
|
+
var {spawn: spawn10 } = globalThis.Bun;
|
|
26995
29042
|
var tmuxPath = null;
|
|
26996
29043
|
var initPromise3 = null;
|
|
26997
29044
|
async function findTmuxPath() {
|
|
26998
29045
|
const isWindows2 = process.platform === "win32";
|
|
26999
29046
|
const cmd = isWindows2 ? "where" : "which";
|
|
27000
29047
|
try {
|
|
27001
|
-
const proc =
|
|
29048
|
+
const proc = spawn10([cmd, "tmux"], {
|
|
27002
29049
|
stdout: "pipe",
|
|
27003
29050
|
stderr: "pipe"
|
|
27004
29051
|
});
|
|
@@ -27012,7 +29059,7 @@ async function findTmuxPath() {
|
|
|
27012
29059
|
if (!path8) {
|
|
27013
29060
|
return null;
|
|
27014
29061
|
}
|
|
27015
|
-
const verifyProc =
|
|
29062
|
+
const verifyProc = spawn10([path8, "-V"], {
|
|
27016
29063
|
stdout: "pipe",
|
|
27017
29064
|
stderr: "pipe"
|
|
27018
29065
|
});
|
|
@@ -27126,8 +29173,8 @@ var interactive_bash = tool({
|
|
|
27126
29173
|
}
|
|
27127
29174
|
});
|
|
27128
29175
|
// src/tools/background-task/tools.ts
|
|
27129
|
-
import { existsSync as
|
|
27130
|
-
import { join as
|
|
29176
|
+
import { existsSync as existsSync43, readdirSync as readdirSync17 } from "fs";
|
|
29177
|
+
import { join as join52 } from "path";
|
|
27131
29178
|
|
|
27132
29179
|
// src/tools/background-task/constants.ts
|
|
27133
29180
|
var BACKGROUND_TASK_DESCRIPTION = `Run agent task in background. Returns task_id immediately; notifies on completion.
|
|
@@ -27137,15 +29184,15 @@ var BACKGROUND_OUTPUT_DESCRIPTION = `Get output from background task. System not
|
|
|
27137
29184
|
var BACKGROUND_CANCEL_DESCRIPTION = `Cancel running background task(s). Use all=true to cancel ALL before final answer.`;
|
|
27138
29185
|
|
|
27139
29186
|
// src/tools/background-task/tools.ts
|
|
27140
|
-
function
|
|
27141
|
-
if (!
|
|
29187
|
+
function getMessageDir10(sessionID) {
|
|
29188
|
+
if (!existsSync43(MESSAGE_STORAGE))
|
|
27142
29189
|
return null;
|
|
27143
|
-
const directPath =
|
|
27144
|
-
if (
|
|
29190
|
+
const directPath = join52(MESSAGE_STORAGE, sessionID);
|
|
29191
|
+
if (existsSync43(directPath))
|
|
27145
29192
|
return directPath;
|
|
27146
|
-
for (const dir of
|
|
27147
|
-
const sessionPath =
|
|
27148
|
-
if (
|
|
29193
|
+
for (const dir of readdirSync17(MESSAGE_STORAGE)) {
|
|
29194
|
+
const sessionPath = join52(MESSAGE_STORAGE, dir, sessionID);
|
|
29195
|
+
if (existsSync43(sessionPath))
|
|
27149
29196
|
return sessionPath;
|
|
27150
29197
|
}
|
|
27151
29198
|
return null;
|
|
@@ -27176,7 +29223,7 @@ function createBackgroundTask(manager) {
|
|
|
27176
29223
|
return `\u274C Agent parameter is required. Please specify which agent to use (e.g., "explore", "librarian", "build", etc.)`;
|
|
27177
29224
|
}
|
|
27178
29225
|
try {
|
|
27179
|
-
const messageDir =
|
|
29226
|
+
const messageDir = getMessageDir10(toolContext.sessionID);
|
|
27180
29227
|
const prevMessage = messageDir ? findNearestMessageWithFields(messageDir) : null;
|
|
27181
29228
|
const parentModel = prevMessage?.model?.providerID && prevMessage?.model?.modelID ? { providerID: prevMessage.model.providerID, modelID: prevMessage.model.modelID } : undefined;
|
|
27182
29229
|
const task = await manager.launch({
|
|
@@ -27575,7 +29622,8 @@ session_id: ${sessionID}
|
|
|
27575
29622
|
var MULTIMODAL_LOOKER_AGENT = "multimodal-looker";
|
|
27576
29623
|
var LOOK_AT_DESCRIPTION = `Analyze media files (PDFs, images, diagrams) via Gemini 2.5 Flash in separate context. Saves main context tokens.`;
|
|
27577
29624
|
// src/tools/look-at/tools.ts
|
|
27578
|
-
import { extname as extname3, basename as
|
|
29625
|
+
import { extname as extname3, basename as basename5 } from "path";
|
|
29626
|
+
import { pathToFileURL } from "url";
|
|
27579
29627
|
function inferMimeType(filePath) {
|
|
27580
29628
|
const ext = extname3(filePath).toLowerCase();
|
|
27581
29629
|
const mimeTypes = {
|
|
@@ -27609,7 +29657,7 @@ function createLookAt(ctx) {
|
|
|
27609
29657
|
async execute(args, toolContext) {
|
|
27610
29658
|
log(`[look_at] Analyzing file: ${args.file_path}, goal: ${args.goal}`);
|
|
27611
29659
|
const mimeType = inferMimeType(args.file_path);
|
|
27612
|
-
const filename =
|
|
29660
|
+
const filename = basename5(args.file_path);
|
|
27613
29661
|
const prompt = `Analyze this file and extract the requested information.
|
|
27614
29662
|
|
|
27615
29663
|
Goal: ${args.goal}
|
|
@@ -27643,7 +29691,7 @@ If the requested information is not found, clearly state what is missing.`;
|
|
|
27643
29691
|
},
|
|
27644
29692
|
parts: [
|
|
27645
29693
|
{ type: "text", text: prompt },
|
|
27646
|
-
{ type: "file", mime: mimeType, url:
|
|
29694
|
+
{ type: "file", mime: mimeType, url: pathToFileURL(args.file_path).href, filename }
|
|
27647
29695
|
]
|
|
27648
29696
|
}
|
|
27649
29697
|
});
|
|
@@ -27702,17 +29750,17 @@ var builtinTools = {
|
|
|
27702
29750
|
session_info
|
|
27703
29751
|
};
|
|
27704
29752
|
// src/features/background-agent/manager.ts
|
|
27705
|
-
import { existsSync as
|
|
27706
|
-
import { join as
|
|
27707
|
-
function
|
|
27708
|
-
if (!
|
|
29753
|
+
import { existsSync as existsSync44, readdirSync as readdirSync18 } from "fs";
|
|
29754
|
+
import { join as join53 } from "path";
|
|
29755
|
+
function getMessageDir11(sessionID) {
|
|
29756
|
+
if (!existsSync44(MESSAGE_STORAGE))
|
|
27709
29757
|
return null;
|
|
27710
|
-
const directPath =
|
|
27711
|
-
if (
|
|
29758
|
+
const directPath = join53(MESSAGE_STORAGE, sessionID);
|
|
29759
|
+
if (existsSync44(directPath))
|
|
27712
29760
|
return directPath;
|
|
27713
|
-
for (const dir of
|
|
27714
|
-
const sessionPath =
|
|
27715
|
-
if (
|
|
29761
|
+
for (const dir of readdirSync18(MESSAGE_STORAGE)) {
|
|
29762
|
+
const sessionPath = join53(MESSAGE_STORAGE, dir, sessionID);
|
|
29763
|
+
if (existsSync44(sessionPath))
|
|
27716
29764
|
return sessionPath;
|
|
27717
29765
|
}
|
|
27718
29766
|
return null;
|
|
@@ -27924,6 +29972,7 @@ class BackgroundManager {
|
|
|
27924
29972
|
this.pollingInterval = setInterval(() => {
|
|
27925
29973
|
this.pollRunningTasks();
|
|
27926
29974
|
}, 2000);
|
|
29975
|
+
this.pollingInterval.unref();
|
|
27927
29976
|
}
|
|
27928
29977
|
stopPolling() {
|
|
27929
29978
|
if (this.pollingInterval) {
|
|
@@ -27931,6 +29980,11 @@ class BackgroundManager {
|
|
|
27931
29980
|
this.pollingInterval = undefined;
|
|
27932
29981
|
}
|
|
27933
29982
|
}
|
|
29983
|
+
cleanup() {
|
|
29984
|
+
this.stopPolling();
|
|
29985
|
+
this.tasks.clear();
|
|
29986
|
+
this.notifications.clear();
|
|
29987
|
+
}
|
|
27934
29988
|
notifyParentSession(task) {
|
|
27935
29989
|
const duration3 = this.formatDuration(task.startedAt, task.completedAt);
|
|
27936
29990
|
log("[background-agent] notifyParentSession called for task:", task.id);
|
|
@@ -27949,7 +30003,7 @@ class BackgroundManager {
|
|
|
27949
30003
|
log("[background-agent] Sending notification to parent session:", { parentSessionID: task.parentSessionID });
|
|
27950
30004
|
setTimeout(async () => {
|
|
27951
30005
|
try {
|
|
27952
|
-
const messageDir =
|
|
30006
|
+
const messageDir = getMessageDir11(task.parentSessionID);
|
|
27953
30007
|
const prevMessage = messageDir ? findNearestMessageWithFields(messageDir) : null;
|
|
27954
30008
|
const modelContext = task.parentModel ?? prevMessage?.model;
|
|
27955
30009
|
const modelField = modelContext?.providerID && modelContext?.modelID ? { providerID: modelContext.providerID, modelID: modelContext.modelID } : undefined;
|
|
@@ -28150,7 +30204,8 @@ var HookNameSchema = exports_external.enum([
|
|
|
28150
30204
|
"agent-usage-reminder",
|
|
28151
30205
|
"non-interactive-env",
|
|
28152
30206
|
"interactive-bash-session",
|
|
28153
|
-
"empty-message-sanitizer"
|
|
30207
|
+
"empty-message-sanitizer",
|
|
30208
|
+
"thinking-block-validator"
|
|
28154
30209
|
]);
|
|
28155
30210
|
var AgentOverrideConfigSchema = exports_external.object({
|
|
28156
30211
|
model: exports_external.string().optional(),
|
|
@@ -28183,7 +30238,9 @@ var ClaudeCodeConfigSchema = exports_external.object({
|
|
|
28183
30238
|
commands: exports_external.boolean().optional(),
|
|
28184
30239
|
skills: exports_external.boolean().optional(),
|
|
28185
30240
|
agents: exports_external.boolean().optional(),
|
|
28186
|
-
hooks: exports_external.boolean().optional()
|
|
30241
|
+
hooks: exports_external.boolean().optional(),
|
|
30242
|
+
plugins: exports_external.boolean().optional(),
|
|
30243
|
+
plugins_override: exports_external.record(exports_external.string(), exports_external.boolean()).optional()
|
|
28187
30244
|
});
|
|
28188
30245
|
var SisyphusAgentConfigSchema = exports_external.object({
|
|
28189
30246
|
disabled: exports_external.boolean().optional(),
|
|
@@ -28191,12 +30248,45 @@ var SisyphusAgentConfigSchema = exports_external.object({
|
|
|
28191
30248
|
planner_enabled: exports_external.boolean().optional(),
|
|
28192
30249
|
replace_plan: exports_external.boolean().optional()
|
|
28193
30250
|
});
|
|
30251
|
+
var DynamicContextPruningConfigSchema = exports_external.object({
|
|
30252
|
+
enabled: exports_external.boolean().default(false),
|
|
30253
|
+
notification: exports_external.enum(["off", "minimal", "detailed"]).default("detailed"),
|
|
30254
|
+
turn_protection: exports_external.object({
|
|
30255
|
+
enabled: exports_external.boolean().default(true),
|
|
30256
|
+
turns: exports_external.number().min(1).max(10).default(3)
|
|
30257
|
+
}).optional(),
|
|
30258
|
+
protected_tools: exports_external.array(exports_external.string()).default([
|
|
30259
|
+
"task",
|
|
30260
|
+
"todowrite",
|
|
30261
|
+
"todoread",
|
|
30262
|
+
"lsp_rename",
|
|
30263
|
+
"lsp_code_action_resolve",
|
|
30264
|
+
"session_read",
|
|
30265
|
+
"session_write",
|
|
30266
|
+
"session_search"
|
|
30267
|
+
]),
|
|
30268
|
+
strategies: exports_external.object({
|
|
30269
|
+
deduplication: exports_external.object({
|
|
30270
|
+
enabled: exports_external.boolean().default(true)
|
|
30271
|
+
}).optional(),
|
|
30272
|
+
supersede_writes: exports_external.object({
|
|
30273
|
+
enabled: exports_external.boolean().default(true),
|
|
30274
|
+
aggressive: exports_external.boolean().default(false)
|
|
30275
|
+
}).optional(),
|
|
30276
|
+
purge_errors: exports_external.object({
|
|
30277
|
+
enabled: exports_external.boolean().default(true),
|
|
30278
|
+
turns: exports_external.number().min(1).max(20).default(5)
|
|
30279
|
+
}).optional()
|
|
30280
|
+
}).optional()
|
|
30281
|
+
});
|
|
28194
30282
|
var ExperimentalConfigSchema = exports_external.object({
|
|
28195
30283
|
aggressive_truncation: exports_external.boolean().optional(),
|
|
28196
30284
|
auto_resume: exports_external.boolean().optional(),
|
|
28197
30285
|
preemptive_compaction: exports_external.boolean().optional(),
|
|
28198
30286
|
preemptive_compaction_threshold: exports_external.number().min(0.5).max(0.95).optional(),
|
|
28199
|
-
truncate_all_tool_outputs: exports_external.boolean().default(true)
|
|
30287
|
+
truncate_all_tool_outputs: exports_external.boolean().default(true),
|
|
30288
|
+
dynamic_context_pruning: DynamicContextPruningConfigSchema.optional(),
|
|
30289
|
+
dcp_on_compaction_failure: exports_external.boolean().optional()
|
|
28200
30290
|
});
|
|
28201
30291
|
var OhMyOpenCodeConfigSchema = exports_external.object({
|
|
28202
30292
|
$schema: exports_external.string().optional(),
|
|
@@ -28340,28 +30430,13 @@ function loadConfigFromPath2(configPath, ctx) {
|
|
|
28340
30430
|
try {
|
|
28341
30431
|
if (fs7.existsSync(configPath)) {
|
|
28342
30432
|
const content = fs7.readFileSync(configPath, "utf-8");
|
|
28343
|
-
const rawConfig =
|
|
30433
|
+
const rawConfig = parseJsonc(content);
|
|
28344
30434
|
migrateConfigFile(configPath, rawConfig);
|
|
28345
30435
|
const result = OhMyOpenCodeConfigSchema.safeParse(rawConfig);
|
|
28346
30436
|
if (!result.success) {
|
|
28347
30437
|
const errorMsg = result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join(", ");
|
|
28348
30438
|
log(`Config validation error in ${configPath}:`, result.error.issues);
|
|
28349
30439
|
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(() => {});
|
|
28365
30440
|
return null;
|
|
28366
30441
|
}
|
|
28367
30442
|
log(`Config loaded from ${configPath}`, { agents: result.data.agents });
|
|
@@ -28371,21 +30446,6 @@ Config will be ignored. Please fix the errors above.`,
|
|
|
28371
30446
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
28372
30447
|
log(`Error loading config from ${configPath}:`, err);
|
|
28373
30448
|
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(() => {});
|
|
28389
30449
|
}
|
|
28390
30450
|
return null;
|
|
28391
30451
|
}
|
|
@@ -28416,8 +30476,12 @@ function mergeConfigs(base, override) {
|
|
|
28416
30476
|
};
|
|
28417
30477
|
}
|
|
28418
30478
|
function loadPluginConfig(directory, ctx) {
|
|
28419
|
-
const
|
|
28420
|
-
const
|
|
30479
|
+
const userBasePath = path8.join(getUserConfigDir(), "opencode", "oh-my-opencode");
|
|
30480
|
+
const userDetected = detectConfigFile(userBasePath);
|
|
30481
|
+
const userConfigPath = userDetected.format !== "none" ? userDetected.path : userBasePath + ".json";
|
|
30482
|
+
const projectBasePath = path8.join(directory, ".opencode", "oh-my-opencode");
|
|
30483
|
+
const projectDetected = detectConfigFile(projectBasePath);
|
|
30484
|
+
const projectConfigPath = projectDetected.format !== "none" ? projectDetected.path : projectBasePath + ".json";
|
|
28421
30485
|
let config3 = loadConfigFromPath2(userConfigPath, ctx) ?? {};
|
|
28422
30486
|
const projectConfig = loadConfigFromPath2(projectConfigPath, ctx);
|
|
28423
30487
|
if (projectConfig) {
|
|
@@ -28478,6 +30542,7 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
28478
30542
|
const nonInteractiveEnv = isHookEnabled("non-interactive-env") ? createNonInteractiveEnvHook(ctx) : null;
|
|
28479
30543
|
const interactiveBashSession = isHookEnabled("interactive-bash-session") ? createInteractiveBashSessionHook(ctx) : null;
|
|
28480
30544
|
const emptyMessageSanitizer = isHookEnabled("empty-message-sanitizer") ? createEmptyMessageSanitizerHook() : null;
|
|
30545
|
+
const thinkingBlockValidator = isHookEnabled("thinking-block-validator") ? createThinkingBlockValidatorHook() : null;
|
|
28481
30546
|
const backgroundManager = new BackgroundManager(ctx);
|
|
28482
30547
|
const todoContinuationEnforcer = isHookEnabled("todo-continuation-enforcer") ? createTodoContinuationEnforcer(ctx, { backgroundManager }) : null;
|
|
28483
30548
|
if (sessionRecovery && todoContinuationEnforcer) {
|
|
@@ -28504,6 +30569,7 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
28504
30569
|
await keywordDetector?.["chat.message"]?.(input, output);
|
|
28505
30570
|
},
|
|
28506
30571
|
"experimental.chat.messages.transform": async (input, output) => {
|
|
30572
|
+
await thinkingBlockValidator?.["experimental.chat.messages.transform"]?.(input, output);
|
|
28507
30573
|
await emptyMessageSanitizer?.["experimental.chat.messages.transform"]?.(input, output);
|
|
28508
30574
|
},
|
|
28509
30575
|
config: async (config3) => {
|
|
@@ -28523,14 +30589,27 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
28523
30589
|
}
|
|
28524
30590
|
}
|
|
28525
30591
|
}
|
|
30592
|
+
const pluginComponents = pluginConfig.claude_code?.plugins ?? true ? await loadAllPluginComponents({
|
|
30593
|
+
enabledPluginsOverride: pluginConfig.claude_code?.plugins_override
|
|
30594
|
+
}) : { commands: {}, skills: {}, agents: {}, mcpServers: {}, hooksConfigs: [], plugins: [], errors: [] };
|
|
30595
|
+
if (pluginComponents.plugins.length > 0) {
|
|
30596
|
+
log(`Loaded ${pluginComponents.plugins.length} Claude Code plugins`, {
|
|
30597
|
+
plugins: pluginComponents.plugins.map((p) => `${p.name}@${p.version}`)
|
|
30598
|
+
});
|
|
30599
|
+
}
|
|
30600
|
+
if (pluginComponents.errors.length > 0) {
|
|
30601
|
+
log(`Plugin load errors`, { errors: pluginComponents.errors });
|
|
30602
|
+
}
|
|
28526
30603
|
const builtinAgents = createBuiltinAgents(pluginConfig.disabled_agents, pluginConfig.agents, ctx.directory, config3.model);
|
|
28527
30604
|
const userAgents = pluginConfig.claude_code?.agents ?? true ? loadUserAgents() : {};
|
|
28528
30605
|
const projectAgents = pluginConfig.claude_code?.agents ?? true ? loadProjectAgents() : {};
|
|
30606
|
+
const pluginAgents = pluginComponents.agents;
|
|
28529
30607
|
const isSisyphusEnabled = pluginConfig.sisyphus_agent?.disabled !== true;
|
|
28530
30608
|
const builderEnabled = pluginConfig.sisyphus_agent?.default_builder_enabled ?? false;
|
|
28531
30609
|
const plannerEnabled = pluginConfig.sisyphus_agent?.planner_enabled ?? true;
|
|
28532
30610
|
const replacePlan = pluginConfig.sisyphus_agent?.replace_plan ?? true;
|
|
28533
30611
|
if (isSisyphusEnabled && builtinAgents.Sisyphus) {
|
|
30612
|
+
config3.default_agent = "Sisyphus";
|
|
28534
30613
|
const agentConfig = {
|
|
28535
30614
|
Sisyphus: builtinAgents.Sisyphus
|
|
28536
30615
|
};
|
|
@@ -28567,6 +30646,7 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
28567
30646
|
...Object.fromEntries(Object.entries(builtinAgents).filter(([k]) => k !== "Sisyphus")),
|
|
28568
30647
|
...userAgents,
|
|
28569
30648
|
...projectAgents,
|
|
30649
|
+
...pluginAgents,
|
|
28570
30650
|
...filteredConfigAgents,
|
|
28571
30651
|
build: { ...config3.agent?.build, mode: "subagent" },
|
|
28572
30652
|
...replacePlan ? { plan: { ...config3.agent?.plan, mode: "subagent" } } : {}
|
|
@@ -28576,6 +30656,7 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
28576
30656
|
...builtinAgents,
|
|
28577
30657
|
...userAgents,
|
|
28578
30658
|
...projectAgents,
|
|
30659
|
+
...pluginAgents,
|
|
28579
30660
|
...config3.agent
|
|
28580
30661
|
};
|
|
28581
30662
|
}
|
|
@@ -28611,23 +30692,21 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
28611
30692
|
config3.mcp = {
|
|
28612
30693
|
...config3.mcp,
|
|
28613
30694
|
...createBuiltinMcps(pluginConfig.disabled_mcps),
|
|
28614
|
-
...mcpResult.servers
|
|
30695
|
+
...mcpResult.servers,
|
|
30696
|
+
...pluginComponents.mcpServers
|
|
28615
30697
|
};
|
|
28616
30698
|
const userCommands = pluginConfig.claude_code?.commands ?? true ? loadUserCommands() : {};
|
|
28617
30699
|
const opencodeGlobalCommands = loadOpencodeGlobalCommands();
|
|
28618
30700
|
const systemCommands = config3.command ?? {};
|
|
28619
30701
|
const projectCommands = pluginConfig.claude_code?.commands ?? true ? loadProjectCommands() : {};
|
|
28620
30702
|
const opencodeProjectCommands = loadOpencodeProjectCommands();
|
|
28621
|
-
const userSkills = pluginConfig.claude_code?.skills ?? true ? loadUserSkillsAsCommands() : {};
|
|
28622
|
-
const projectSkills = pluginConfig.claude_code?.skills ?? true ? loadProjectSkillsAsCommands() : {};
|
|
28623
30703
|
config3.command = {
|
|
28624
30704
|
...userCommands,
|
|
28625
|
-
...userSkills,
|
|
28626
30705
|
...opencodeGlobalCommands,
|
|
28627
30706
|
...systemCommands,
|
|
28628
30707
|
...projectCommands,
|
|
28629
|
-
...
|
|
28630
|
-
...
|
|
30708
|
+
...opencodeProjectCommands,
|
|
30709
|
+
...pluginComponents.commands
|
|
28631
30710
|
};
|
|
28632
30711
|
},
|
|
28633
30712
|
event: async (input) => {
|