@khalilgharbaoui/opencode-claude-code-plugin 0.49.0 → 0.50.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.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/claude-code-language-model.ts
2
- import { join as join12 } from "path";
2
+ import { join as join13 } from "path";
3
3
 
4
4
  // src/ids.ts
5
5
  import { randomInt } from "crypto";
@@ -3514,33 +3514,97 @@ function clearCompression(sessionKey2) {
3514
3514
  }
3515
3515
 
3516
3516
  // src/mcp-bridge.ts
3517
- import * as fs4 from "fs";
3518
- import * as path5 from "path";
3519
- import * as os2 from "os";
3517
+ import * as fs5 from "fs";
3518
+ import * as path6 from "path";
3519
+ import * as os3 from "os";
3520
3520
  import * as crypto2 from "crypto";
3521
3521
  import {
3522
3522
  parse as parseJsonc,
3523
3523
  printParseErrorCode
3524
3524
  } from "jsonc-parser";
3525
+
3526
+ // src/mcp-auth.ts
3527
+ import * as fs4 from "fs";
3528
+ import * as os2 from "os";
3529
+ import * as path5 from "path";
3530
+ var EXPIRY_SKEW_SECONDS = 30;
3531
+ function opencodeStateDir() {
3532
+ const dataHome = process.env.XDG_DATA_HOME || path5.join(os2.homedir(), ".local", "share");
3533
+ return path5.join(dataHome, "opencode");
3534
+ }
3535
+ function readMcpAuthStore(stateDir) {
3536
+ const file = path5.join(stateDir, "mcp-auth.json");
3537
+ try {
3538
+ const stat = fs4.lstatSync(file);
3539
+ if (!stat.isFile()) {
3540
+ log.debug("ignoring opencode MCP auth store that is not a regular file", { path: file });
3541
+ return {};
3542
+ }
3543
+ if (typeof stat.uid === "number" && typeof process.getuid === "function" && stat.uid !== process.getuid()) {
3544
+ log.debug("ignoring opencode MCP auth store owned by another user", { path: file });
3545
+ return {};
3546
+ }
3547
+ const parsed = JSON.parse(fs4.readFileSync(file, "utf8"));
3548
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
3549
+ log.debug("opencode MCP auth store has an unexpected shape", { path: file });
3550
+ return {};
3551
+ }
3552
+ return parsed;
3553
+ } catch {
3554
+ return {};
3555
+ }
3556
+ }
3557
+ function selectBearerToken(store, serverName, serverUrl, now = Date.now()) {
3558
+ const entry = Object.prototype.hasOwnProperty.call(store, serverName) ? store[serverName] : void 0;
3559
+ if (!entry || typeof entry !== "object") return { token: null, refusal: "no-entry" };
3560
+ if (!entry.serverUrl || entry.serverUrl !== serverUrl) {
3561
+ return { token: null, refusal: "url-mismatch" };
3562
+ }
3563
+ const accessToken = entry.tokens?.accessToken;
3564
+ if (typeof accessToken !== "string" || accessToken.length === 0) {
3565
+ return { token: null, refusal: "no-token" };
3566
+ }
3567
+ const expiresAt = entry.tokens?.expiresAt;
3568
+ if (typeof expiresAt === "number" && expiresAt - now / 1e3 < EXPIRY_SKEW_SECONDS) {
3569
+ return { token: null, refusal: "expired" };
3570
+ }
3571
+ return { token: accessToken, expiresAt: typeof expiresAt === "number" ? expiresAt : void 0 };
3572
+ }
3573
+ function buildFreshnessKey(expiresAt) {
3574
+ return expiresAt === void 0 ? "no-expiry" : String(expiresAt);
3575
+ }
3576
+ function hasAuthorizationHeader(headers) {
3577
+ return Object.keys(headers).some((key) => key.toLowerCase() === "authorization");
3578
+ }
3579
+ function injectBearerHeaders(server2, accessToken) {
3580
+ const existing = server2.headers && typeof server2.headers === "object" && !Array.isArray(server2.headers) ? server2.headers : {};
3581
+ if (hasAuthorizationHeader(existing)) return { ...server2 };
3582
+ return {
3583
+ ...server2,
3584
+ headers: { ...existing, Authorization: `Bearer ${accessToken}` }
3585
+ };
3586
+ }
3587
+
3588
+ // src/mcp-bridge.ts
3525
3589
  var FILE_NAMES = ["opencode.jsonc", "opencode.json", "config.json"];
3526
3590
  var PROJECT_FILE_NAMES = ["opencode.json", "opencode.jsonc"];
3527
3591
  function fileExists(p) {
3528
3592
  try {
3529
- return fs4.statSync(p).isFile();
3593
+ return fs5.statSync(p).isFile();
3530
3594
  } catch {
3531
3595
  return false;
3532
3596
  }
3533
3597
  }
3534
3598
  function dirExists(p) {
3535
3599
  try {
3536
- return fs4.statSync(p).isDirectory();
3600
+ return fs5.statSync(p).isDirectory();
3537
3601
  } catch {
3538
3602
  return false;
3539
3603
  }
3540
3604
  }
3541
3605
  function readAndParse(file) {
3542
3606
  try {
3543
- const raw = fs4.readFileSync(file, "utf8");
3607
+ const raw = fs5.readFileSync(file, "utf8");
3544
3608
  const errors = [];
3545
3609
  const parsed = parseJsonc(raw, errors, { allowTrailingComma: true });
3546
3610
  if (errors.length > 0) {
@@ -3576,14 +3640,14 @@ function deepMerge(target, source) {
3576
3640
  }
3577
3641
  function walkUp(opts) {
3578
3642
  const out = [];
3579
- let current = path5.resolve(opts.start);
3643
+ let current = path6.resolve(opts.start);
3580
3644
  while (true) {
3581
3645
  for (const target of opts.targets) {
3582
- const candidate = path5.join(current, target);
3646
+ const candidate = path6.join(current, target);
3583
3647
  if (opts.predicate(candidate)) out.push(candidate);
3584
3648
  }
3585
- if (opts.stop && current === path5.resolve(opts.stop)) break;
3586
- const parent = path5.dirname(current);
3649
+ if (opts.stop && current === path6.resolve(opts.stop)) break;
3650
+ const parent = path6.dirname(current);
3587
3651
  if (parent === current) break;
3588
3652
  current = parent;
3589
3653
  }
@@ -3591,28 +3655,28 @@ function walkUp(opts) {
3591
3655
  }
3592
3656
  function detectWorktree(cwd) {
3593
3657
  const override = process.env.OPENCODE_WORKTREE;
3594
- if (override) return path5.resolve(override);
3595
- let current = path5.resolve(cwd);
3658
+ if (override) return path6.resolve(override);
3659
+ let current = path6.resolve(cwd);
3596
3660
  while (true) {
3597
- const gitPath = path5.join(current, ".git");
3661
+ const gitPath = path6.join(current, ".git");
3598
3662
  try {
3599
- if (fs4.existsSync(gitPath)) return current;
3663
+ if (fs5.existsSync(gitPath)) return current;
3600
3664
  } catch {
3601
3665
  }
3602
- const parent = path5.dirname(current);
3666
+ const parent = path6.dirname(current);
3603
3667
  if (parent === current) return void 0;
3604
3668
  current = parent;
3605
3669
  }
3606
3670
  }
3607
3671
  function globalConfigDir() {
3608
- const xdg = process.env.XDG_CONFIG_HOME ?? path5.join(os2.homedir(), ".config");
3609
- return path5.join(xdg, "opencode");
3672
+ const xdg = process.env.XDG_CONFIG_HOME ?? path6.join(os3.homedir(), ".config");
3673
+ return path6.join(xdg, "opencode");
3610
3674
  }
3611
3675
  function loadGlobalConfig(hostApi = "v1") {
3612
3676
  const dir = globalConfigDir();
3613
3677
  let merged = {};
3614
3678
  for (const name of FILE_NAMES.slice().reverse()) {
3615
- const file = path5.join(dir, name);
3679
+ const file = path6.join(dir, name);
3616
3680
  if (!fileExists(file)) continue;
3617
3681
  const parsed = readAndParse(file);
3618
3682
  if (parsed) merged = mergeConfigLayer(merged, parsed, hostApi);
@@ -3622,7 +3686,7 @@ function loadGlobalConfig(hostApi = "v1") {
3622
3686
  function loadProjectFilesInDir(dir, hostApi = "v1") {
3623
3687
  let merged = {};
3624
3688
  for (const name of PROJECT_FILE_NAMES) {
3625
- const file = path5.join(dir, name);
3689
+ const file = path6.join(dir, name);
3626
3690
  if (!fileExists(file)) continue;
3627
3691
  const parsed = readAndParse(file);
3628
3692
  if (parsed) merged = mergeConfigLayer(merged, parsed, hostApi);
@@ -3633,7 +3697,7 @@ function dotOpencodeDirs(cwd, worktree) {
3633
3697
  const dirs = [];
3634
3698
  const seen = /* @__PURE__ */ new Set();
3635
3699
  const push = (p) => {
3636
- const abs = path5.resolve(p);
3700
+ const abs = path6.resolve(p);
3637
3701
  if (!seen.has(abs) && dirExists(abs)) {
3638
3702
  seen.add(abs);
3639
3703
  dirs.push(abs);
@@ -3647,9 +3711,9 @@ function dotOpencodeDirs(cwd, worktree) {
3647
3711
  })) {
3648
3712
  push(dir);
3649
3713
  }
3650
- const home = os2.homedir();
3714
+ const home = os3.homedir();
3651
3715
  if (home) {
3652
- const homeDot = path5.join(home, ".opencode");
3716
+ const homeDot = path6.join(home, ".opencode");
3653
3717
  if (dirExists(homeDot)) push(homeDot);
3654
3718
  }
3655
3719
  const envDir = process.env.OPENCODE_CONFIG_DIR;
@@ -3667,7 +3731,33 @@ function substituteEnvPlaceholders(source) {
3667
3731
  }
3668
3732
  return out;
3669
3733
  }
3670
- function translateServer(name, spec) {
3734
+ var warnedExpiredBearer = /* @__PURE__ */ new Set();
3735
+ function withBearerToken(name, spec, url, auth) {
3736
+ if (!auth) return spec;
3737
+ const lookup = selectBearerToken(auth.store, name, url);
3738
+ if (lookup.token === null) {
3739
+ if (lookup.refusal === "expired" && !warnedExpiredBearer.has(name)) {
3740
+ warnedExpiredBearer.add(name);
3741
+ log.warn(
3742
+ "opencode's stored OAuth token for this MCP server has expired, so the bridged server carries no credential; re-authenticate it in opencode",
3743
+ { server: name }
3744
+ );
3745
+ } else {
3746
+ log.debug("no opencode OAuth token for bridged MCP server", {
3747
+ server: name,
3748
+ reason: lookup.refusal
3749
+ });
3750
+ }
3751
+ return spec;
3752
+ }
3753
+ log.info("bridged MCP server carries opencode's stored OAuth token", { server: name });
3754
+ return injectBearerHeaders(spec, lookup.token);
3755
+ }
3756
+ function bearerAuthContext(options) {
3757
+ if (!options?.oauthTokens) return void 0;
3758
+ return { store: readMcpAuthStore(options.stateDir ?? opencodeStateDir()) };
3759
+ }
3760
+ function translateServer(name, spec, auth) {
3671
3761
  if (spec.enabled === false) return null;
3672
3762
  const type = spec.type;
3673
3763
  if (type === "local") {
@@ -3693,7 +3783,7 @@ function translateServer(name, spec) {
3693
3783
  log.warn("skipping remote MCP server with no url", { name });
3694
3784
  return null;
3695
3785
  }
3696
- const out = {
3786
+ let out = {
3697
3787
  type: "http",
3698
3788
  url: spec.url
3699
3789
  };
@@ -3702,7 +3792,7 @@ function translateServer(name, spec) {
3702
3792
  spec.headers
3703
3793
  );
3704
3794
  }
3705
- return out;
3795
+ return withBearerToken(name, out, spec.url, auth);
3706
3796
  }
3707
3797
  log.warn("skipping MCP server with unknown type", {
3708
3798
  name,
@@ -3753,18 +3843,19 @@ function mergeMcp(target, source) {
3753
3843
  return out;
3754
3844
  }
3755
3845
  var MCP_PENDING_STATUS = "pending";
3756
- function bridgeOpencodeMcp(cwd, runtimeStatus, excludeServers, hostApi = "v1") {
3846
+ function bridgeOpencodeMcp(cwd, runtimeStatus, excludeServers, hostApi = "v1", auth) {
3757
3847
  const {
3758
3848
  servers: merged,
3759
3849
  enabledServerNames: allEnabledServerNames,
3760
3850
  hash
3761
- } = mergeOpencodeMcp(cwd, runtimeStatus, hostApi);
3851
+ } = mergeOpencodeMcp(cwd, runtimeStatus, hostApi, auth);
3852
+ const authContext = bearerAuthContext(auth);
3762
3853
  const servers = {};
3763
3854
  const bridgedServerNames = [];
3764
3855
  for (const [name, spec] of Object.entries(merged)) {
3765
3856
  if (!spec || typeof spec !== "object") continue;
3766
3857
  if (excludeServers?.has(name)) continue;
3767
- const translated = translateServer(name, spec);
3858
+ const translated = translateServer(name, spec, authContext);
3768
3859
  if (translated) {
3769
3860
  servers[name] = translated;
3770
3861
  bridgedServerNames.push(name);
@@ -3796,7 +3887,7 @@ function opencodeConfigLayers(cwd, hostApi = "v1") {
3796
3887
  const projectDirs = [];
3797
3888
  const seenProjectDirs = /* @__PURE__ */ new Set();
3798
3889
  for (const f of projectFiles) {
3799
- const d = path5.dirname(f);
3890
+ const d = path6.dirname(f);
3800
3891
  if (!seenProjectDirs.has(d)) {
3801
3892
  seenProjectDirs.add(d);
3802
3893
  projectDirs.push(d);
@@ -3822,7 +3913,7 @@ function loadMergedOpencodeConfig(cwd, hostApi = "v1") {
3822
3913
  for (const layer of opencodeConfigLayers(cwd, hostApi)) merged = mergeConfigLayer(merged, layer, hostApi);
3823
3914
  return merged;
3824
3915
  }
3825
- function mergeOpencodeMcp(cwd, runtimeStatus, hostApi = "v1") {
3916
+ function mergeOpencodeMcp(cwd, runtimeStatus, hostApi = "v1", auth) {
3826
3917
  let merged = {};
3827
3918
  for (const layer of opencodeConfigLayers(cwd, hostApi)) {
3828
3919
  const servers = extractMcpBlock(layer);
@@ -3846,7 +3937,19 @@ function mergeOpencodeMcp(cwd, runtimeStatus, hostApi = "v1") {
3846
3937
  enabledServerNames.push(name);
3847
3938
  }
3848
3939
  const mergedBody = JSON.stringify({ mcpServers: merged }, null, 2);
3849
- const hash = crypto2.createHash("sha256").update(mergedBody).digest("hex").slice(0, 12);
3940
+ const hasher = crypto2.createHash("sha256").update(mergedBody);
3941
+ const authContext = bearerAuthContext(auth);
3942
+ if (authContext) {
3943
+ for (const [name, spec] of Object.entries(merged)) {
3944
+ if (!spec || typeof spec !== "object") continue;
3945
+ const server2 = spec;
3946
+ if (server2.type !== "remote" || typeof server2.url !== "string") continue;
3947
+ const lookup = selectBearerToken(authContext.store, name, server2.url);
3948
+ if (lookup.token === null) continue;
3949
+ hasher.update(`\0bearer:${name}:${buildFreshnessKey(lookup.expiresAt)}`);
3950
+ }
3951
+ }
3952
+ const hash = hasher.digest("hex").slice(0, 12);
3850
3953
  return { servers: merged, enabledServerNames, hash };
3851
3954
  }
3852
3955
  function finishBridge(input) {
@@ -3863,13 +3966,13 @@ function finishBridge(input) {
3863
3966
  }
3864
3967
  const body = JSON.stringify({ mcpServers: servers }, null, 2);
3865
3968
  const bodyDigest = crypto2.createHash("sha256").update(body).digest("hex").slice(0, 12);
3866
- const outPath = path5.join(
3969
+ const outPath = path6.join(
3867
3970
  pluginTmpDir(),
3868
3971
  `mcp-${bodyDigest}.json`
3869
3972
  );
3870
3973
  try {
3871
3974
  if (!fileExists(outPath)) {
3872
- fs4.writeFileSync(outPath, body, { encoding: "utf8", mode: 384 });
3975
+ fs5.writeFileSync(outPath, body, { encoding: "utf8", mode: 384 });
3873
3976
  }
3874
3977
  } catch (e) {
3875
3978
  log.warn("failed to write bridged MCP config", {
@@ -4536,17 +4639,17 @@ function tailIsReplyOnly(history, from) {
4536
4639
  }
4537
4640
 
4538
4641
  // src/session-resume-store.ts
4539
- import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync2, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "fs";
4540
- import { homedir as homedir4 } from "os";
4541
- import { join as join5 } from "path";
4642
+ import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync3, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "fs";
4643
+ import { homedir as homedir5 } from "os";
4644
+ import { join as join6 } from "path";
4542
4645
  var MAX_RESUME_RECORDS = 256;
4543
4646
  var RESUME_RECORD_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
4544
4647
  var records = null;
4545
4648
  var pathOverride = null;
4546
4649
  function resumeStorePath() {
4547
4650
  if (pathOverride) return pathOverride;
4548
- const stateRoot = process.env.XDG_STATE_HOME || join5(homedir4(), ".local", "state");
4549
- return join5(stateRoot, "opencode-claude-code-plugin", "claude-sessions.json");
4651
+ const stateRoot = process.env.XDG_STATE_HOME || join6(homedir5(), ".local", "state");
4652
+ return join6(stateRoot, "opencode-claude-code-plugin", "claude-sessions.json");
4550
4653
  }
4551
4654
  function isDigest(value) {
4552
4655
  const entry = value;
@@ -4560,7 +4663,7 @@ function load() {
4560
4663
  if (records) return records;
4561
4664
  records = /* @__PURE__ */ new Map();
4562
4665
  try {
4563
- const parsed = JSON.parse(readFileSync2(resumeStorePath(), "utf8"));
4666
+ const parsed = JSON.parse(readFileSync3(resumeStorePath(), "utf8"));
4564
4667
  const entries = parsed && typeof parsed === "object" ? parsed.sessions : null;
4565
4668
  if (entries && typeof entries === "object") {
4566
4669
  const cutoff = Date.now() - RESUME_RECORD_MAX_AGE_MS;
@@ -4575,7 +4678,7 @@ function save(update) {
4575
4678
  const file = resumeStorePath();
4576
4679
  const merged = /* @__PURE__ */ new Map();
4577
4680
  try {
4578
- const onDisk = JSON.parse(readFileSync2(file, "utf8"))?.sessions;
4681
+ const onDisk = JSON.parse(readFileSync3(file, "utf8"))?.sessions;
4579
4682
  if (onDisk && typeof onDisk === "object") {
4580
4683
  for (const [key, record] of Object.entries(onDisk)) {
4581
4684
  if (isRecord2(record)) merged.set(key, record);
@@ -4588,7 +4691,7 @@ function save(update) {
4588
4691
  const kept = [...merged.entries()].filter(([, record]) => record.updatedAt >= cutoff).sort((a, b) => a[1].updatedAt - b[1].updatedAt).slice(-MAX_RESUME_RECORDS);
4589
4692
  records = new Map(kept);
4590
4693
  try {
4591
- mkdirSync3(join5(file, ".."), { recursive: true, mode: 448 });
4694
+ mkdirSync3(join6(file, ".."), { recursive: true, mode: 448 });
4592
4695
  const tmp = `${file}.${process.pid}.tmp`;
4593
4696
  writeFileSync3(tmp, JSON.stringify({ version: 1, sessions: Object.fromEntries(kept) }), {
4594
4697
  mode: 384
@@ -6037,7 +6140,7 @@ async function handleBtwCommand(client, input, options = {}) {
6037
6140
  // src/diagnostic-bundle.ts
6038
6141
  import { createHash as createHash4, randomBytes as randomBytes2 } from "crypto";
6039
6142
  import { openSync, readSync, closeSync, fstatSync, realpathSync } from "fs";
6040
- import { homedir as homedir5 } from "os";
6143
+ import { homedir as homedir6 } from "os";
6041
6144
 
6042
6145
  // src/log-messages.ts
6043
6146
  var PLUGIN_LOG_MESSAGES = /* @__PURE__ */ new Set([
@@ -6133,6 +6236,7 @@ var PLUGIN_LOG_MESSAGES = /* @__PURE__ */ new Set([
6133
6236
  "no proxyTools entry was recognised; nothing will be proxied this turn",
6134
6237
  "no stdout after envelope write; respawning claude process to resume conversation",
6135
6238
  "opencode 2 event subscription ended; deleted sessions keep their claude process until idle eviction",
6239
+ "opencode's stored OAuth token for this MCP server has expired, so the bridged server carries no credential; re-authenticate it in opencode",
6136
6240
  "pending proxy call arrived after stream close; rejecting",
6137
6241
  'permissionMode "plan" is enforced: claude cannot edit files or run commands, and --dangerously-skip-permissions is deliberately not passed so it stays that way. Headless Claude Code is not offered an ExitPlanMode tool, so nothing releases plan mode mid-session; approving a plan in chat does not unlock writes. Leaving plan mode means changing the config and restarting opencode.',
6138
6242
  `permissionPreset "read-only": this claude CLI has no --permission-prompts (2.1.263+), so permission requests are denied by the plugin's own can_use_tool handler instead of by the CLI. Same outcome, one layer fewer.`,
@@ -6398,7 +6502,7 @@ function createIdHasher(salt = randomBytes2(16).toString("hex")) {
6398
6502
  return (raw) => createHash4("sha256").update(salt).update("\0").update(raw).digest("hex").slice(0, 8);
6399
6503
  }
6400
6504
  function createRedactionContext(options = {}) {
6401
- const home = options.home ?? homedir5();
6505
+ const home = options.home ?? homedir6();
6402
6506
  const homes = /* @__PURE__ */ new Set();
6403
6507
  if (home) homes.add(home);
6404
6508
  if (home) {
@@ -6583,8 +6687,8 @@ function redactLogLine(raw, context) {
6583
6687
  if (!parsed.data || Object.keys(parsed.data).length === 0) return head;
6584
6688
  return `${head} ${JSON.stringify(sanitizeLogData(parsed.data, context))}`;
6585
6689
  }
6586
- function readLogTail(path14, maxBytes = BUNDLE_TAIL_BYTES) {
6587
- const fd = openSync(path14, "r");
6690
+ function readLogTail(path15, maxBytes = BUNDLE_TAIL_BYTES) {
6691
+ const fd = openSync(path15, "r");
6588
6692
  try {
6589
6693
  const size = fstatSync(fd).size;
6590
6694
  const length = Math.min(size, maxBytes);
@@ -6789,14 +6893,14 @@ async function fetchPlanUsage(cliPath, options = {}) {
6789
6893
  }
6790
6894
 
6791
6895
  // src/stale-build.ts
6792
- import * as fs6 from "fs";
6793
- import * as path7 from "path";
6896
+ import * as fs7 from "fs";
6897
+ import * as path8 from "path";
6794
6898
  import { fileURLToPath as fileURLToPath2 } from "url";
6795
6899
 
6796
6900
  // src/startup-diagnostics.ts
6797
6901
  import { execFile as execFile3 } from "child_process";
6798
- import * as fs5 from "fs";
6799
- import * as path6 from "path";
6902
+ import * as fs6 from "fs";
6903
+ import * as path7 from "path";
6800
6904
  import { promisify as promisify2 } from "util";
6801
6905
  import { fileURLToPath } from "url";
6802
6906
 
@@ -6831,8 +6935,8 @@ var cachedPluginVersion;
6831
6935
  function pluginVersion() {
6832
6936
  if (cachedPluginVersion) return cachedPluginVersion;
6833
6937
  try {
6834
- const here = path6.dirname(fileURLToPath(import.meta.url));
6835
- const raw = fs5.readFileSync(path6.join(here, "..", "package.json"), "utf8");
6938
+ const here = path7.dirname(fileURLToPath(import.meta.url));
6939
+ const raw = fs6.readFileSync(path7.join(here, "..", "package.json"), "utf8");
6836
6940
  const version = JSON.parse(raw).version;
6837
6941
  cachedPluginVersion = typeof version === "string" ? version : "unknown";
6838
6942
  } catch {
@@ -6856,7 +6960,7 @@ var opencodeVersionProbe;
6856
6960
  function detectOpencodeVersion(execPath = process.execPath) {
6857
6961
  if (opencodeVersionProbe) return opencodeVersionProbe;
6858
6962
  opencodeVersionProbe = (async () => {
6859
- if (!path6.basename(execPath).toLowerCase().includes("opencode")) {
6963
+ if (!path7.basename(execPath).toLowerCase().includes("opencode")) {
6860
6964
  log.debug("skipping opencode version probe: execPath is not opencode", { execPath });
6861
6965
  return void 0;
6862
6966
  }
@@ -7085,10 +7189,10 @@ function defaultEntryPath() {
7085
7189
  }
7086
7190
  function readOnDiskBuild(entryPath) {
7087
7191
  try {
7088
- const raw = fs6.readFileSync(path7.join(path7.dirname(entryPath), "..", "package.json"), "utf8");
7192
+ const raw = fs7.readFileSync(path8.join(path8.dirname(entryPath), "..", "package.json"), "utf8");
7089
7193
  const version = JSON.parse(raw).version;
7090
7194
  if (typeof version !== "string" || version.length === 0) return void 0;
7091
- const stat = fs6.statSync(entryPath);
7195
+ const stat = fs7.statSync(entryPath);
7092
7196
  return { version, mtimeMs: stat.mtimeMs, size: stat.size };
7093
7197
  } catch {
7094
7198
  return void 0;
@@ -7099,7 +7203,7 @@ function recordLoadedBuild() {
7099
7203
  let mtimeMs;
7100
7204
  let size;
7101
7205
  try {
7102
- const stat = fs6.statSync(entryPath);
7206
+ const stat = fs7.statSync(entryPath);
7103
7207
  mtimeMs = stat.mtimeMs;
7104
7208
  size = stat.size;
7105
7209
  } catch {
@@ -7537,7 +7641,7 @@ Could not build the report: ${message}`;
7537
7641
 
7538
7642
  // src/agent-models.ts
7539
7643
  import { readFile, readdir } from "fs/promises";
7540
- import path8 from "path";
7644
+ import path9 from "path";
7541
7645
 
7542
7646
  // src/models.ts
7543
7647
  var PROVIDER_ID = "claude-code";
@@ -8103,13 +8207,13 @@ async function readAgentMarkdownRecords(directories) {
8103
8207
  const name = entry.slice(0, -3);
8104
8208
  if (records2[name]) continue;
8105
8209
  try {
8106
- const text = await readFile(path8.join(directory, entry), "utf8");
8210
+ const text = await readFile(path9.join(directory, entry), "utf8");
8107
8211
  records2[name] = parseAgentFrontmatter(text);
8108
8212
  const prompt = agentMarkdownBody(text);
8109
8213
  if (prompt) records2[name].prompt = prompt;
8110
8214
  } catch (err) {
8111
8215
  log.debug("failed to read agent markdown", {
8112
- file: path8.join(directory, entry),
8216
+ file: path9.join(directory, entry),
8113
8217
  error: String(err)
8114
8218
  });
8115
8219
  }
@@ -8121,12 +8225,12 @@ function agentDirectories(home, projectDirectory) {
8121
8225
  const directories = [];
8122
8226
  if (projectDirectory) {
8123
8227
  for (const name of AGENT_DIR_NAMES) {
8124
- directories.push(path8.join(projectDirectory, ".opencode", name));
8228
+ directories.push(path9.join(projectDirectory, ".opencode", name));
8125
8229
  }
8126
8230
  }
8127
8231
  if (home) {
8128
8232
  for (const name of AGENT_DIR_NAMES) {
8129
- directories.push(path8.join(home, ".config", "opencode", name));
8233
+ directories.push(path9.join(home, ".config", "opencode", name));
8130
8234
  }
8131
8235
  }
8132
8236
  return directories;
@@ -8442,6 +8546,17 @@ function clipWithMarker(text, max) {
8442
8546
  return `${text.slice(0, max)}
8443
8547
  \u2026[truncated ${text.length - max} chars]`;
8444
8548
  }
8549
+ var MAX_REPLAY_MESSAGE_CHARS = 2e3;
8550
+ var MAX_REPLAY_LAST_REPLY_CHARS = 12e3;
8551
+ var MAX_REPLAY_HEAD_CHARS = 1e3;
8552
+ function clipMiddle(text, max) {
8553
+ if (text.length <= max) return text;
8554
+ const head = Math.min(Math.floor(max / 4), MAX_REPLAY_HEAD_CHARS);
8555
+ const tail = max - head;
8556
+ return `${text.slice(0, head)}
8557
+ \u2026[${text.length - max} chars omitted]\u2026
8558
+ ${text.slice(-tail)}`;
8559
+ }
8445
8560
  function renderToolInput(input) {
8446
8561
  let raw;
8447
8562
  try {
@@ -8510,13 +8625,20 @@ function compactConversationHistory(prompt, opts = {}) {
8510
8625
  return null;
8511
8626
  }
8512
8627
  const historyParts = [];
8628
+ let lastAssistant = -1;
8629
+ for (let i = conversationMessages.length - 2; i >= 0; i--) {
8630
+ if (conversationMessages[i].role === "assistant") {
8631
+ lastAssistant = i;
8632
+ break;
8633
+ }
8634
+ }
8513
8635
  for (let i = 0; i < conversationMessages.length - 1; i++) {
8514
8636
  const msg = conversationMessages[i];
8515
8637
  const role = msg.role === "user" ? "User" : msg.role === "assistant" ? "Assistant" : "Tool";
8516
8638
  const { text } = renderMessageContentForCompaction(msg);
8517
8639
  if (text.trim()) {
8518
- const truncated = text.length > 2e3 ? text.slice(0, 2e3) + "..." : text;
8519
- historyParts.push(`${role}: ${truncated}`);
8640
+ const max = i === lastAssistant ? MAX_REPLAY_LAST_REPLY_CHARS : MAX_REPLAY_MESSAGE_CHARS;
8641
+ historyParts.push(`${role}: ${clipMiddle(text, max)}`);
8520
8642
  }
8521
8643
  }
8522
8644
  if (historyParts.length === 0) {
@@ -8726,14 +8848,14 @@ function getTrailingUserMessages(prompt, opts = {}) {
8726
8848
 
8727
8849
  // src/skill-bridge.ts
8728
8850
  import * as crypto3 from "crypto";
8729
- import * as fs7 from "fs";
8730
- import * as os3 from "os";
8731
- import * as path9 from "path";
8851
+ import * as fs8 from "fs";
8852
+ import * as os4 from "os";
8853
+ import * as path10 from "path";
8732
8854
  import { fileURLToPath as fileURLToPath3 } from "url";
8733
8855
  function bundledSkillsDir() {
8734
8856
  try {
8735
8857
  const here = fileURLToPath3(import.meta.url);
8736
- const dir = path9.resolve(path9.dirname(here), "..", "skills");
8858
+ const dir = path10.resolve(path10.dirname(here), "..", "skills");
8737
8859
  return dirExists2(dir) ? dir : null;
8738
8860
  } catch {
8739
8861
  return null;
@@ -8741,16 +8863,16 @@ function bundledSkillsDir() {
8741
8863
  }
8742
8864
  function dirExists2(p) {
8743
8865
  try {
8744
- return fs7.statSync(p).isDirectory();
8866
+ return fs8.statSync(p).isDirectory();
8745
8867
  } catch {
8746
8868
  return false;
8747
8869
  }
8748
8870
  }
8749
8871
  function realPath(p) {
8750
8872
  try {
8751
- return fs7.realpathSync(p);
8873
+ return fs8.realpathSync(p);
8752
8874
  } catch {
8753
- return path9.resolve(p);
8875
+ return path10.resolve(p);
8754
8876
  }
8755
8877
  }
8756
8878
  function envEnabled(name) {
@@ -8771,10 +8893,10 @@ function declaredSkillName(source) {
8771
8893
  return SAFE_SKILL_NAME.test(name) ? name : null;
8772
8894
  }
8773
8895
  function readSkill(root, entry) {
8774
- const dir = path9.join(root, entry);
8896
+ const dir = path10.join(root, entry);
8775
8897
  let source;
8776
8898
  try {
8777
- source = fs7.readFileSync(path9.join(dir, "SKILL.md"), "utf8");
8899
+ source = fs8.readFileSync(path10.join(dir, "SKILL.md"), "utf8");
8778
8900
  } catch {
8779
8901
  return null;
8780
8902
  }
@@ -8792,47 +8914,47 @@ function skillRoots(cwd) {
8792
8914
  const roots = [];
8793
8915
  const seen = /* @__PURE__ */ new Set();
8794
8916
  const push = (p) => {
8795
- const abs = path9.resolve(p);
8917
+ const abs = path10.resolve(p);
8796
8918
  if (seen.has(abs)) return;
8797
8919
  seen.add(abs);
8798
8920
  if (dirExists2(abs)) roots.push(abs);
8799
8921
  };
8800
- const home = os3.homedir();
8801
- const homeDir = home ? path9.resolve(home) : null;
8802
- let current = path9.resolve(cwd);
8922
+ const home = os4.homedir();
8923
+ const homeDir = home ? path10.resolve(home) : null;
8924
+ let current = path10.resolve(cwd);
8803
8925
  while (true) {
8804
- push(path9.join(current, ".opencode", "skills"));
8926
+ push(path10.join(current, ".opencode", "skills"));
8805
8927
  if (current !== homeDir) {
8806
- push(path9.join(current, ".claude", "skills"));
8807
- push(path9.join(current, ".agents", "skills"));
8928
+ push(path10.join(current, ".claude", "skills"));
8929
+ push(path10.join(current, ".agents", "skills"));
8808
8930
  }
8809
- const parent = path9.dirname(current);
8931
+ const parent = path10.dirname(current);
8810
8932
  if (parent === current) break;
8811
8933
  current = parent;
8812
8934
  }
8813
- if (homeDir) push(path9.join(homeDir, ".opencode", "skills"));
8935
+ if (homeDir) push(path10.join(homeDir, ".opencode", "skills"));
8814
8936
  const envDir = process.env.OPENCODE_CONFIG_DIR;
8815
8937
  if (envDir) {
8816
- push(path9.join(envDir, "skills"));
8817
- push(path9.join(envDir, "skill"));
8938
+ push(path10.join(envDir, "skills"));
8939
+ push(path10.join(envDir, "skill"));
8818
8940
  }
8819
- const xdg = process.env.XDG_CONFIG_HOME ?? (homeDir ? path9.join(homeDir, ".config") : null);
8941
+ const xdg = process.env.XDG_CONFIG_HOME ?? (homeDir ? path10.join(homeDir, ".config") : null);
8820
8942
  if (xdg) {
8821
- push(path9.join(xdg, "opencode", "skills"));
8822
- push(path9.join(xdg, "opencode", "skill"));
8943
+ push(path10.join(xdg, "opencode", "skills"));
8944
+ push(path10.join(xdg, "opencode", "skill"));
8823
8945
  }
8824
8946
  if (homeDir && !envEnabled("OPENCODE_DISABLE_EXTERNAL_SKILLS")) {
8825
8947
  if (!envEnabled("OPENCODE_DISABLE_CLAUDE_CODE_SKILLS")) {
8826
- push(path9.join(homeDir, ".claude", "skills"));
8948
+ push(path10.join(homeDir, ".claude", "skills"));
8827
8949
  }
8828
- push(path9.join(homeDir, ".agents", "skills"));
8950
+ push(path10.join(homeDir, ".agents", "skills"));
8829
8951
  }
8830
8952
  return roots;
8831
8953
  }
8832
8954
  function collectSkills(root, claimed, found) {
8833
8955
  let entries;
8834
8956
  try {
8835
- entries = fs7.readdirSync(root, { withFileTypes: true });
8957
+ entries = fs8.readdirSync(root, { withFileTypes: true });
8836
8958
  } catch {
8837
8959
  return;
8838
8960
  }
@@ -8872,23 +8994,23 @@ function discoverBundledSkills() {
8872
8994
  }
8873
8995
  function claudeConfigDir(configDir) {
8874
8996
  const explicit = configDir ?? process.env.CLAUDE_CONFIG_DIR;
8875
- if (explicit) return path9.resolve(expandHome(explicit));
8876
- const home = os3.homedir();
8877
- return home ? path9.resolve(home, ".claude") : null;
8997
+ if (explicit) return path10.resolve(expandHome(explicit));
8998
+ const home = os4.homedir();
8999
+ return home ? path10.resolve(home, ".claude") : null;
8878
9000
  }
8879
9001
  function installedPluginSkillRoots(configDir, cwd) {
8880
9002
  if (!configDir) return [];
8881
9003
  let parsed;
8882
9004
  try {
8883
9005
  parsed = JSON.parse(
8884
- fs7.readFileSync(path9.join(configDir, "plugins", "installed_plugins.json"), "utf8")
9006
+ fs8.readFileSync(path10.join(configDir, "plugins", "installed_plugins.json"), "utf8")
8885
9007
  );
8886
9008
  } catch {
8887
9009
  return [];
8888
9010
  }
8889
9011
  const plugins = parsed?.plugins;
8890
9012
  if (!plugins || typeof plugins !== "object") return [];
8891
- const workspace = path9.resolve(cwd);
9013
+ const workspace = path10.resolve(cwd);
8892
9014
  const roots = [];
8893
9015
  for (const installs of Object.values(plugins)) {
8894
9016
  if (!Array.isArray(installs)) continue;
@@ -8896,28 +9018,28 @@ function installedPluginSkillRoots(configDir, cwd) {
8896
9018
  const entry = install;
8897
9019
  if (typeof entry?.installPath !== "string" || !entry.installPath) continue;
8898
9020
  if (entry.scope !== "user" && !coversWorkspace(entry.projectPath, workspace)) continue;
8899
- roots.push(path9.join(entry.installPath, "skills"));
9021
+ roots.push(path10.join(entry.installPath, "skills"));
8900
9022
  }
8901
9023
  }
8902
9024
  return roots;
8903
9025
  }
8904
9026
  function coversWorkspace(projectPath, workspace) {
8905
9027
  if (typeof projectPath !== "string" || !projectPath) return false;
8906
- const root = path9.resolve(projectPath);
9028
+ const root = path10.resolve(projectPath);
8907
9029
  if (root === workspace) return true;
8908
- const rel = path9.relative(root, workspace);
8909
- return rel !== "" && !rel.startsWith("..") && !path9.isAbsolute(rel);
9030
+ const rel = path10.relative(root, workspace);
9031
+ return rel !== "" && !rel.startsWith("..") && !path10.isAbsolute(rel);
8910
9032
  }
8911
9033
  function discoverNativeClaudeSkills(opts) {
8912
9034
  const found = [];
8913
9035
  const scanned = /* @__PURE__ */ new Set();
8914
9036
  const scan = (root, scope) => {
8915
- const abs = path9.resolve(root);
9037
+ const abs = path10.resolve(root);
8916
9038
  if (scanned.has(abs)) return;
8917
9039
  scanned.add(abs);
8918
9040
  let entries;
8919
9041
  try {
8920
- entries = fs7.readdirSync(abs, { withFileTypes: true });
9042
+ entries = fs8.readdirSync(abs, { withFileTypes: true });
8921
9043
  } catch {
8922
9044
  return;
8923
9045
  }
@@ -8929,13 +9051,13 @@ function discoverNativeClaudeSkills(opts) {
8929
9051
  }
8930
9052
  };
8931
9053
  const configDir = claudeConfigDir(opts.configDir);
8932
- if (configDir) scan(path9.join(configDir, "skills"), "user");
8933
- const home = os3.homedir();
8934
- const homeDir = home ? path9.resolve(home) : null;
8935
- let current = path9.resolve(opts.cwd);
9054
+ if (configDir) scan(path10.join(configDir, "skills"), "user");
9055
+ const home = os4.homedir();
9056
+ const homeDir = home ? path10.resolve(home) : null;
9057
+ let current = path10.resolve(opts.cwd);
8936
9058
  while (true) {
8937
- if (current !== homeDir) scan(path9.join(current, ".claude", "skills"), "project");
8938
- const parent = path9.dirname(current);
9059
+ if (current !== homeDir) scan(path10.join(current, ".claude", "skills"), "project");
9060
+ const parent = path10.dirname(current);
8939
9061
  if (parent === current) break;
8940
9062
  current = parent;
8941
9063
  }
@@ -8969,10 +9091,10 @@ function dropNativelyLoadedSkills(skills, native) {
8969
9091
  }
8970
9092
  function readBundledSkillInfos() {
8971
9093
  return discoverBundledSkills().flatMap((skill) => {
8972
- const file = path9.join(skill.dir, "SKILL.md");
9094
+ const file = path10.join(skill.dir, "SKILL.md");
8973
9095
  let text;
8974
9096
  try {
8975
- text = fs7.readFileSync(file, "utf8");
9097
+ text = fs8.readFileSync(file, "utf8");
8976
9098
  } catch {
8977
9099
  return [];
8978
9100
  }
@@ -8996,15 +9118,15 @@ function readBundledSkillInfos() {
8996
9118
  }
8997
9119
  function linkSkill(source, target) {
8998
9120
  try {
8999
- fs7.symlinkSync(source, target, process.platform === "win32" ? "junction" : "dir");
9121
+ fs8.symlinkSync(source, target, process.platform === "win32" ? "junction" : "dir");
9000
9122
  return;
9001
9123
  } catch {
9002
- fs7.cpSync(source, target, { recursive: true, dereference: true });
9124
+ fs8.cpSync(source, target, { recursive: true, dereference: true });
9003
9125
  }
9004
9126
  }
9005
9127
  function fileExists2(p) {
9006
9128
  try {
9007
- return fs7.statSync(p).isFile();
9129
+ return fs8.statSync(p).isFile();
9008
9130
  } catch {
9009
9131
  return false;
9010
9132
  }
@@ -9013,14 +9135,14 @@ function buildSkillPluginDir(skills) {
9013
9135
  if (skills.length === 0) return null;
9014
9136
  const fingerprint = skills.map((s) => `${s.name}\0${s.dir}`).join("\n");
9015
9137
  const hash = crypto3.createHash("sha256").update(fingerprint).digest("hex").slice(0, 12);
9016
- const root = path9.join(pluginTmpDir(), `skills-${hash}`);
9017
- const manifest = path9.join(root, ".claude-plugin", "plugin.json");
9138
+ const root = path10.join(pluginTmpDir(), `skills-${hash}`);
9139
+ const manifest = path10.join(root, ".claude-plugin", "plugin.json");
9018
9140
  if (fileExists2(manifest)) return root;
9019
9141
  try {
9020
- fs7.rmSync(root, { recursive: true, force: true });
9021
- fs7.mkdirSync(path9.join(root, ".claude-plugin"), { recursive: true });
9022
- fs7.mkdirSync(path9.join(root, "skills"), { recursive: true });
9023
- fs7.writeFileSync(
9142
+ fs8.rmSync(root, { recursive: true, force: true });
9143
+ fs8.mkdirSync(path10.join(root, ".claude-plugin"), { recursive: true });
9144
+ fs8.mkdirSync(path10.join(root, "skills"), { recursive: true });
9145
+ fs8.writeFileSync(
9024
9146
  manifest,
9025
9147
  JSON.stringify(
9026
9148
  {
@@ -9033,7 +9155,7 @@ function buildSkillPluginDir(skills) {
9033
9155
  { encoding: "utf8", mode: 384 }
9034
9156
  );
9035
9157
  for (const skill of skills) {
9036
- linkSkill(skill.dir, path9.join(root, "skills", skill.name));
9158
+ linkSkill(skill.dir, path10.join(root, "skills", skill.name));
9037
9159
  }
9038
9160
  } catch (err) {
9039
9161
  log.warn("failed to stage opencode skill plugin dir", {
@@ -9049,7 +9171,7 @@ function registerBundledSkillPath(config) {
9049
9171
  if (!dir) return false;
9050
9172
  config.skills ??= {};
9051
9173
  const paths = config.skills.paths ??= [];
9052
- if (paths.some((p) => path9.resolve(p) === dir)) return false;
9174
+ if (paths.some((p) => path10.resolve(p) === dir)) return false;
9053
9175
  paths.push(dir);
9054
9176
  return true;
9055
9177
  }
@@ -9099,36 +9221,36 @@ import { EventEmitter as EventEmitter4 } from "events";
9099
9221
  import { randomUUID as randomUUID5 } from "crypto";
9100
9222
  import { existsSync as existsSync4, writeFileSync as writeFileSync5 } from "fs";
9101
9223
  import { unlink as unlink2 } from "fs/promises";
9102
- import { join as join10 } from "path";
9224
+ import { join as join11 } from "path";
9103
9225
 
9104
9226
  // src/claude-session-bun.ts
9105
- import * as os4 from "os";
9106
- import * as fs8 from "fs";
9107
- import * as path10 from "path";
9227
+ import * as os5 from "os";
9228
+ import * as fs9 from "fs";
9229
+ import * as path11 from "path";
9108
9230
  import { execFileSync } from "child_process";
9109
9231
  import { randomUUID as randomUUID4 } from "crypto";
9110
9232
  import { StringDecoder } from "string_decoder";
9111
9233
  function resolveClaude(cmd = "claude") {
9112
- if (path10.isAbsolute(cmd) && fs8.existsSync(cmd)) return cmd;
9234
+ if (path11.isAbsolute(cmd) && fs9.existsSync(cmd)) return cmd;
9113
9235
  const viaBun = Bun.which(cmd);
9114
9236
  if (viaBun) return viaBun;
9115
- const isWin = os4.platform() === "win32";
9237
+ const isWin = os5.platform() === "win32";
9116
9238
  try {
9117
9239
  const out = execFileSync(isWin ? "where" : "which", [cmd], {
9118
9240
  encoding: "utf8",
9119
9241
  stdio: ["ignore", "pipe", "ignore"]
9120
9242
  });
9121
- const first = out.split(/\r?\n/).map((l) => l.trim()).filter(Boolean).find((p) => fs8.existsSync(p));
9243
+ const first = out.split(/\r?\n/).map((l) => l.trim()).filter(Boolean).find((p) => fs9.existsSync(p));
9122
9244
  if (first) return first;
9123
9245
  } catch {
9124
9246
  }
9125
9247
  throw new Error(`Could not resolve command on PATH: ${cmd}`);
9126
9248
  }
9127
9249
  function encodeCwd(cwd) {
9128
- const resolved = path10.resolve(cwd);
9250
+ const resolved = path11.resolve(cwd);
9129
9251
  let real = resolved;
9130
9252
  try {
9131
- real = fs8.realpathSync.native(resolved);
9253
+ real = fs9.realpathSync.native(resolved);
9132
9254
  } catch {
9133
9255
  }
9134
9256
  return real.replace(/[^a-zA-Z0-9]/g, "-");
@@ -9136,7 +9258,7 @@ function encodeCwd(cwd) {
9136
9258
  var bunPtySpawner = (argv, opts) => {
9137
9259
  const [command, ...args] = argv;
9138
9260
  const resolved = resolveClaude(command ?? "claude");
9139
- if (os4.platform() === "win32" && isBatchFile(resolved)) {
9261
+ if (os5.platform() === "win32" && isBatchFile(resolved)) {
9140
9262
  throw new Error(
9141
9263
  `The interactive transport cannot run a Windows batch shim (${resolved}). Point \`cliPath\` at claude.exe, or use the default headless transport.`
9142
9264
  );
@@ -9244,10 +9366,10 @@ function classifyScreen(text) {
9244
9366
  return null;
9245
9367
  }
9246
9368
  function interactiveTranscriptPath(opts) {
9247
- return path10.join(
9369
+ return path11.join(
9248
9370
  resolveConfigDir(opts.configDir),
9249
9371
  "projects",
9250
- encodeCwd(path10.resolve(opts.cwd)),
9372
+ encodeCwd(path11.resolve(opts.cwd)),
9251
9373
  `${opts.sessionId}.jsonl`
9252
9374
  );
9253
9375
  }
@@ -9425,7 +9547,7 @@ var TranscriptCursor = class {
9425
9547
  const filePath = this.resolvePath();
9426
9548
  let stat;
9427
9549
  try {
9428
- stat = fs8.statSync(filePath);
9550
+ stat = fs9.statSync(filePath);
9429
9551
  } catch {
9430
9552
  return;
9431
9553
  }
@@ -9441,13 +9563,13 @@ var TranscriptCursor = class {
9441
9563
  if (stat.size <= this.offset) return;
9442
9564
  let fd = null;
9443
9565
  try {
9444
- fd = fs8.openSync(filePath, "r");
9566
+ fd = fs9.openSync(filePath, "r");
9445
9567
  const buffer = Buffer.allocUnsafe(
9446
9568
  Math.min(TRANSCRIPT_CHUNK_BYTES, stat.size - this.offset)
9447
9569
  );
9448
9570
  while (this.offset < stat.size) {
9449
9571
  const want = Math.min(buffer.length, stat.size - this.offset);
9450
- const read = fs8.readSync(fd, buffer, 0, want, this.offset);
9572
+ const read = fs9.readSync(fd, buffer, 0, want, this.offset);
9451
9573
  this.stats.reads++;
9452
9574
  if (read <= 0) break;
9453
9575
  this.offset += read;
@@ -9458,7 +9580,7 @@ var TranscriptCursor = class {
9458
9580
  } finally {
9459
9581
  if (fd !== null) {
9460
9582
  try {
9461
- fs8.closeSync(fd);
9583
+ fs9.closeSync(fd);
9462
9584
  } catch {
9463
9585
  }
9464
9586
  }
@@ -9488,12 +9610,12 @@ function isOwnPromptRecord(rec, normalized) {
9488
9610
  }
9489
9611
  function resolveConfigDir(configDir) {
9490
9612
  const value = configDir ?? process.env.CLAUDE_CONFIG_DIR;
9491
- if (!value) return path10.join(os4.homedir(), ".claude");
9492
- if (value === "~") return os4.homedir();
9613
+ if (!value) return path11.join(os5.homedir(), ".claude");
9614
+ if (value === "~") return os5.homedir();
9493
9615
  if (value.startsWith("~/") || value.startsWith("~\\")) {
9494
- return path10.join(os4.homedir(), value.slice(2));
9616
+ return path11.join(os5.homedir(), value.slice(2));
9495
9617
  }
9496
- return path10.resolve(value);
9618
+ return path11.resolve(value);
9497
9619
  }
9498
9620
  function fatalScreenMessage(kind, configDir, cwd) {
9499
9621
  if (kind === "trust") {
@@ -9557,7 +9679,7 @@ var ClaudeSession = class {
9557
9679
  abandonTurn = false;
9558
9680
  o;
9559
9681
  constructor(opts = {}) {
9560
- this.cwd = path10.resolve(opts.cwd ?? process.cwd());
9682
+ this.cwd = path11.resolve(opts.cwd ?? process.cwd());
9561
9683
  this.configDir = resolveConfigDir(opts.configDir);
9562
9684
  this.explicitConfigDir = opts.configDir ? this.configDir : void 0;
9563
9685
  this.signal = opts.signal;
@@ -9569,7 +9691,7 @@ var ClaudeSession = class {
9569
9691
  this.onExit = opts.onExit;
9570
9692
  this.spawnPty = opts.spawnPty ?? bunPtySpawner;
9571
9693
  this.sessionId = opts.resumeSessionId ?? randomUUID4();
9572
- this.jsonlPath = path10.join(
9694
+ this.jsonlPath = path11.join(
9573
9695
  this.configDir,
9574
9696
  "projects",
9575
9697
  encodeCwd(this.cwd),
@@ -10280,7 +10402,7 @@ ${text2}`
10280
10402
  }
10281
10403
  function stageImage(data, extension) {
10282
10404
  try {
10283
- const file = join10(pluginTmpDir(), `attachment-${randomUUID5()}.${extension}`);
10405
+ const file = join11(pluginTmpDir(), `attachment-${randomUUID5()}.${extension}`);
10284
10406
  writeFileSync5(file, data, { mode: 384, flag: "wx" });
10285
10407
  return file;
10286
10408
  } catch (err) {
@@ -10765,7 +10887,7 @@ function spawnInteractiveProcess(opts) {
10765
10887
  import { randomUUID as randomUUID6 } from "crypto";
10766
10888
  import { constants as fsConstants } from "fs";
10767
10889
  import { chmod as chmod2, copyFile as copyFile2, lstat as lstat2, mkdir as mkdir2 } from "fs/promises";
10768
- import path11 from "path";
10890
+ import path12 from "path";
10769
10891
  var transcriptPath = interactiveTranscriptPath;
10770
10892
  function configDirForAccount(account) {
10771
10893
  const normalized = normalizeAccountName(account || DEFAULT_ACCOUNT);
@@ -10793,7 +10915,7 @@ async function carryTranscriptToAccount(input) {
10793
10915
  const carriedId = renamed ? (input.newSessionId ?? randomUUID6)() : sessionId;
10794
10916
  const target = renamed ? transcriptPath({ configDir: targetConfigDir, cwd, sessionId: carriedId }) : targetOwn;
10795
10917
  try {
10796
- await mkdir2(path11.dirname(target), { recursive: true, mode: 448 });
10918
+ await mkdir2(path12.dirname(target), { recursive: true, mode: 448 });
10797
10919
  await copyFile2(live.file, target, fsConstants.COPYFILE_EXCL);
10798
10920
  await chmod2(target, 384);
10799
10921
  } catch (err) {
@@ -10813,7 +10935,7 @@ function dedupe2(dirs) {
10813
10935
  const seen = /* @__PURE__ */ new Set();
10814
10936
  const out = [];
10815
10937
  for (const dir of dirs) {
10816
- const resolved = path11.resolve(dir);
10938
+ const resolved = path12.resolve(dir);
10817
10939
  if (seen.has(resolved)) continue;
10818
10940
  seen.add(resolved);
10819
10941
  out.push(resolved);
@@ -10822,13 +10944,13 @@ function dedupe2(dirs) {
10822
10944
  }
10823
10945
 
10824
10946
  // src/prompts.ts
10825
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "fs";
10826
- import { homedir as homedir8 } from "os";
10947
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
10948
+ import { homedir as homedir9 } from "os";
10827
10949
  import { randomUUID as randomUUID7 } from "crypto";
10828
- import { basename as basename2, dirname as dirname6, isAbsolute as isAbsolute3, join as join11 } from "path";
10829
- function readPromptFileIfPresent(path14) {
10950
+ import { basename as basename2, dirname as dirname6, isAbsolute as isAbsolute3, join as join12 } from "path";
10951
+ function readPromptFileIfPresent(path15) {
10830
10952
  try {
10831
- const content = readFileSync6(path14, "utf8").trim();
10953
+ const content = readFileSync7(path15, "utf8").trim();
10832
10954
  return content || void 0;
10833
10955
  } catch {
10834
10956
  return void 0;
@@ -10837,7 +10959,7 @@ function readPromptFileIfPresent(path14) {
10837
10959
  function nearestWorkspaceAgentsPrompt(cwd) {
10838
10960
  let dir = cwd;
10839
10961
  while (true) {
10840
- const content = readPromptFileIfPresent(join11(dir, "AGENTS.md"));
10962
+ const content = readPromptFileIfPresent(join12(dir, "AGENTS.md"));
10841
10963
  if (content) return content;
10842
10964
  const parent = dirname6(dir);
10843
10965
  if (parent === dir) return void 0;
@@ -10933,9 +11055,9 @@ function extractSystemMessages(prompt) {
10933
11055
  }
10934
11056
  var CLAUDE_NATIVE_INSTRUCTION_FILES = /* @__PURE__ */ new Set(["CLAUDE.md", "CLAUDE.local.md"]);
10935
11057
  function userAuthoredInstructions(forwarded, opts = {}) {
10936
- const readFile2 = opts.readFile ?? ((path14) => {
11058
+ const readFile2 = opts.readFile ?? ((path15) => {
10937
11059
  try {
10938
- return readFileSync6(path14, "utf8");
11060
+ return readFileSync7(path15, "utf8");
10939
11061
  } catch {
10940
11062
  return null;
10941
11063
  }
@@ -10947,16 +11069,16 @@ function userAuthoredInstructions(forwarded, opts = {}) {
10947
11069
  }
10948
11070
  const seen = /* @__PURE__ */ new Set();
10949
11071
  for (const match of forwarded.matchAll(/^Instructions from: (.+)$/gm)) {
10950
- const path14 = match[1].trim();
10951
- if (seen.has(path14) || !isAbsolute3(path14)) continue;
10952
- if (CLAUDE_NATIVE_INSTRUCTION_FILES.has(basename2(path14))) continue;
10953
- seen.add(path14);
10954
- const file = readFile2(path14);
11072
+ const path15 = match[1].trim();
11073
+ if (seen.has(path15) || !isAbsolute3(path15)) continue;
11074
+ if (CLAUDE_NATIVE_INSTRUCTION_FILES.has(basename2(path15))) continue;
11075
+ seen.add(path15);
11076
+ const file = readFile2(path15);
10955
11077
  if (!file?.trim()) continue;
10956
11078
  const start = match.index + match[0].length + 1;
10957
11079
  const content = forwarded.startsWith(file, start) ? file : forwarded.startsWith(file.trimEnd(), start) ? file.trimEnd() : null;
10958
11080
  if (content === null) continue;
10959
- out.push({ text: `Instructions from: ${path14}
11081
+ out.push({ text: `Instructions from: ${path15}
10960
11082
  ${content}`, content });
10961
11083
  }
10962
11084
  return out;
@@ -10976,8 +11098,8 @@ ${options.compressionSummary.trim()}`
10976
11098
  for (const s of extraSystemContent) {
10977
11099
  if (s.trim()) parts.push(s.trim());
10978
11100
  }
10979
- const configRoot = process.env.XDG_CONFIG_HOME ?? join11(homedir8(), ".config");
10980
- const globalAgents = readPromptFileIfPresent(join11(configRoot, "opencode", "AGENTS.md"));
11101
+ const configRoot = process.env.XDG_CONFIG_HOME ?? join12(homedir9(), ".config");
11102
+ const globalAgents = readPromptFileIfPresent(join12(configRoot, "opencode", "AGENTS.md"));
10981
11103
  const workspaceAgents = nearestWorkspaceAgentsPrompt(cwd);
10982
11104
  const forwarded = extraSystemContent.join("\n\n");
10983
11105
  const pushGlobal = !!globalAgents && !forwarded.includes(globalAgents);
@@ -10993,10 +11115,10 @@ ${options.compressionSummary.trim()}`
10993
11115
  if (includeMultiStepHint) parts.push(MULTI_STEP_TASK_HINT);
10994
11116
  const content = parts.join("\n\n");
10995
11117
  if (!content) return void 0;
10996
- const path14 = join11(pluginTmpDir(), `opencode-cc-sys-${randomUUID7()}.md`);
11118
+ const path15 = join12(pluginTmpDir(), `opencode-cc-sys-${randomUUID7()}.md`);
10997
11119
  try {
10998
- writeFileSync6(path14, content, { encoding: "utf8", mode: 384 });
10999
- return path14;
11120
+ writeFileSync6(path15, content, { encoding: "utf8", mode: 384 });
11121
+ return path15;
11000
11122
  } catch (err) {
11001
11123
  log.warn("failed to write system prompt file", { error: String(err) });
11002
11124
  return void 0;
@@ -11423,7 +11545,9 @@ function effectiveMcpConfig(config, cwd, proxyConfigPath, runtimeStatus, exclude
11423
11545
  let bridgedHash = null;
11424
11546
  let allEnabledServerNames = [];
11425
11547
  if (config.bridgeOpencodeMcp !== false) {
11426
- const bridged = bridgeOpencodeMcp(cwd, runtimeStatus, excludeServers, config.hostApi);
11548
+ const bridged = bridgeOpencodeMcp(cwd, runtimeStatus, excludeServers, config.hostApi, {
11549
+ oauthTokens: config.bridgeMcpOauthTokens === true
11550
+ });
11427
11551
  if (bridged) {
11428
11552
  if (bridged.path) paths.push(bridged.path);
11429
11553
  bridgedHash = bridged.hash;
@@ -14127,7 +14251,7 @@ var ClaudeCodeLanguageModel = class {
14127
14251
  let replayReason = "no-session-for-key";
14128
14252
  if (this.config.resumeAfterRestart !== false && includeHistoryContext && !compactionMode && !forkFromClaudeSessionId && failoverAnswer?.kind !== "switch") {
14129
14253
  const configDir = resolveConfigDir(this.config.configDir);
14130
- const transcriptPath2 = (id) => join12(configDir, "projects", encodeCwd(cwd), `${id}.jsonl`);
14254
+ const transcriptPath2 = (id) => join13(configDir, "projects", encodeCwd(cwd), `${id}.jsonl`);
14131
14255
  const resumePoint = findResumePoint({
14132
14256
  sessionKey: sk,
14133
14257
  prompt: options.prompt,
@@ -15383,8 +15507,8 @@ var ClaudeCodeLanguageModel = class {
15383
15507
  };
15384
15508
 
15385
15509
  // src/account-tier.ts
15386
- import { readFileSync as readFileSync7 } from "fs";
15387
- import path12 from "path";
15510
+ import { readFileSync as readFileSync8 } from "fs";
15511
+ import path13 from "path";
15388
15512
  var ORGANIZATION_TYPE_LABELS = {
15389
15513
  claude_max: "Max",
15390
15514
  claude_pro: "Pro",
@@ -15413,7 +15537,7 @@ function readAccountTier(account) {
15413
15537
  return label;
15414
15538
  }
15415
15539
  function readTierFromConfigDir(configDir, allowLegacyFallback) {
15416
- const candidates = [path12.join(configDir, ".claude.json")];
15540
+ const candidates = [path13.join(configDir, ".claude.json")];
15417
15541
  if (allowLegacyFallback) candidates.push(expandHome("~/.claude.json"));
15418
15542
  for (const candidate of candidates) {
15419
15543
  const oauth = readOauthAccount(candidate);
@@ -15426,7 +15550,7 @@ function readTierFromConfigDir(configDir, allowLegacyFallback) {
15426
15550
  function readOauthAccount(file) {
15427
15551
  let raw;
15428
15552
  try {
15429
- raw = readFileSync7(file, "utf8");
15553
+ raw = readFileSync8(file, "utf8");
15430
15554
  } catch {
15431
15555
  return void 0;
15432
15556
  }
@@ -15455,13 +15579,13 @@ function tierLabel(oauthAccount) {
15455
15579
  import {
15456
15580
  existsSync as existsSync5,
15457
15581
  mkdirSync as mkdirSync5,
15458
- readFileSync as readFileSync8,
15582
+ readFileSync as readFileSync9,
15459
15583
  realpathSync as realpathSync4,
15460
15584
  rmSync as rmSync3,
15461
15585
  writeFileSync as writeFileSync7
15462
15586
  } from "fs";
15463
- import { homedir as homedir9 } from "os";
15464
- import { dirname as dirname7, join as join13, resolve as resolve5 } from "path";
15587
+ import { homedir as homedir10 } from "os";
15588
+ import { dirname as dirname7, join as join14, resolve as resolve5 } from "path";
15465
15589
  import { fileURLToPath as fileURLToPath4 } from "url";
15466
15590
  var STALE_PACKAGE_NAME = "opencode-claude-code-plugin";
15467
15591
  var SUSPECT_DESCRIPTION_TOKEN = "Claude Code";
@@ -15469,20 +15593,20 @@ var alreadyRan = false;
15469
15593
  function candidateCacheRoots() {
15470
15594
  const xdg = process.env.XDG_CACHE_HOME;
15471
15595
  return [
15472
- xdg ? join13(xdg, "opencode") : null,
15473
- join13(homedir9(), ".cache", "opencode"),
15474
- join13(homedir9(), "Library", "Caches", "opencode")
15596
+ xdg ? join14(xdg, "opencode") : null,
15597
+ join14(homedir10(), ".cache", "opencode"),
15598
+ join14(homedir10(), "Library", "Caches", "opencode")
15475
15599
  ].filter((p) => Boolean(p));
15476
15600
  }
15477
15601
  function userOpencodeJsonPath() {
15478
- const xdgConfig = process.env.XDG_CONFIG_HOME ?? join13(homedir9(), ".config");
15479
- return join13(xdgConfig, "opencode", "opencode.json");
15602
+ const xdgConfig = process.env.XDG_CONFIG_HOME ?? join14(homedir10(), ".config");
15603
+ return join14(xdgConfig, "opencode", "opencode.json");
15480
15604
  }
15481
15605
  function userIntendsToUseUnscoped() {
15482
15606
  const cfg = userOpencodeJsonPath();
15483
15607
  if (!existsSync5(cfg)) return false;
15484
15608
  try {
15485
- const json = JSON.parse(readFileSync8(cfg, "utf8"));
15609
+ const json = JSON.parse(readFileSync9(cfg, "utf8"));
15486
15610
  const plugins = json.plugin;
15487
15611
  if (!Array.isArray(plugins)) return false;
15488
15612
  return plugins.some(
@@ -15503,19 +15627,19 @@ function ourLoadedDir() {
15503
15627
  function ourVersion(dir) {
15504
15628
  if (!dir) return null;
15505
15629
  try {
15506
- const pkg = JSON.parse(readFileSync8(join13(dir, "package.json"), "utf8"));
15630
+ const pkg = JSON.parse(readFileSync9(join14(dir, "package.json"), "utf8"));
15507
15631
  return typeof pkg.version === "string" && pkg.version ? pkg.version : null;
15508
15632
  } catch {
15509
15633
  return null;
15510
15634
  }
15511
15635
  }
15512
15636
  function markerPath() {
15513
- const stateRoot = process.env.XDG_STATE_HOME ?? join13(homedir9(), ".local", "state");
15514
- return join13(stateRoot, "opencode-claude-code-plugin", "cleanup-stale.json");
15637
+ const stateRoot = process.env.XDG_STATE_HOME ?? join14(homedir10(), ".local", "state");
15638
+ return join14(stateRoot, "opencode-claude-code-plugin", "cleanup-stale.json");
15515
15639
  }
15516
15640
  function markerVersion() {
15517
15641
  try {
15518
- const marker = JSON.parse(readFileSync8(markerPath(), "utf8"));
15642
+ const marker = JSON.parse(readFileSync9(markerPath(), "utf8"));
15519
15643
  return typeof marker.version === "string" ? marker.version : null;
15520
15644
  } catch {
15521
15645
  return null;
@@ -15560,7 +15684,7 @@ function cleanupStaleUnscopedInstall() {
15560
15684
  }
15561
15685
  function cleanupOne(cacheRoot, ourDir) {
15562
15686
  if (!existsSync5(cacheRoot)) return;
15563
- const stalePath = join13(cacheRoot, "node_modules", STALE_PACKAGE_NAME);
15687
+ const stalePath = join14(cacheRoot, "node_modules", STALE_PACKAGE_NAME);
15564
15688
  if (!existsSync5(stalePath)) return;
15565
15689
  let realStalePath = stalePath;
15566
15690
  try {
@@ -15568,11 +15692,11 @@ function cleanupOne(cacheRoot, ourDir) {
15568
15692
  } catch {
15569
15693
  }
15570
15694
  if (ourDir && realStalePath === ourDir) return;
15571
- const pkgJsonPath = join13(stalePath, "package.json");
15695
+ const pkgJsonPath = join14(stalePath, "package.json");
15572
15696
  if (!existsSync5(pkgJsonPath)) return;
15573
15697
  let pkg = {};
15574
15698
  try {
15575
- pkg = JSON.parse(readFileSync8(pkgJsonPath, "utf8"));
15699
+ pkg = JSON.parse(readFileSync9(pkgJsonPath, "utf8"));
15576
15700
  } catch {
15577
15701
  return;
15578
15702
  }
@@ -15588,10 +15712,10 @@ function cleanupOne(cacheRoot, ourDir) {
15588
15712
  });
15589
15713
  return;
15590
15714
  }
15591
- const cachePkgJson = join13(cacheRoot, "package.json");
15715
+ const cachePkgJson = join14(cacheRoot, "package.json");
15592
15716
  if (!existsSync5(cachePkgJson)) return;
15593
15717
  try {
15594
- const cfg = JSON.parse(readFileSync8(cachePkgJson, "utf8"));
15718
+ const cfg = JSON.parse(readFileSync9(cachePkgJson, "utf8"));
15595
15719
  if (cfg?.dependencies?.[STALE_PACKAGE_NAME]) {
15596
15720
  delete cfg.dependencies[STALE_PACKAGE_NAME];
15597
15721
  writeFileSync7(cachePkgJson, JSON.stringify(cfg, null, 2) + "\n");
@@ -15671,7 +15795,7 @@ function registerOpencodeLogSink(client) {
15671
15795
  }
15672
15796
 
15673
15797
  // src/v2-client.ts
15674
- import path13 from "path";
15798
+ import path14 from "path";
15675
15799
  function formatAgentTypeList(agents) {
15676
15800
  const lines = agents.flatMap((agent) => {
15677
15801
  if (agent.hidden === true || agent.mode === "primary") return [];
@@ -15700,7 +15824,7 @@ function toV1Session(info) {
15700
15824
  if (!info || typeof info !== "object") return void 0;
15701
15825
  const session = info;
15702
15826
  const root = session.location?.directory;
15703
- const directory = typeof root === "string" ? typeof session.subpath === "string" && session.subpath.length > 0 ? path13.resolve(root, session.subpath) : root : void 0;
15827
+ const directory = typeof root === "string" ? typeof session.subpath === "string" && session.subpath.length > 0 ? path14.resolve(root, session.subpath) : root : void 0;
15704
15828
  return { ...info, directory, parentID: session.parentID };
15705
15829
  }
15706
15830
  function toV1Messages(messages) {
@@ -16235,6 +16359,9 @@ function createClaudeCode(settings = {}) {
16235
16359
  mcpConfig: settings.mcpConfig,
16236
16360
  strictMcpConfig: settings.strictMcpConfig,
16237
16361
  bridgeOpencodeMcp: settings.bridgeOpencodeMcp ?? true,
16362
+ // No `?? true` twin: reading opencode's credential store is opt-in, so
16363
+ // the absent value has to stay falsy.
16364
+ bridgeMcpOauthTokens: settings.bridgeMcpOauthTokens,
16238
16365
  controlRequestBehavior: preset?.controlRequestBehavior ?? settings.controlRequestBehavior ?? "allow",
16239
16366
  controlRequestToolBehaviors: preset ? void 0 : settings.controlRequestToolBehaviors,
16240
16367
  controlRequestDenyMessage: settings.controlRequestDenyMessage,