oh-my-opencode 2.5.4 → 2.6.0

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.
Files changed (45) hide show
  1. package/README.ja.md +13 -5
  2. package/README.ko.md +13 -5
  3. package/README.md +43 -5
  4. package/README.zh-cn.md +13 -5
  5. package/dist/cli/get-local-version/formatter.d.ts +3 -0
  6. package/dist/cli/get-local-version/index.d.ts +3 -0
  7. package/dist/cli/get-local-version/types.d.ts +13 -0
  8. package/dist/cli/index.js +1241 -108
  9. package/dist/config/index.d.ts +1 -1
  10. package/dist/config/schema.d.ts +87 -0
  11. package/dist/features/background-agent/manager.d.ts +1 -0
  12. package/dist/features/claude-code-plugin-loader/index.d.ts +3 -0
  13. package/dist/features/claude-code-plugin-loader/loader.d.ts +20 -0
  14. package/dist/features/claude-code-plugin-loader/types.d.ts +173 -0
  15. package/dist/hooks/anthropic-auto-compact/index.d.ts +1 -1
  16. package/dist/hooks/anthropic-auto-compact/pruning-deduplication.d.ts +7 -0
  17. package/dist/hooks/anthropic-auto-compact/pruning-deduplication.test.d.ts +1 -0
  18. package/dist/hooks/anthropic-auto-compact/pruning-executor.d.ts +3 -0
  19. package/dist/hooks/anthropic-auto-compact/pruning-purge-errors.d.ts +7 -0
  20. package/dist/hooks/anthropic-auto-compact/pruning-storage.d.ts +2 -0
  21. package/dist/hooks/anthropic-auto-compact/pruning-supersede.d.ts +6 -0
  22. package/dist/hooks/anthropic-auto-compact/pruning-types.d.ts +36 -0
  23. package/dist/hooks/anthropic-auto-compact/types.d.ts +5 -0
  24. package/dist/hooks/index.d.ts +1 -0
  25. package/dist/hooks/session-notification-utils.d.ts +9 -0
  26. package/dist/hooks/thinking-block-validator/index.d.ts +30 -0
  27. package/dist/index.js +2723 -644
  28. package/dist/shared/claude-config-dir.d.ts +1 -0
  29. package/dist/shared/claude-config-dir.test.d.ts +1 -0
  30. package/dist/shared/index.d.ts +2 -0
  31. package/dist/shared/jsonc-parser.d.ts +15 -0
  32. package/dist/shared/jsonc-parser.test.d.ts +1 -0
  33. package/dist/tools/ast-grep/index.d.ts +2 -90
  34. package/dist/tools/ast-grep/tools.d.ts +3 -88
  35. package/dist/tools/background-task/tools.d.ts +4 -38
  36. package/dist/tools/call-omo-agent/tools.d.ts +2 -21
  37. package/dist/tools/glob/tools.d.ts +2 -11
  38. package/dist/tools/grep/tools.d.ts +2 -13
  39. package/dist/tools/index.d.ts +3 -367
  40. package/dist/tools/interactive-bash/tools.d.ts +2 -9
  41. package/dist/tools/look-at/tools.d.ts +2 -12
  42. package/dist/tools/lsp/tools.d.ts +12 -152
  43. package/dist/tools/session-manager/tools.d.ts +5 -52
  44. package/dist/tools/slashcommand/tools.d.ts +2 -9
  45. 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 parse = (input, options) => {
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 = parse(rest, { ...options, fastpaths: false }).output;
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
- parse.fastpaths = (input, options) => {
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 = parse;
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 parse = require_parse();
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 format = opts.format || (posix ? utils.toPosixSlashes : null);
1392
+ const format2 = opts.format || (posix ? utils.toPosixSlashes : null);
1393
1393
  let match = input === glob;
1394
- let output = match && format ? format(input) : input;
1394
+ let output = match && format2 ? format2(input) : input;
1395
1395
  if (match === false) {
1396
- output = format ? format(input) : input;
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 parse(pattern, { ...options, fastpaths: false });
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 = parse.fastpaths(input, options);
1442
+ parsed.output = parse3.fastpaths(input, options);
1443
1443
  }
1444
1444
  if (!parsed.output) {
1445
- parsed = parse(input, options);
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 { targetMaxTokens = DEFAULT_TARGET_MAX_TOKENS, preserveHeaderLines = 3 } = options;
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 { result: output, truncated: false };
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 existsSync5, readdirSync as readdirSync2 } from "fs";
3371
- import { join as join7 } from "path";
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 existsSync4, mkdirSync, readFileSync as readFileSync2, readdirSync, writeFileSync } from "fs";
3384
- import { join as join6 } from "path";
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 join5 } from "path";
4268
+ import { join as join6 } from "path";
3388
4269
  var OPENCODE_STORAGE = getOpenCodeStorageDir();
3389
- var MESSAGE_STORAGE = join5(OPENCODE_STORAGE, "message");
3390
- var PART_STORAGE = join5(OPENCODE_STORAGE, "part");
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 = readFileSync2(join6(messageDir, file), "utf-8");
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 (!existsSync4(MESSAGE_STORAGE)) {
4304
+ if (!existsSync5(MESSAGE_STORAGE)) {
3424
4305
  mkdirSync(MESSAGE_STORAGE, { recursive: true });
3425
4306
  }
3426
- const directPath = join6(MESSAGE_STORAGE, sessionID);
3427
- if (existsSync4(directPath)) {
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 = join6(MESSAGE_STORAGE, dir, sessionID);
3432
- if (existsSync4(sessionPath)) {
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(join6(messageDir, `${messageID}.json`), JSON.stringify(messageMeta, null, 2));
3486
- const partDir = join6(PART_STORAGE, messageID);
3487
- if (!existsSync4(partDir)) {
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(join6(partDir, `${partID}.json`), JSON.stringify(textPart, null, 2));
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 (!existsSync5(MESSAGE_STORAGE))
4404
+ if (!existsSync6(MESSAGE_STORAGE))
3524
4405
  return null;
3525
- const directPath = join7(MESSAGE_STORAGE, sessionID);
3526
- if (existsSync5(directPath))
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 = join7(MESSAGE_STORAGE, dir, sessionID);
3530
- if (existsSync5(sessionPath))
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.$`osascript -e ${'display notification "' + esMessage + '" with title "' + esTitle + '"'}`;
4917
+ await ctx.$`${osascriptPath2} -e ${'display notification "' + esMessage + '" with title "' + esTitle + '"'}`.catch(() => {});
3910
4918
  break;
3911
4919
  }
3912
- case "linux":
3913
- await ctx.$`notify-send ${title} ${message} 2>/dev/null`.catch(() => {});
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.$`powershell -Command ${toastScript}`.catch(() => {});
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
- ctx.$`afplay ${soundPath}`.catch(() => {});
4952
+ case "darwin": {
4953
+ const afplayPath2 = await getAfplayPath();
4954
+ if (!afplayPath2)
4955
+ return;
4956
+ ctx.$`${afplayPath2} ${soundPath}`.catch(() => {});
3939
4957
  break;
3940
- case "linux":
3941
- ctx.$`paplay ${soundPath} 2>/dev/null`.catch(() => {
3942
- ctx.$`aplay ${soundPath} 2>/dev/null`.catch(() => {});
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
- case "win32":
3946
- ctx.$`powershell -Command ${"(New-Object Media.SoundPlayer '" + soundPath + "').PlaySync()"}`.catch(() => {});
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 existsSync6, mkdirSync as mkdirSync2, readdirSync as readdirSync3, readFileSync as readFileSync3, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
4122
- import { join as join9 } from "path";
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 join8 } from "path";
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 = join8(xdgData ?? "", "opencode", "storage");
4148
- var MESSAGE_STORAGE2 = join8(OPENCODE_STORAGE2, "message");
4149
- var PART_STORAGE2 = join8(OPENCODE_STORAGE2, "part");
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 (!existsSync6(MESSAGE_STORAGE2))
5192
+ if (!existsSync7(MESSAGE_STORAGE2))
4162
5193
  return "";
4163
- const directPath = join9(MESSAGE_STORAGE2, sessionID);
4164
- if (existsSync6(directPath)) {
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 = join9(MESSAGE_STORAGE2, dir, sessionID);
4169
- if (existsSync6(sessionPath)) {
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 || !existsSync6(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 = readFileSync3(join9(messageDir, file), "utf-8");
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 = join9(PART_STORAGE2, messageID);
4200
- if (!existsSync6(partDir))
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 = readFileSync3(join9(partDir, file), "utf-8");
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 = join9(PART_STORAGE2, messageID);
4238
- if (!existsSync6(partDir)) {
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(join9(partDir, `${partId}.json`), JSON.stringify(part, null, 2));
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 = join9(PART_STORAGE2, messageID);
4315
- if (!existsSync6(partDir)) {
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(join9(partDir, `${partId}.json`), JSON.stringify(part, null, 2));
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 = join9(PART_STORAGE2, messageID);
4336
- if (!existsSync6(partDir))
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 = join9(partDir, file);
4344
- const content = readFileSync3(filePath, "utf-8");
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 = join9(PART_STORAGE2, messageID);
4358
- if (!existsSync6(partDir))
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 = join9(partDir, file);
4366
- const content = readFileSync3(filePath, "utf-8");
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: spawn3 } = globalThis.Bun;
5695
+ var {spawn: spawn4 } = globalThis.Bun;
4641
5696
  import { createRequire as createRequire2 } from "module";
4642
- import { dirname, join as join11 } from "path";
4643
- import { existsSync as existsSync8 } from "fs";
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: spawn2 } = globalThis.Bun;
4649
- import { existsSync as existsSync7, mkdirSync as mkdirSync3, chmodSync, unlinkSync as unlinkSync2, appendFileSync as appendFileSync2 } from "fs";
4650
- import { join as join10 } from "path";
4651
- import { homedir as homedir4, tmpdir as tmpdir2 } from "os";
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 = join10(tmpdir2(), "comment-checker-debug.log");
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 || join10(homedir4(), ".cache");
4673
- return join10(base, "oh-my-opencode", "bin");
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 = join10(getCacheDir(), getBinaryName());
4680
- return existsSync7(binaryPath) ? binaryPath : null;
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 = spawn2(["tar", "-xzf", archivePath, "-C", destDir], {
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" ? spawn2(["powershell", "-command", `Expand-Archive -Path '${archivePath}' -DestinationPath '${destDir}' -Force`], {
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
- }) : spawn2(["unzip", "-o", archivePath, "-d", destDir], {
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 = join10(cacheDir, binaryName);
4728
- if (existsSync7(binaryPath)) {
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 (!existsSync7(cacheDir)) {
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 = join10(cacheDir, assetName);
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 (existsSync7(archivePath)) {
5810
+ if (existsSync8(archivePath)) {
4756
5811
  unlinkSync2(archivePath);
4757
5812
  }
4758
- if (process.platform !== "win32" && existsSync7(binaryPath)) {
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 = join11(tmpdir3(), "comment-checker-debug.log");
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 = join11(cliDir, "bin", binaryName);
4800
- if (existsSync8(binaryPath)) {
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 && existsSync8(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 (!existsSync8(binaryPath)) {
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 = spawn3([binaryPath], {
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 existsSync9 } from "fs";
5949
+ import { existsSync as existsSync10 } from "fs";
4895
5950
  import { tmpdir as tmpdir4 } from "os";
4896
- import { join as join12 } from "path";
5951
+ import { join as join13 } from "path";
4897
5952
  var DEBUG3 = process.env.COMMENT_CHECKER_DEBUG === "1";
4898
- var DEBUG_FILE3 = join12(tmpdir4(), "comment-checker-debug.log");
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 || !existsSync9(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 existsSync11, readFileSync as readFileSync5 } from "fs";
5047
- import { dirname as dirname2, join as join15, resolve as resolve2 } from "path";
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 existsSync10,
6106
+ existsSync as existsSync11,
5052
6107
  mkdirSync as mkdirSync4,
5053
- readFileSync as readFileSync4,
6108
+ readFileSync as readFileSync5,
5054
6109
  writeFileSync as writeFileSync3,
5055
6110
  unlinkSync as unlinkSync3
5056
6111
  } from "fs";
5057
- import { join as join14 } from "path";
6112
+ import { join as join15 } from "path";
5058
6113
 
5059
6114
  // src/hooks/directory-agents-injector/constants.ts
5060
- import { join as join13 } from "path";
5061
- var OPENCODE_STORAGE3 = join13(xdgData ?? "", "opencode", "storage");
5062
- var AGENTS_INJECTOR_STORAGE = join13(OPENCODE_STORAGE3, "directory-agents");
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 join14(AGENTS_INJECTOR_STORAGE, `${sessionID}.json`);
6122
+ return join15(AGENTS_INJECTOR_STORAGE, `${sessionID}.json`);
5068
6123
  }
5069
6124
  function loadInjectedPaths(sessionID) {
5070
6125
  const filePath = getStoragePath(sessionID);
5071
- if (!existsSync10(filePath))
6126
+ if (!existsSync11(filePath))
5072
6127
  return new Set;
5073
6128
  try {
5074
- const content = readFileSync4(filePath, "utf-8");
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 (!existsSync10(AGENTS_INJECTOR_STORAGE)) {
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 (existsSync10(filePath)) {
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 = join15(current, AGENTS_FILENAME);
5122
- if (existsSync11(agentsPath)) {
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 = readFileSync5(agentsPath, "utf-8");
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 existsSync13, readFileSync as readFileSync7 } from "fs";
5219
- import { dirname as dirname3, join as join18, resolve as resolve3 } from "path";
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 existsSync12,
6278
+ existsSync as existsSync13,
5224
6279
  mkdirSync as mkdirSync5,
5225
- readFileSync as readFileSync6,
6280
+ readFileSync as readFileSync7,
5226
6281
  writeFileSync as writeFileSync4,
5227
6282
  unlinkSync as unlinkSync4
5228
6283
  } from "fs";
5229
- import { join as join17 } from "path";
6284
+ import { join as join18 } from "path";
5230
6285
 
5231
6286
  // src/hooks/directory-readme-injector/constants.ts
5232
- import { join as join16 } from "path";
5233
- var OPENCODE_STORAGE4 = join16(xdgData ?? "", "opencode", "storage");
5234
- var README_INJECTOR_STORAGE = join16(OPENCODE_STORAGE4, "directory-readme");
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 join17(README_INJECTOR_STORAGE, `${sessionID}.json`);
6294
+ return join18(README_INJECTOR_STORAGE, `${sessionID}.json`);
5240
6295
  }
5241
6296
  function loadInjectedPaths2(sessionID) {
5242
6297
  const filePath = getStoragePath2(sessionID);
5243
- if (!existsSync12(filePath))
6298
+ if (!existsSync13(filePath))
5244
6299
  return new Set;
5245
6300
  try {
5246
- const content = readFileSync6(filePath, "utf-8");
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 (!existsSync12(README_INJECTOR_STORAGE)) {
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 (existsSync12(filePath)) {
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 = join18(current, README_FILENAME);
5294
- if (existsSync13(readmePath)) {
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 = readFileSync7(readmePath, "utf-8");
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
- } catch {}
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
- const bedrockJson = JSON.parse(responseBody);
5537
- if (typeof bedrockJson.message === "string" && isTokenLimitError(bedrockJson.message)) {
5538
- return {
5539
- currentTokens: 0,
5540
- maxTokens: 0,
5541
- errorType: "bedrock_input_too_long"
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
- } catch {}
6951
+ }
5545
6952
  }
5546
- for (const text of textSources) {
5547
- const tokens = extractTokensFromMessage(text);
5548
- if (tokens) {
5549
- return {
5550
- currentTokens: tokens.current,
5551
- maxTokens: tokens.max,
5552
- errorType: "token_limit_exceeded"
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
- if (combinedText.toLowerCase().includes("non-empty content")) {
5557
- return {
5558
- currentTokens: 0,
5559
- maxTokens: 0,
5560
- errorType: "non-empty content",
5561
- messageIndex: extractMessageIndex2(combinedText)
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
- if (isTokenLimitError(combinedText)) {
5565
- return {
5566
- currentTokens: 0,
5567
- maxTokens: 0,
5568
- errorType: "token_limit_exceeded_unknown"
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/types.ts
5575
- var RETRY_CONFIG = {
5576
- maxAttempts: 2,
5577
- initialDelayMs: 2000,
5578
- backoffFactor: 2,
5579
- maxDelayMs: 30000
5580
- };
5581
- var FALLBACK_CONFIG = {
5582
- maxRevertAttempts: 3,
5583
- minMessagesRequired: 2
5584
- };
5585
- var TRUNCATE_CONFIG = {
5586
- maxTruncateAttempts: 20,
5587
- minOutputSizeToTruncate: 500,
5588
- targetTokenRatio: 0.5,
5589
- charsPerToken: 4
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 existsSync14, readdirSync as readdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
5594
- import { homedir as homedir5 } from "os";
5595
- import { join as join19 } from "path";
5596
- var OPENCODE_STORAGE5 = join19(xdgData ?? "", "opencode", "storage");
5597
- if (process.platform === "darwin" && !existsSync14(OPENCODE_STORAGE5)) {
5598
- const localShare = join19(homedir5(), ".local", "share", "opencode", "storage");
5599
- if (existsSync14(localShare)) {
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 MESSAGE_STORAGE3 = join19(OPENCODE_STORAGE5, "message");
5604
- var PART_STORAGE3 = join19(OPENCODE_STORAGE5, "part");
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 getMessageDir3(sessionID) {
5607
- if (!existsSync14(MESSAGE_STORAGE3))
7227
+ function getMessageDir7(sessionID) {
7228
+ if (!existsSync19(MESSAGE_STORAGE7))
5608
7229
  return "";
5609
- const directPath = join19(MESSAGE_STORAGE3, sessionID);
5610
- if (existsSync14(directPath)) {
7230
+ const directPath = join24(MESSAGE_STORAGE7, sessionID);
7231
+ if (existsSync19(directPath)) {
5611
7232
  return directPath;
5612
7233
  }
5613
- for (const dir of readdirSync4(MESSAGE_STORAGE3)) {
5614
- const sessionPath = join19(MESSAGE_STORAGE3, dir, sessionID);
5615
- if (existsSync14(sessionPath)) {
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 = getMessageDir3(sessionID);
5623
- if (!messageDir || !existsSync14(messageDir))
7243
+ const messageDir = getMessageDir7(sessionID);
7244
+ if (!messageDir || !existsSync19(messageDir))
5624
7245
  return [];
5625
7246
  const messageIds = [];
5626
- for (const file of readdirSync4(messageDir)) {
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 = join19(PART_STORAGE3, messageID);
5639
- if (!existsSync14(partDir))
7259
+ const partDir = join24(PART_STORAGE3, messageID);
7260
+ if (!existsSync19(partDir))
5640
7261
  continue;
5641
- for (const file of readdirSync4(partDir)) {
7262
+ for (const file of readdirSync8(partDir)) {
5642
7263
  if (!file.endsWith(".json"))
5643
7264
  continue;
5644
7265
  try {
5645
- const partPath = join19(partDir, file);
5646
- const content = readFileSync8(partPath, "utf-8");
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 = readFileSync8(partPath, "utf-8");
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
- writeFileSync5(partPath, JSON.stringify(part, null, 2));
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 existsSync15, readdirSync as readdirSync5 } from "fs";
6259
- import { join as join20 } from "path";
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 getMessageDir4(sessionID) {
6273
- if (!existsSync15(MESSAGE_STORAGE))
7943
+ function getMessageDir8(sessionID) {
7944
+ if (!existsSync20(MESSAGE_STORAGE))
6274
7945
  return null;
6275
- const directPath = join20(MESSAGE_STORAGE, sessionID);
6276
- if (existsSync15(directPath))
7946
+ const directPath = join25(MESSAGE_STORAGE, sessionID);
7947
+ if (existsSync20(directPath))
6277
7948
  return directPath;
6278
- for (const dir of readdirSync5(MESSAGE_STORAGE)) {
6279
- const sessionPath = join20(MESSAGE_STORAGE, dir, sessionID);
6280
- if (existsSync15(sessionPath))
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 = getMessageDir4(sessionID);
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 = getMessageDir4(sessionID);
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 { homedir as homedir6 } from "os";
6765
- import { join as join21 } from "path";
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 home = homedir6();
8460
+ const claudeConfigDir = getClaudeConfigDir();
6791
8461
  const paths = [
6792
- join21(home, ".claude", "settings.json"),
6793
- join21(process.cwd(), ".claude", "settings.json"),
6794
- join21(process.cwd(), ".claude", "settings.local.json")
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 && existsSync16(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 (existsSync16(settingsPath)) {
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 existsSync17 } from "fs";
8508
+ import { existsSync as existsSync22 } from "fs";
6839
8509
  import { homedir as homedir7 } from "os";
6840
- import { join as join22 } from "path";
6841
- var USER_CONFIG_PATH = join22(homedir7(), ".config", "opencode", "opencode-cc-plugin.json");
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 join22(process.cwd(), ".opencode", "opencode-cc-plugin.json");
8513
+ return join27(process.cwd(), ".opencode", "opencode-cc-plugin.json");
6844
8514
  }
6845
8515
  async function loadConfigFromPath(path5) {
6846
- if (!existsSync17(path5)) {
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 join23 } from "path";
7027
- import { mkdirSync as mkdirSync6, appendFileSync as appendFileSync5, existsSync as existsSync18, writeFileSync as writeFileSync6, unlinkSync as unlinkSync5 } from "fs";
7028
- import { homedir as homedir8, tmpdir as tmpdir5 } from "os";
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 = join23(homedir8(), ".claude", "transcripts");
8700
+ var TRANSCRIPT_DIR = join28(getClaudeConfigDir(), "transcripts");
7031
8701
  function getTranscriptPath(sessionId) {
7032
- return join23(TRANSCRIPT_DIR, `${sessionId}.jsonl`);
8702
+ return join28(TRANSCRIPT_DIR, `${sessionId}.jsonl`);
7033
8703
  }
7034
8704
  function ensureTranscriptDir() {
7035
- if (!existsSync18(TRANSCRIPT_DIR)) {
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 = join23(tmpdir5(), `opencode-transcript-${sessionId}-${randomUUID()}.jsonl`);
7123
- writeFileSync6(tempPath, entries.join(`
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 = join23(tmpdir5(), `opencode-transcript-${sessionId}-${randomUUID()}.jsonl`);
7143
- writeFileSync6(tempPath, JSON.stringify(currentEntry) + `
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 join24 } from "path";
7355
- import { homedir as homedir9 } from "os";
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 join24(TODO_DIR, `${sessionId}-agent-${sessionId}.json`);
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 readFileSync10 } from "fs";
7783
- import { homedir as homedir10 } from "os";
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 existsSync19,
7789
- readdirSync as readdirSync6,
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 join26, relative } from "path";
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 join25 } from "path";
7797
- var OPENCODE_STORAGE6 = join25(xdgData ?? "", "opencode", "storage");
7798
- var RULES_INJECTOR_STORAGE = join25(OPENCODE_STORAGE6, "rules-injector");
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 = join26(current, marker);
7826
- if (existsSync19(markerPath)) {
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 (!existsSync19(dir))
9507
+ if (!existsSync24(dir))
7839
9508
  return;
7840
9509
  try {
7841
- const entries = readdirSync6(dir, { withFileTypes: true });
9510
+ const entries = readdirSync10(dir, { withFileTypes: true });
7842
9511
  for (const entry of entries) {
7843
- const fullPath = join26(dir, entry.name);
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 = join26(currentDir, parent, subdir);
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 = join26(homeDir, USER_RULE_DIR);
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 existsSync20,
9751
+ existsSync as existsSync25,
8083
9752
  mkdirSync as mkdirSync7,
8084
- readFileSync as readFileSync9,
8085
- writeFileSync as writeFileSync7,
9753
+ readFileSync as readFileSync14,
9754
+ writeFileSync as writeFileSync8,
8086
9755
  unlinkSync as unlinkSync6
8087
9756
  } from "fs";
8088
- import { join as join27 } from "path";
9757
+ import { join as join32 } from "path";
8089
9758
  function getStoragePath3(sessionID) {
8090
- return join27(RULES_INJECTOR_STORAGE, `${sessionID}.json`);
9759
+ return join32(RULES_INJECTOR_STORAGE, `${sessionID}.json`);
8091
9760
  }
8092
9761
  function loadInjectedRules(sessionID) {
8093
9762
  const filePath = getStoragePath3(sessionID);
8094
- if (!existsSync20(filePath))
9763
+ if (!existsSync25(filePath))
8095
9764
  return { contentHashes: new Set, realPaths: new Set };
8096
9765
  try {
8097
- const content = readFileSync9(filePath, "utf-8");
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 (!existsSync20(RULES_INJECTOR_STORAGE)) {
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
- writeFileSync7(getStoragePath3(sessionID), JSON.stringify(storageData, null, 2));
9786
+ writeFileSync8(getStoragePath3(sessionID), JSON.stringify(storageData, null, 2));
8118
9787
  }
8119
9788
  function clearInjectedRules(sessionID) {
8120
9789
  const filePath = getStoragePath3(sessionID);
8121
- if (existsSync20(filePath)) {
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 = homedir10();
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 = readFileSync10(candidate.path, "utf-8");
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).catch(() => {});
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 existsSync23,
10365
+ existsSync as existsSync28,
8697
10366
  mkdirSync as mkdirSync8,
8698
- readFileSync as readFileSync13,
8699
- writeFileSync as writeFileSync10,
10367
+ readFileSync as readFileSync18,
10368
+ writeFileSync as writeFileSync11,
8700
10369
  unlinkSync as unlinkSync7
8701
10370
  } from "fs";
8702
- import { join as join32 } from "path";
10371
+ import { join as join37 } from "path";
8703
10372
 
8704
10373
  // src/hooks/agent-usage-reminder/constants.ts
8705
- import { join as join31 } from "path";
8706
- var OPENCODE_STORAGE7 = join31(xdgData ?? "", "opencode", "storage");
8707
- var AGENT_USAGE_REMINDER_STORAGE = join31(OPENCODE_STORAGE7, "agent-usage-reminder");
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 join32(AGENT_USAGE_REMINDER_STORAGE, `${sessionID}.json`);
10421
+ return join37(AGENT_USAGE_REMINDER_STORAGE, `${sessionID}.json`);
8753
10422
  }
8754
10423
  function loadAgentUsageState(sessionID) {
8755
10424
  const filePath = getStoragePath4(sessionID);
8756
- if (!existsSync23(filePath))
10425
+ if (!existsSync28(filePath))
8757
10426
  return null;
8758
10427
  try {
8759
- const content = readFileSync13(filePath, "utf-8");
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 (!existsSync23(AGENT_USAGE_REMINDER_STORAGE)) {
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
- writeFileSync10(filePath, JSON.stringify(state2, null, 2));
10439
+ writeFileSync11(filePath, JSON.stringify(state2, null, 2));
8771
10440
  }
8772
10441
  function clearAgentUsageState(sessionID) {
8773
10442
  const filePath = getStoragePath4(sessionID);
8774
- if (existsSync23(filePath)) {
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 existsSync24,
10749
+ existsSync as existsSync29,
9070
10750
  mkdirSync as mkdirSync9,
9071
- readFileSync as readFileSync14,
9072
- writeFileSync as writeFileSync11,
10751
+ readFileSync as readFileSync19,
10752
+ writeFileSync as writeFileSync12,
9073
10753
  unlinkSync as unlinkSync8
9074
10754
  } from "fs";
9075
- import { join as join34 } from "path";
10755
+ import { join as join39 } from "path";
9076
10756
 
9077
10757
  // src/hooks/interactive-bash-session/constants.ts
9078
- import { join as join33 } from "path";
9079
- var OPENCODE_STORAGE8 = join33(xdgData ?? "", "opencode", "storage");
9080
- var INTERACTIVE_BASH_SESSION_STORAGE = join33(OPENCODE_STORAGE8, "interactive-bash-session");
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 join34(INTERACTIVE_BASH_SESSION_STORAGE, `${sessionID}.json`);
10772
+ return join39(INTERACTIVE_BASH_SESSION_STORAGE, `${sessionID}.json`);
9093
10773
  }
9094
10774
  function loadInteractiveBashSessionState(sessionID) {
9095
10775
  const filePath = getStoragePath5(sessionID);
9096
- if (!existsSync24(filePath))
10776
+ if (!existsSync29(filePath))
9097
10777
  return null;
9098
10778
  try {
9099
- const content = readFileSync14(filePath, "utf-8");
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 (!existsSync24(INTERACTIVE_BASH_SESSION_STORAGE)) {
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
- writeFileSync11(filePath, JSON.stringify(serialized, null, 2));
10800
+ writeFileSync12(filePath, JSON.stringify(serialized, null, 2));
9121
10801
  }
9122
10802
  function clearInteractiveBashSessionState(sessionID) {
9123
10803
  const filePath = getStoragePath5(sessionID);
9124
- if (existsSync24(filePath)) {
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 existsSync25, readdirSync as readdirSync7, readFileSync as readFileSync15 } from "fs";
11069
- import { homedir as homedir12 } from "os";
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 (!existsSync25(commandsDir)) {
12834
+ if (!existsSync30(commandsDir)) {
11073
12835
  return [];
11074
12836
  }
11075
- const entries = readdirSync7(commandsDir, { withFileTypes: true });
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 = join35(commandsDir, entry.name);
12842
+ const commandPath = join40(commandsDir, entry.name);
11081
12843
  const commandName = basename(entry.name, ".md");
11082
12844
  try {
11083
- const content = readFileSync15(commandPath, "utf-8");
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 = join35(homedir12(), ".claude", "commands");
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 = join35(process.cwd(), ".claude", "commands");
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 opencodeCommandsDir = join35(homedir12(), ".config", "opencode", "command");
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 = join35(process.cwd(), ".opencode", "command");
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 existsSync27, readdirSync as readdirSync9, readFileSync as readFileSync17 } from "fs";
11214
- import { homedir as homedir14 } from "os";
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 (!existsSync27(agentsDir)) {
12921
+ if (!existsSync31(agentsDir)) {
11230
12922
  return [];
11231
12923
  }
11232
- const entries = readdirSync9(agentsDir, { withFileTypes: true });
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 = join37(agentsDir, entry.name);
12929
+ const agentPath = join41(agentsDir, entry.name);
11238
12930
  const agentName = basename2(entry.name, ".md");
11239
12931
  try {
11240
- const content = readFileSync17(agentPath, "utf-8");
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 = join37(homedir14(), ".claude", "agents");
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 = join37(process.cwd(), ".claude", "agents");
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 existsSync28 } from "fs";
11286
- import { homedir as homedir15 } from "os";
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 home = homedir15();
13044
+ const claudeConfigDir = getClaudeConfigDir();
11354
13045
  const cwd = process.cwd();
11355
13046
  return [
11356
- { path: join38(home, ".claude", ".mcp.json"), scope: "user" },
11357
- { path: join38(cwd, ".mcp.json"), scope: "project" },
11358
- { path: join38(cwd, ".claude", ".mcp.json"), scope: "local" }
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 (!existsSync28(filePath)) {
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 existsSync29, readFileSync as readFileSync18 } from "fs";
11698
- import { join as join39 } from "path";
11699
- import { homedir as homedir16 } from "os";
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 (!existsSync29(path8))
13749
+ if (!existsSync34(path8))
11702
13750
  return null;
11703
13751
  try {
11704
- return JSON.parse(readFileSync18(path8, "utf-8"));
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: join39(cwd, ".opencode", "oh-my-opencode.json"),
11713
- user: join39(homedir16(), ".config", "opencode", "oh-my-opencode.json"),
11714
- opencode: join39(homedir16(), ".config", "opencode", "opencode.json")
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 (existsSync29(join39(p, cmd)) || existsSync29(join39(p, cmd + ext))) {
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
- join39(cwd, "node_modules", ".bin", cmd),
11817
- join39(cwd, "node_modules", ".bin", cmd + ext),
11818
- join39(homedir16(), ".config", "opencode", "bin", cmd),
11819
- join39(homedir16(), ".config", "opencode", "bin", cmd + ext),
11820
- join39(homedir16(), ".config", "opencode", "node_modules", ".bin", cmd),
11821
- join39(homedir16(), ".config", "opencode", "node_modules", ".bin", cmd + ext)
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 (existsSync29(p)) {
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: spawn4 } = globalThis.Bun;
11874
- import { readFileSync as readFileSync19 } from "fs";
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 = spawn4(this.server.command, {
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 = readFileSync19(absPath, "utf-8");
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 existsSync30, readFileSync as readFileSync20, writeFileSync as writeFileSync12 } from "fs";
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 (!existsSync30(dir) || !__require("fs").statSync(dir).isDirectory()) {
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 (existsSync30(__require("path").join(dir, marker))) {
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 = readFileSync20(filePath, "utf-8");
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
- writeFileSync12(filePath, lines.join(`
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
- writeFileSync12(filePath, "", "utf-8");
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 = readFileSync20(oldPath, "utf-8");
12653
- writeFileSync12(newPath, content, "utf-8");
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: () => parse3,
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: () => 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 parse = /* @__PURE__ */ _parse($ZodRealError);
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 ? parse(inst._def.input, args) : args;
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 parse(inst._def.output, result);
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, format, fnOrRegex, _params = {}) {
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 (format) {
23155
- json.format = formatMap[format] ?? format;
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 format === "string" && format.includes("int"))
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 parse3 = /* @__PURE__ */ _parse(ZodRealError);
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) => parse3(inst, data, params, { callee: inst.parse });
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(format, fnOrRegex, _params = {}) {
24280
- return _stringFormat(ZodCustomStringFormat, format, fnOrRegex, _params);
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 format = `${alg}_${enc}`;
24291
- const regex = exports_regexes[format];
26338
+ const format2 = `${alg}_${enc}`;
26339
+ const regex = exports_regexes[format2];
24292
26340
  if (!regex)
24293
- throw new Error(`Unrecognized hash format: ${format}`);
24294
- return _stringFormat(ZodCustomStringFormat, format, regex, params);
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 join41 } from "path";
25354
- import { existsSync as existsSync32, statSync as statSync4 } from "fs";
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: spawn5 } = globalThis.Bun;
25358
- import { existsSync as existsSync31, mkdirSync as mkdirSync10, chmodSync as chmodSync2, unlinkSync as unlinkSync9 } from "fs";
25359
- import { join as join40 } from "path";
25360
- import { homedir as homedir17 } from "os";
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 || join40(homedir17(), "AppData", "Local");
25386
- return join40(base2, "oh-my-opencode", "bin");
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 || join40(homedir17(), ".cache");
25390
- return join40(base, "oh-my-opencode", "bin");
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 = join40(getCacheDir3(), getBinaryName3());
25397
- return existsSync31(binaryPath) ? binaryPath : null;
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" ? spawn5([
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" }) : spawn5(["unzip", "-o", archivePath, "-d", destDir], { 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 = join40(cacheDir, binaryName);
25424
- if (existsSync31(binaryPath)) {
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 (!existsSync31(cacheDir)) {
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 = join40(cacheDir, assetName);
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 (existsSync31(archivePath)) {
27491
+ if (existsSync36(archivePath)) {
25444
27492
  unlinkSync9(archivePath);
25445
27493
  }
25446
- if (process.platform !== "win32" && existsSync31(binaryPath)) {
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 = join41(cliDir, binaryName);
25498
- if (existsSync32(sgPath) && isValidBinary(sgPath)) {
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 = join41(pkgDir, astGrepName);
25510
- if (existsSync32(binaryPath) && isValidBinary(binaryPath)) {
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 (existsSync32(path8) && isValidBinary(path8)) {
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: spawn6 } = globalThis.Bun;
25574
- import { existsSync as existsSync33 } from "fs";
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 && existsSync33(resolvedCliPath3)) {
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 && existsSync33(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 (!existsSync33(cliPath) && cliPath !== "sg") {
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 = spawn6([cliPath, ...args], {
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: spawn7 } = globalThis.Bun;
27929
+ var {spawn: spawn8 } = globalThis.Bun;
25882
27930
 
25883
27931
  // src/tools/grep/constants.ts
25884
- import { existsSync as existsSync35 } from "fs";
25885
- import { join as join43, dirname as dirname7 } from "path";
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 existsSync34, mkdirSync as mkdirSync11, chmodSync as chmodSync3, unlinkSync as unlinkSync10, readdirSync as readdirSync10 } from "fs";
25890
- import { join as join42 } from "path";
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 join42(homeDir, ".cache", "oh-my-opencode", "bin");
27941
+ return join47(homeDir, ".cache", "oh-my-opencode", "bin");
25894
27942
  }
25895
27943
  function getRgPath() {
25896
27944
  const isWindows2 = process.platform === "win32";
25897
- return join42(getInstallDir(), isWindows2 ? "rg.exe" : "rg");
27945
+ return join47(getInstallDir(), isWindows2 ? "rg.exe" : "rg");
25898
27946
  }
25899
27947
  function getInstalledRipgrepPath() {
25900
27948
  const rgPath = getRgPath();
25901
- return existsSync34(rgPath) ? rgPath : null;
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
- join43(execDir, rgName),
25925
- join43(execDir, "bin", rgName),
25926
- join43(execDir, "..", "bin", rgName),
25927
- join43(execDir, "..", "libexec", rgName)
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 (existsSync35(candidate)) {
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 = spawn7([cli.path, ...args], {
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: spawn8 } = globalThis.Bun;
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 = spawn8([cli.path, ...args], {
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 existsSync36, readdirSync as readdirSync11, readFileSync as readFileSync21 } from "fs";
26334
- import { homedir as homedir18 } from "os";
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 (!existsSync36(commandsDir)) {
28384
+ if (!existsSync41(commandsDir)) {
26338
28385
  return [];
26339
28386
  }
26340
- const entries = readdirSync11(commandsDir, { withFileTypes: true });
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 = join44(commandsDir, entry.name);
26346
- const commandName = basename3(entry.name, ".md");
28392
+ const commandPath = join49(commandsDir, entry.name);
28393
+ const commandName = basename4(entry.name, ".md");
26347
28394
  try {
26348
- const content = readFileSync21(commandPath, "utf-8");
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 userCommandsDir = join44(homedir18(), ".claude", "commands");
26374
- const projectCommandsDir = join44(process.cwd(), ".claude", "commands");
26375
- const opencodeGlobalDir = join44(homedir18(), ".config", "opencode", "command");
26376
- const opencodeProjectDir = join44(process.cwd(), ".opencode", "command");
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 join45 } from "path";
26501
- import { homedir as homedir19 } from "os";
28548
+ import { join as join50 } from "path";
26502
28549
  var OPENCODE_STORAGE9 = getOpenCodeStorageDir();
26503
- var MESSAGE_STORAGE4 = join45(OPENCODE_STORAGE9, "message");
26504
- var PART_STORAGE4 = join45(OPENCODE_STORAGE9, "part");
26505
- var TODO_DIR2 = join45(homedir19(), ".claude", "todos");
26506
- var TRANSCRIPT_DIR2 = join45(homedir19(), ".claude", "transcripts");
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 existsSync37, readdirSync as readdirSync12, readFileSync as readFileSync22 } from "fs";
26580
- import { join as join46 } from "path";
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 (!existsSync37(MESSAGE_STORAGE4))
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 readdirSync12(dir, { withFileTypes: true })) {
28634
+ for (const entry of readdirSync16(dir, { withFileTypes: true })) {
26588
28635
  if (entry.isDirectory()) {
26589
- const sessionPath = join46(dir, entry.name);
26590
- const files = readdirSync12(sessionPath);
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(MESSAGE_STORAGE4);
28649
+ scanDirectory(MESSAGE_STORAGE8);
26603
28650
  return [...new Set(sessions)];
26604
28651
  }
26605
- function getMessageDir5(sessionID) {
26606
- if (!existsSync37(MESSAGE_STORAGE4))
28652
+ function getMessageDir9(sessionID) {
28653
+ if (!existsSync42(MESSAGE_STORAGE8))
26607
28654
  return "";
26608
- const directPath = join46(MESSAGE_STORAGE4, sessionID);
26609
- if (existsSync37(directPath)) {
28655
+ const directPath = join51(MESSAGE_STORAGE8, sessionID);
28656
+ if (existsSync42(directPath)) {
26610
28657
  return directPath;
26611
28658
  }
26612
- for (const dir of readdirSync12(MESSAGE_STORAGE4)) {
26613
- const sessionPath = join46(MESSAGE_STORAGE4, dir, sessionID);
26614
- if (existsSync37(sessionPath)) {
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 getMessageDir5(sessionID) !== "";
28668
+ return getMessageDir9(sessionID) !== "";
26622
28669
  }
26623
28670
  function readSessionMessages(sessionID) {
26624
- const messageDir = getMessageDir5(sessionID);
26625
- if (!messageDir || !existsSync37(messageDir))
28671
+ const messageDir = getMessageDir9(sessionID);
28672
+ if (!messageDir || !existsSync42(messageDir))
26626
28673
  return [];
26627
28674
  const messages = [];
26628
- for (const file2 of readdirSync12(messageDir)) {
28675
+ for (const file2 of readdirSync16(messageDir)) {
26629
28676
  if (!file2.endsWith(".json"))
26630
28677
  continue;
26631
28678
  try {
26632
- const content = readFileSync22(join46(messageDir, file2), "utf-8");
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 = join46(PART_STORAGE4, messageID);
26656
- if (!existsSync37(partDir))
28702
+ const partDir = join51(PART_STORAGE4, messageID);
28703
+ if (!existsSync42(partDir))
26657
28704
  return [];
26658
28705
  const parts = [];
26659
- for (const file2 of readdirSync12(partDir)) {
28706
+ for (const file2 of readdirSync16(partDir)) {
26660
28707
  if (!file2.endsWith(".json"))
26661
28708
  continue;
26662
28709
  try {
26663
- const content = readFileSync22(join46(partDir, file2), "utf-8");
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 (!existsSync37(TODO_DIR2))
28719
+ if (!existsSync42(TODO_DIR2))
26673
28720
  return [];
26674
- const todoFiles = readdirSync12(TODO_DIR2).filter((f) => f.includes(sessionID) && f.endsWith(".json"));
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 = readFileSync22(join46(TODO_DIR2, file2), "utf-8");
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 (!existsSync37(TRANSCRIPT_DIR2))
28741
+ if (!existsSync42(TRANSCRIPT_DIR2))
26695
28742
  return 0;
26696
- const transcriptFile = join46(TRANSCRIPT_DIR2, `${sessionID}.jsonl`);
26697
- if (!existsSync37(transcriptFile))
28743
+ const transcriptFile = join51(TRANSCRIPT_DIR2, `${sessionID}.jsonl`);
28744
+ if (!existsSync42(transcriptFile))
26698
28745
  return 0;
26699
28746
  try {
26700
- const content = readFileSync22(transcriptFile, "utf-8");
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: spawn9 } = globalThis.Bun;
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 = spawn9([cmd, "tmux"], {
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 = spawn9([path8, "-V"], {
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 existsSync38, readdirSync as readdirSync13 } from "fs";
27130
- import { join as join47 } from "path";
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 getMessageDir6(sessionID) {
27141
- if (!existsSync38(MESSAGE_STORAGE))
29187
+ function getMessageDir10(sessionID) {
29188
+ if (!existsSync43(MESSAGE_STORAGE))
27142
29189
  return null;
27143
- const directPath = join47(MESSAGE_STORAGE, sessionID);
27144
- if (existsSync38(directPath))
29190
+ const directPath = join52(MESSAGE_STORAGE, sessionID);
29191
+ if (existsSync43(directPath))
27145
29192
  return directPath;
27146
- for (const dir of readdirSync13(MESSAGE_STORAGE)) {
27147
- const sessionPath = join47(MESSAGE_STORAGE, dir, sessionID);
27148
- if (existsSync38(sessionPath))
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 = getMessageDir6(toolContext.sessionID);
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 basename4 } from "path";
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 = basename4(args.file_path);
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: `file://${args.file_path}`, filename }
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 existsSync39, readdirSync as readdirSync14 } from "fs";
27706
- import { join as join48 } from "path";
27707
- function getMessageDir7(sessionID) {
27708
- if (!existsSync39(MESSAGE_STORAGE))
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 = join48(MESSAGE_STORAGE, sessionID);
27711
- if (existsSync39(directPath))
29758
+ const directPath = join53(MESSAGE_STORAGE, sessionID);
29759
+ if (existsSync44(directPath))
27712
29760
  return directPath;
27713
- for (const dir of readdirSync14(MESSAGE_STORAGE)) {
27714
- const sessionPath = join48(MESSAGE_STORAGE, dir, sessionID);
27715
- if (existsSync39(sessionPath))
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 = getMessageDir7(task.parentSessionID);
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 = JSON.parse(content);
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 userConfigPath = path8.join(getUserConfigDir(), "opencode", "oh-my-opencode.json");
28420
- const projectConfigPath = path8.join(directory, ".opencode", "oh-my-opencode.json");
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
- ...projectSkills,
28630
- ...opencodeProjectCommands
30708
+ ...opencodeProjectCommands,
30709
+ ...pluginComponents.commands
28631
30710
  };
28632
30711
  },
28633
30712
  event: async (input) => {