@khalilgharbaoui/opencode-claude-code-plugin 0.2.1 → 0.2.3

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
@@ -15,6 +15,9 @@ var log = {
15
15
  info(msg, data) {
16
16
  if (DEBUG) console.error(fmt("INFO", msg, data));
17
17
  },
18
+ notice(msg, data) {
19
+ console.error(fmt("NOTICE", msg, data));
20
+ },
18
21
  warn(msg, data) {
19
22
  if (DEBUG) console.error(fmt("WARN", msg, data));
20
23
  },
@@ -711,6 +714,7 @@ async function getRuntimeMcpStatus() {
711
714
  import { spawn } from "child_process";
712
715
  import { createInterface } from "readline";
713
716
  import { EventEmitter } from "events";
717
+ import { unlink } from "fs/promises";
714
718
  var activeProcesses = /* @__PURE__ */ new Map();
715
719
  var claudeSessions = /* @__PURE__ */ new Map();
716
720
  var MAX_ACTIVE_PROCESSES = 16;
@@ -751,7 +755,7 @@ function setClaudeSessionId(key, sessionId) {
751
755
  function deleteClaudeSessionId(key) {
752
756
  claudeSessions.delete(key);
753
757
  }
754
- function spawnClaudeProcess(cliPath, cliArgs, cwd, sessionKey2, proxyServer, mcpHash) {
758
+ function spawnClaudeProcess(cliPath, cliArgs, cwd, sessionKey2, proxyServer, mcpHash, systemPromptFile) {
755
759
  evictIfNeeded();
756
760
  log.info("spawning new claude process", { cliPath, cliArgs, cwd, sessionKey: sessionKey2 });
757
761
  const proc = spawn(cliPath, cliArgs, {
@@ -772,7 +776,8 @@ function spawnClaudeProcess(cliPath, cliArgs, cwd, sessionKey2, proxyServer, mcp
772
776
  proc,
773
777
  lineEmitter,
774
778
  proxyServer: proxyServer ?? null,
775
- mcpHash
779
+ mcpHash,
780
+ systemPromptFile
776
781
  };
777
782
  activeProcesses.set(sessionKey2, ap);
778
783
  proc.on("error", (err) => {
@@ -781,6 +786,10 @@ function spawnClaudeProcess(cliPath, cliArgs, cwd, sessionKey2, proxyServer, mcp
781
786
  proc.on("exit", (code, signal) => {
782
787
  log.info("claude process exited", { code, signal, sessionKey: sessionKey2 });
783
788
  void proxyServer?.close();
789
+ if (systemPromptFile) {
790
+ void unlink(systemPromptFile).catch(() => {
791
+ });
792
+ }
784
793
  activeProcesses.delete(sessionKey2);
785
794
  if (code !== 0 && code !== null) {
786
795
  log.info("process exited with error, clearing session", {
@@ -812,7 +821,8 @@ function buildCliArgs(opts) {
812
821
  permissionMode,
813
822
  mcpConfig,
814
823
  strictMcpConfig,
815
- disallowedTools
824
+ disallowedTools,
825
+ appendSystemPromptFile
816
826
  } = opts;
817
827
  const args = [
818
828
  "--output-format",
@@ -846,6 +856,9 @@ function buildCliArgs(opts) {
846
856
  if (disallowedTools && disallowedTools.length > 0) {
847
857
  args.push("--disallowedTools", ...disallowedTools);
848
858
  }
859
+ if (appendSystemPromptFile) {
860
+ args.push("--append-system-prompt-file", appendSystemPromptFile);
861
+ }
849
862
  if (skipPermissions) {
850
863
  args.push("--dangerously-skip-permissions");
851
864
  }
@@ -1243,6 +1256,47 @@ function resolvePendingProxyCall(sessionKey2, result) {
1243
1256
  }
1244
1257
 
1245
1258
  // src/claude-code-language-model.ts
1259
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
1260
+ import { unlink as unlink2 } from "fs/promises";
1261
+ import { homedir as homedir2, tmpdir as tmpdir3 } from "os";
1262
+ import { randomUUID as randomUUID2 } from "crypto";
1263
+ import { dirname as dirname2, join as join3 } from "path";
1264
+ function readPromptFileIfPresent(path4) {
1265
+ try {
1266
+ const content = readFileSync2(path4, "utf8").trim();
1267
+ return content || void 0;
1268
+ } catch {
1269
+ return void 0;
1270
+ }
1271
+ }
1272
+ function nearestWorkspaceAgentsPrompt(cwd) {
1273
+ let dir = cwd;
1274
+ while (true) {
1275
+ const content = readPromptFileIfPresent(join3(dir, "AGENTS.md"));
1276
+ if (content) return content;
1277
+ const parent = dirname2(dir);
1278
+ if (parent === dir) return void 0;
1279
+ dir = parent;
1280
+ }
1281
+ }
1282
+ function buildAppendedSystemPrompt(cwd) {
1283
+ const parts = [];
1284
+ const configRoot = process.env.XDG_CONFIG_HOME ?? join3(homedir2(), ".config");
1285
+ const globalAgents = readPromptFileIfPresent(join3(configRoot, "opencode", "AGENTS.md"));
1286
+ const workspaceAgents = nearestWorkspaceAgentsPrompt(cwd);
1287
+ if (globalAgents) parts.push(globalAgents);
1288
+ if (workspaceAgents && workspaceAgents !== globalAgents) parts.push(workspaceAgents);
1289
+ const content = parts.join("\n\n");
1290
+ if (!content) return void 0;
1291
+ const path4 = join3(tmpdir3(), `opencode-cc-sys-${randomUUID2()}.md`);
1292
+ try {
1293
+ writeFileSync3(path4, content, "utf8");
1294
+ return path4;
1295
+ } catch (err) {
1296
+ log.warn("failed to write system prompt file", { error: String(err) });
1297
+ return void 0;
1298
+ }
1299
+ }
1246
1300
  var ClaudeCodeLanguageModel = class {
1247
1301
  specificationVersion = "v3";
1248
1302
  modelId;
@@ -1646,6 +1700,7 @@ var ClaudeCodeLanguageModel = class {
1646
1700
  reasoningEffort
1647
1701
  );
1648
1702
  const runtimeStatus = await getRuntimeMcpStatus();
1703
+ const systemPromptFile = buildAppendedSystemPrompt(cwd);
1649
1704
  const cliArgs = buildCliArgs({
1650
1705
  sessionKey: sk,
1651
1706
  skipPermissions: this.config.skipPermissions !== false,
@@ -1654,7 +1709,8 @@ var ClaudeCodeLanguageModel = class {
1654
1709
  permissionMode: this.config.permissionMode,
1655
1710
  mcpConfig: this.effectiveMcpConfig(cwd, void 0, runtimeStatus).paths,
1656
1711
  strictMcpConfig: this.config.strictMcpConfig,
1657
- disallowedTools: this.config.webSearch === "disabled" ? ["WebSearch"] : void 0
1712
+ disallowedTools: this.config.webSearch === "disabled" ? ["WebSearch"] : void 0,
1713
+ appendSystemPromptFile: systemPromptFile
1658
1714
  });
1659
1715
  log.info("doGenerate starting", {
1660
1716
  cwd,
@@ -1670,6 +1726,12 @@ var ClaudeCodeLanguageModel = class {
1670
1726
  env: { ...process.env, TERM: "xterm-256color" },
1671
1727
  shell: process.platform === "win32"
1672
1728
  });
1729
+ if (systemPromptFile) {
1730
+ proc.on("exit", () => {
1731
+ void unlink2(systemPromptFile).catch(() => {
1732
+ });
1733
+ });
1734
+ }
1673
1735
  const rl = createInterface2({ input: proc.stdout });
1674
1736
  let responseText = "";
1675
1737
  let thinkingText = "";
@@ -1968,6 +2030,7 @@ ${plan}
1968
2030
  proxyServer?.configPath(),
1969
2031
  runtimeStatus
1970
2032
  );
2033
+ const systemPromptFile = activeProcess ? void 0 : buildAppendedSystemPrompt(cwd);
1971
2034
  const cliArgs = buildCliArgs({
1972
2035
  sessionKey: sk,
1973
2036
  skipPermissions,
@@ -1975,7 +2038,8 @@ ${plan}
1975
2038
  permissionMode: self.config.permissionMode,
1976
2039
  mcpConfig: mcp.paths,
1977
2040
  strictMcpConfig: self.config.strictMcpConfig,
1978
- disallowedTools: allDisallowed.length > 0 ? allDisallowed : void 0
2041
+ disallowedTools: allDisallowed.length > 0 ? allDisallowed : void 0,
2042
+ appendSystemPromptFile: systemPromptFile
1979
2043
  });
1980
2044
  if (activeProcess) {
1981
2045
  proc = activeProcess.proc;
@@ -1988,7 +2052,8 @@ ${plan}
1988
2052
  cwd,
1989
2053
  sk,
1990
2054
  proxyServer,
1991
- mcp.bridgedHash
2055
+ mcp.bridgedHash,
2056
+ systemPromptFile
1992
2057
  );
1993
2058
  proc = ap.proc;
1994
2059
  lineEmitter = ap.lineEmitter;
@@ -2674,7 +2739,6 @@ function toConfigModel(model) {
2674
2739
  attachment: model.capabilities.attachment,
2675
2740
  tool_call: model.capabilities.toolcall,
2676
2741
  modalities: { input: inputMods, output: outputMods },
2677
- interleaved: model.capabilities.interleaved,
2678
2742
  cost: {
2679
2743
  input: model.cost.input,
2680
2744
  output: model.cost.output,
@@ -2895,13 +2959,13 @@ function titleizeAccount(account) {
2895
2959
  // src/cleanup-stale.ts
2896
2960
  import {
2897
2961
  existsSync as existsSync2,
2898
- readFileSync as readFileSync2,
2962
+ readFileSync as readFileSync3,
2899
2963
  realpathSync,
2900
2964
  rmSync,
2901
- writeFileSync as writeFileSync3
2965
+ writeFileSync as writeFileSync4
2902
2966
  } from "fs";
2903
- import { homedir as homedir2 } from "os";
2904
- import { join as join3, resolve as resolve2 } from "path";
2967
+ import { homedir as homedir3 } from "os";
2968
+ import { join as join4, resolve as resolve2 } from "path";
2905
2969
  import { fileURLToPath } from "url";
2906
2970
  var STALE_PACKAGE_NAME = "opencode-claude-code-plugin";
2907
2971
  var SUSPECT_DESCRIPTION_TOKEN = "Claude Code";
@@ -2909,20 +2973,20 @@ var alreadyRan = false;
2909
2973
  function candidateCacheRoots() {
2910
2974
  const xdg = process.env.XDG_CACHE_HOME;
2911
2975
  return [
2912
- xdg ? join3(xdg, "opencode") : null,
2913
- join3(homedir2(), ".cache", "opencode"),
2914
- join3(homedir2(), "Library", "Caches", "opencode")
2976
+ xdg ? join4(xdg, "opencode") : null,
2977
+ join4(homedir3(), ".cache", "opencode"),
2978
+ join4(homedir3(), "Library", "Caches", "opencode")
2915
2979
  ].filter((p) => Boolean(p));
2916
2980
  }
2917
2981
  function userOpencodeJsonPath() {
2918
- const xdgConfig = process.env.XDG_CONFIG_HOME ?? join3(homedir2(), ".config");
2919
- return join3(xdgConfig, "opencode", "opencode.json");
2982
+ const xdgConfig = process.env.XDG_CONFIG_HOME ?? join4(homedir3(), ".config");
2983
+ return join4(xdgConfig, "opencode", "opencode.json");
2920
2984
  }
2921
2985
  function userIntendsToUseUnscoped() {
2922
2986
  const cfg = userOpencodeJsonPath();
2923
2987
  if (!existsSync2(cfg)) return false;
2924
2988
  try {
2925
- const json = JSON.parse(readFileSync2(cfg, "utf8"));
2989
+ const json = JSON.parse(readFileSync3(cfg, "utf8"));
2926
2990
  const plugins = json.plugin;
2927
2991
  if (!Array.isArray(plugins)) return false;
2928
2992
  return plugins.some(
@@ -2959,7 +3023,7 @@ function cleanupStaleUnscopedInstall() {
2959
3023
  }
2960
3024
  function cleanupOne(cacheRoot, ourDir) {
2961
3025
  if (!existsSync2(cacheRoot)) return;
2962
- const stalePath = join3(cacheRoot, "node_modules", STALE_PACKAGE_NAME);
3026
+ const stalePath = join4(cacheRoot, "node_modules", STALE_PACKAGE_NAME);
2963
3027
  if (!existsSync2(stalePath)) return;
2964
3028
  let realStalePath = stalePath;
2965
3029
  try {
@@ -2967,11 +3031,11 @@ function cleanupOne(cacheRoot, ourDir) {
2967
3031
  } catch {
2968
3032
  }
2969
3033
  if (ourDir && realStalePath === ourDir) return;
2970
- const pkgJsonPath = join3(stalePath, "package.json");
3034
+ const pkgJsonPath = join4(stalePath, "package.json");
2971
3035
  if (!existsSync2(pkgJsonPath)) return;
2972
3036
  let pkg = {};
2973
3037
  try {
2974
- pkg = JSON.parse(readFileSync2(pkgJsonPath, "utf8"));
3038
+ pkg = JSON.parse(readFileSync3(pkgJsonPath, "utf8"));
2975
3039
  } catch {
2976
3040
  return;
2977
3041
  }
@@ -2987,13 +3051,13 @@ function cleanupOne(cacheRoot, ourDir) {
2987
3051
  });
2988
3052
  return;
2989
3053
  }
2990
- const cachePkgJson = join3(cacheRoot, "package.json");
3054
+ const cachePkgJson = join4(cacheRoot, "package.json");
2991
3055
  if (!existsSync2(cachePkgJson)) return;
2992
3056
  try {
2993
- const cfg = JSON.parse(readFileSync2(cachePkgJson, "utf8"));
3057
+ const cfg = JSON.parse(readFileSync3(cachePkgJson, "utf8"));
2994
3058
  if (cfg?.dependencies?.[STALE_PACKAGE_NAME]) {
2995
3059
  delete cfg.dependencies[STALE_PACKAGE_NAME];
2996
- writeFileSync3(cachePkgJson, JSON.stringify(cfg, null, 2) + "\n");
3060
+ writeFileSync4(cachePkgJson, JSON.stringify(cfg, null, 2) + "\n");
2997
3061
  log.info("cleanup-stale: pruned dep from cache package.json");
2998
3062
  }
2999
3063
  } catch (err) {
@@ -3191,12 +3255,20 @@ var server = async (input) => {
3191
3255
  config: async (config) => {
3192
3256
  config.provider ??= {};
3193
3257
  const expanded = await expandAccountProviders(config);
3194
- if (expanded) return;
3258
+ if (expanded) {
3259
+ const registered = Object.entries(config.provider).filter(([id]) => id === PROVIDER_ID2 || id.startsWith(`${PROVIDER_ID2}-`)).map(([id, p]) => ({ id, name: p?.name ?? id }));
3260
+ log.notice("registered claude-code providers", { providers: registered });
3261
+ return;
3262
+ }
3195
3263
  const existing = config.provider[PROVIDER_ID2];
3196
3264
  config.provider[PROVIDER_ID2] = {
3197
3265
  ...existing,
3198
3266
  ...await providerConfig(existing)
3199
3267
  };
3268
+ log.notice("registered claude-code provider", {
3269
+ id: PROVIDER_ID2,
3270
+ name: config.provider[PROVIDER_ID2]?.name ?? PROVIDER_ID2
3271
+ });
3200
3272
  },
3201
3273
  // No `event` hook: MCP config drift is detected at turn start by the
3202
3274
  // hot-reload check in `claude-code-language-model.ts`, which respawns