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

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;
@@ -2895,13 +2960,13 @@ function titleizeAccount(account) {
2895
2960
  // src/cleanup-stale.ts
2896
2961
  import {
2897
2962
  existsSync as existsSync2,
2898
- readFileSync as readFileSync2,
2963
+ readFileSync as readFileSync3,
2899
2964
  realpathSync,
2900
2965
  rmSync,
2901
- writeFileSync as writeFileSync3
2966
+ writeFileSync as writeFileSync4
2902
2967
  } from "fs";
2903
- import { homedir as homedir2 } from "os";
2904
- import { join as join3, resolve as resolve2 } from "path";
2968
+ import { homedir as homedir3 } from "os";
2969
+ import { join as join4, resolve as resolve2 } from "path";
2905
2970
  import { fileURLToPath } from "url";
2906
2971
  var STALE_PACKAGE_NAME = "opencode-claude-code-plugin";
2907
2972
  var SUSPECT_DESCRIPTION_TOKEN = "Claude Code";
@@ -2909,20 +2974,20 @@ var alreadyRan = false;
2909
2974
  function candidateCacheRoots() {
2910
2975
  const xdg = process.env.XDG_CACHE_HOME;
2911
2976
  return [
2912
- xdg ? join3(xdg, "opencode") : null,
2913
- join3(homedir2(), ".cache", "opencode"),
2914
- join3(homedir2(), "Library", "Caches", "opencode")
2977
+ xdg ? join4(xdg, "opencode") : null,
2978
+ join4(homedir3(), ".cache", "opencode"),
2979
+ join4(homedir3(), "Library", "Caches", "opencode")
2915
2980
  ].filter((p) => Boolean(p));
2916
2981
  }
2917
2982
  function userOpencodeJsonPath() {
2918
- const xdgConfig = process.env.XDG_CONFIG_HOME ?? join3(homedir2(), ".config");
2919
- return join3(xdgConfig, "opencode", "opencode.json");
2983
+ const xdgConfig = process.env.XDG_CONFIG_HOME ?? join4(homedir3(), ".config");
2984
+ return join4(xdgConfig, "opencode", "opencode.json");
2920
2985
  }
2921
2986
  function userIntendsToUseUnscoped() {
2922
2987
  const cfg = userOpencodeJsonPath();
2923
2988
  if (!existsSync2(cfg)) return false;
2924
2989
  try {
2925
- const json = JSON.parse(readFileSync2(cfg, "utf8"));
2990
+ const json = JSON.parse(readFileSync3(cfg, "utf8"));
2926
2991
  const plugins = json.plugin;
2927
2992
  if (!Array.isArray(plugins)) return false;
2928
2993
  return plugins.some(
@@ -2959,7 +3024,7 @@ function cleanupStaleUnscopedInstall() {
2959
3024
  }
2960
3025
  function cleanupOne(cacheRoot, ourDir) {
2961
3026
  if (!existsSync2(cacheRoot)) return;
2962
- const stalePath = join3(cacheRoot, "node_modules", STALE_PACKAGE_NAME);
3027
+ const stalePath = join4(cacheRoot, "node_modules", STALE_PACKAGE_NAME);
2963
3028
  if (!existsSync2(stalePath)) return;
2964
3029
  let realStalePath = stalePath;
2965
3030
  try {
@@ -2967,11 +3032,11 @@ function cleanupOne(cacheRoot, ourDir) {
2967
3032
  } catch {
2968
3033
  }
2969
3034
  if (ourDir && realStalePath === ourDir) return;
2970
- const pkgJsonPath = join3(stalePath, "package.json");
3035
+ const pkgJsonPath = join4(stalePath, "package.json");
2971
3036
  if (!existsSync2(pkgJsonPath)) return;
2972
3037
  let pkg = {};
2973
3038
  try {
2974
- pkg = JSON.parse(readFileSync2(pkgJsonPath, "utf8"));
3039
+ pkg = JSON.parse(readFileSync3(pkgJsonPath, "utf8"));
2975
3040
  } catch {
2976
3041
  return;
2977
3042
  }
@@ -2987,13 +3052,13 @@ function cleanupOne(cacheRoot, ourDir) {
2987
3052
  });
2988
3053
  return;
2989
3054
  }
2990
- const cachePkgJson = join3(cacheRoot, "package.json");
3055
+ const cachePkgJson = join4(cacheRoot, "package.json");
2991
3056
  if (!existsSync2(cachePkgJson)) return;
2992
3057
  try {
2993
- const cfg = JSON.parse(readFileSync2(cachePkgJson, "utf8"));
3058
+ const cfg = JSON.parse(readFileSync3(cachePkgJson, "utf8"));
2994
3059
  if (cfg?.dependencies?.[STALE_PACKAGE_NAME]) {
2995
3060
  delete cfg.dependencies[STALE_PACKAGE_NAME];
2996
- writeFileSync3(cachePkgJson, JSON.stringify(cfg, null, 2) + "\n");
3061
+ writeFileSync4(cachePkgJson, JSON.stringify(cfg, null, 2) + "\n");
2997
3062
  log.info("cleanup-stale: pruned dep from cache package.json");
2998
3063
  }
2999
3064
  } catch (err) {
@@ -3191,12 +3256,20 @@ var server = async (input) => {
3191
3256
  config: async (config) => {
3192
3257
  config.provider ??= {};
3193
3258
  const expanded = await expandAccountProviders(config);
3194
- if (expanded) return;
3259
+ if (expanded) {
3260
+ const registered = Object.entries(config.provider).filter(([id]) => id === PROVIDER_ID2 || id.startsWith(`${PROVIDER_ID2}-`)).map(([id, p]) => ({ id, name: p?.name ?? id }));
3261
+ log.notice("registered claude-code providers", { providers: registered });
3262
+ return;
3263
+ }
3195
3264
  const existing = config.provider[PROVIDER_ID2];
3196
3265
  config.provider[PROVIDER_ID2] = {
3197
3266
  ...existing,
3198
3267
  ...await providerConfig(existing)
3199
3268
  };
3269
+ log.notice("registered claude-code provider", {
3270
+ id: PROVIDER_ID2,
3271
+ name: config.provider[PROVIDER_ID2]?.name ?? PROVIDER_ID2
3272
+ });
3200
3273
  },
3201
3274
  // No `event` hook: MCP config drift is detected at turn start by the
3202
3275
  // hot-reload check in `claude-code-language-model.ts`, which respawns