@khalilgharbaoui/opencode-claude-code-plugin 0.53.5 → 0.54.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/claude-code-language-model.ts
2
- import { join as join15 } from "path";
2
+ import { join as join16 } from "path";
3
3
 
4
4
  // src/ids.ts
5
5
  import { randomInt } from "crypto";
@@ -12166,6 +12166,80 @@ function spawnInteractiveProcess(opts) {
12166
12166
  return ap;
12167
12167
  }
12168
12168
 
12169
+ // src/spawn-record-store.ts
12170
+ import { mkdirSync as mkdirSync6, readFileSync as readFileSync8, renameSync as renameSync4, statSync as statSync7, writeFileSync as writeFileSync7 } from "fs";
12171
+ import { homedir as homedir10 } from "os";
12172
+ import { join as join14 } from "path";
12173
+ var MAX_SPAWN_RECORDS = 256;
12174
+ var SPAWN_RECORD_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
12175
+ var pathOverride3 = null;
12176
+ var lastWritten = /* @__PURE__ */ new Map();
12177
+ function spawnRecordStorePath() {
12178
+ if (pathOverride3) return pathOverride3;
12179
+ const stateRoot = process.env.XDG_STATE_HOME || join14(homedir10(), ".local", "state");
12180
+ return join14(stateRoot, "opencode-claude-code-plugin", "session-spawns.json");
12181
+ }
12182
+ function isRecord5(value) {
12183
+ const record = value;
12184
+ if (!record || typeof record !== "object" || Array.isArray(record)) return false;
12185
+ if (typeof record.at !== "number" || !Number.isFinite(record.at)) return false;
12186
+ const ok = (field) => field === void 0 || typeof field === "string";
12187
+ if (!ok(record.model) || !ok(record.effort) || !ok(record.account)) return false;
12188
+ return !!record.model;
12189
+ }
12190
+ function readEntries(file) {
12191
+ const out = /* @__PURE__ */ new Map();
12192
+ try {
12193
+ const parsed = JSON.parse(readFileSync8(file, "utf8"));
12194
+ const entries = parsed && typeof parsed === "object" ? parsed.sessions : null;
12195
+ if (!entries || typeof entries !== "object") return out;
12196
+ const cutoff = Date.now() - SPAWN_RECORD_MAX_AGE_MS;
12197
+ const valid = Object.entries(entries).filter((entry) => isRecord5(entry[1])).filter(([, record]) => record.at >= cutoff).sort((a, b) => a[1].at - b[1].at);
12198
+ for (const [key, record] of valid) {
12199
+ out.set(key, {
12200
+ model: record.model,
12201
+ ...record.effort ? { effort: record.effort } : {},
12202
+ ...record.account ? { account: record.account } : {},
12203
+ at: record.at
12204
+ });
12205
+ }
12206
+ } catch {
12207
+ }
12208
+ return out;
12209
+ }
12210
+ function recordSessionSpawn(sessionID, spawn2, now = Date.now()) {
12211
+ if (!sessionID || sessionID === "default" || !spawn2.model) return false;
12212
+ const record = {
12213
+ model: spawn2.model,
12214
+ ...spawn2.effort ? { effort: spawn2.effort } : {},
12215
+ ...spawn2.account ? { account: spawn2.account } : {},
12216
+ at: now
12217
+ };
12218
+ const fingerprint = `${record.model}
12219
+ ${record.effort ?? ""}
12220
+ ${record.account ?? ""}`;
12221
+ if (lastWritten.get(sessionID) === fingerprint) return false;
12222
+ const file = spawnRecordStorePath();
12223
+ const merged = readEntries(file);
12224
+ merged.delete(sessionID);
12225
+ merged.set(sessionID, record);
12226
+ const cutoff = now - SPAWN_RECORD_MAX_AGE_MS;
12227
+ const kept = [...merged.entries()].filter(([, entry]) => entry.at >= cutoff).sort((a, b) => a[1].at - b[1].at).slice(-MAX_SPAWN_RECORDS);
12228
+ try {
12229
+ mkdirSync6(join14(file, ".."), { recursive: true, mode: 448 });
12230
+ const tmp = `${file}.${process.pid}.tmp`;
12231
+ writeFileSync7(tmp, JSON.stringify({ version: 1, sessions: Object.fromEntries(kept) }), {
12232
+ mode: 384
12233
+ });
12234
+ renameSync4(tmp, file);
12235
+ } catch {
12236
+ return false;
12237
+ }
12238
+ if (lastWritten.size >= MAX_SPAWN_RECORDS) lastWritten.clear();
12239
+ lastWritten.set(sessionID, fingerprint);
12240
+ return true;
12241
+ }
12242
+
12169
12243
  // src/account-transcript.ts
12170
12244
  import { randomUUID as randomUUID6 } from "crypto";
12171
12245
  import { constants as fsConstants } from "fs";
@@ -12227,13 +12301,13 @@ function dedupe2(dirs) {
12227
12301
  }
12228
12302
 
12229
12303
  // src/prompts.ts
12230
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
12231
- import { homedir as homedir10 } from "os";
12304
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync8 } from "fs";
12305
+ import { homedir as homedir11 } from "os";
12232
12306
  import { randomUUID as randomUUID7 } from "crypto";
12233
- import { basename as basename2, dirname as dirname6, isAbsolute as isAbsolute3, join as join14 } from "path";
12307
+ import { basename as basename2, dirname as dirname6, isAbsolute as isAbsolute3, join as join15 } from "path";
12234
12308
  function readPromptFileIfPresent(path15) {
12235
12309
  try {
12236
- const content = readFileSync8(path15, "utf8").trim();
12310
+ const content = readFileSync9(path15, "utf8").trim();
12237
12311
  return content || void 0;
12238
12312
  } catch {
12239
12313
  return void 0;
@@ -12242,7 +12316,7 @@ function readPromptFileIfPresent(path15) {
12242
12316
  function nearestWorkspaceAgentsPrompt(cwd) {
12243
12317
  let dir = cwd;
12244
12318
  while (true) {
12245
- const content = readPromptFileIfPresent(join14(dir, "AGENTS.md"));
12319
+ const content = readPromptFileIfPresent(join15(dir, "AGENTS.md"));
12246
12320
  if (content) return content;
12247
12321
  const parent = dirname6(dir);
12248
12322
  if (parent === dir) return void 0;
@@ -12353,7 +12427,7 @@ var CLAUDE_NATIVE_INSTRUCTION_FILES = /* @__PURE__ */ new Set(["CLAUDE.md", "CLA
12353
12427
  function userAuthoredInstructions(forwarded, opts = {}) {
12354
12428
  const readFile3 = opts.readFile ?? ((path15) => {
12355
12429
  try {
12356
- return readFileSync8(path15, "utf8");
12430
+ return readFileSync9(path15, "utf8");
12357
12431
  } catch {
12358
12432
  return null;
12359
12433
  }
@@ -12394,8 +12468,8 @@ ${options.compressionSummary.trim()}`
12394
12468
  for (const s of extraSystemContent) {
12395
12469
  if (s.trim()) parts.push(s.trim());
12396
12470
  }
12397
- const configRoot = process.env.XDG_CONFIG_HOME ?? join14(homedir10(), ".config");
12398
- const globalAgents = readPromptFileIfPresent(join14(configRoot, "opencode", "AGENTS.md"));
12471
+ const configRoot = process.env.XDG_CONFIG_HOME ?? join15(homedir11(), ".config");
12472
+ const globalAgents = readPromptFileIfPresent(join15(configRoot, "opencode", "AGENTS.md"));
12399
12473
  const workspaceAgents = nearestWorkspaceAgentsPrompt(cwd);
12400
12474
  const forwarded = extraSystemContent.join("\n\n");
12401
12475
  const pushGlobal = !!globalAgents && !forwarded.includes(globalAgents);
@@ -12411,9 +12485,9 @@ ${options.compressionSummary.trim()}`
12411
12485
  if (includeMultiStepHint) parts.push(MULTI_STEP_TASK_HINT);
12412
12486
  const content = parts.join("\n\n");
12413
12487
  if (!content) return void 0;
12414
- const path15 = join14(pluginTmpDir(), `opencode-cc-sys-${randomUUID7()}.md`);
12488
+ const path15 = join15(pluginTmpDir(), `opencode-cc-sys-${randomUUID7()}.md`);
12415
12489
  try {
12416
- writeFileSync7(path15, content, { encoding: "utf8", mode: 384 });
12490
+ writeFileSync8(path15, content, { encoding: "utf8", mode: 384 });
12417
12491
  return path15;
12418
12492
  } catch (err) {
12419
12493
  log.warn("failed to write system prompt file", { error: String(err) });
@@ -15578,6 +15652,13 @@ var ClaudeCodeLanguageModel = class {
15578
15652
  return this.subagentDispatchReleaseStream(dispatchStep, sk, warnings);
15579
15653
  }
15580
15654
  if (!compactionMode) invalidateOtherEffortSessions(baseKey, reasoningEffort);
15655
+ if (!compactionMode && affinity !== "default") {
15656
+ recordSessionSpawn(affinity, {
15657
+ model: stripAccountSuffix(failover.modelId),
15658
+ effort: reasoningEffort,
15659
+ account: (this.config.failoverAccounts?.length ?? 0) > 1 ? failover.target ?? sourceAccount : void 0
15660
+ });
15661
+ }
15581
15662
  const hasPriorConversation = options.prompt.filter((m) => m.role === "user" || m.role === "assistant").length > 1;
15582
15663
  if (!hasPriorConversation) {
15583
15664
  deleteClaudeSessionId(sk);
@@ -15781,7 +15862,7 @@ var ClaudeCodeLanguageModel = class {
15781
15862
  let replayReason = "no-session-for-key";
15782
15863
  if (this.config.resumeAfterRestart !== false && includeHistoryContext && !compactionMode && !forkFromClaudeSessionId && failoverAnswer?.kind !== "switch") {
15783
15864
  const configDir = resolveConfigDir(this.config.configDir);
15784
- const transcriptPath2 = (id, dir) => join15(dir ?? configDir, "projects", encodeCwd(cwd), `${id}.jsonl`);
15865
+ const transcriptPath2 = (id, dir) => join16(dir ?? configDir, "projects", encodeCwd(cwd), `${id}.jsonl`);
15785
15866
  const resumePoint = findResumePoint({
15786
15867
  sessionKey: sk,
15787
15868
  prompt: options.prompt,
@@ -17137,7 +17218,7 @@ var ClaudeCodeLanguageModel = class {
17137
17218
  };
17138
17219
 
17139
17220
  // src/account-tier.ts
17140
- import { readFileSync as readFileSync9 } from "fs";
17221
+ import { readFileSync as readFileSync10 } from "fs";
17141
17222
  import path13 from "path";
17142
17223
  var ORGANIZATION_TYPE_LABELS = {
17143
17224
  claude_max: "Max",
@@ -17180,7 +17261,7 @@ function readTierFromConfigDir(configDir, allowLegacyFallback) {
17180
17261
  function readOauthAccount(file) {
17181
17262
  let raw;
17182
17263
  try {
17183
- raw = readFileSync9(file, "utf8");
17264
+ raw = readFileSync10(file, "utf8");
17184
17265
  } catch {
17185
17266
  return void 0;
17186
17267
  }
@@ -17208,14 +17289,14 @@ function tierLabel(oauthAccount) {
17208
17289
  // src/cleanup-stale.ts
17209
17290
  import {
17210
17291
  existsSync as existsSync5,
17211
- mkdirSync as mkdirSync6,
17212
- readFileSync as readFileSync10,
17292
+ mkdirSync as mkdirSync7,
17293
+ readFileSync as readFileSync11,
17213
17294
  realpathSync as realpathSync4,
17214
17295
  rmSync as rmSync3,
17215
- writeFileSync as writeFileSync8
17296
+ writeFileSync as writeFileSync9
17216
17297
  } from "fs";
17217
- import { homedir as homedir11 } from "os";
17218
- import { dirname as dirname7, join as join16, resolve as resolve5 } from "path";
17298
+ import { homedir as homedir12 } from "os";
17299
+ import { dirname as dirname7, join as join17, resolve as resolve5 } from "path";
17219
17300
  import { fileURLToPath as fileURLToPath4 } from "url";
17220
17301
  var STALE_PACKAGE_NAME = "opencode-claude-code-plugin";
17221
17302
  var SUSPECT_DESCRIPTION_TOKEN = "Claude Code";
@@ -17223,20 +17304,20 @@ var alreadyRan = false;
17223
17304
  function candidateCacheRoots() {
17224
17305
  const xdg = process.env.XDG_CACHE_HOME;
17225
17306
  return [
17226
- xdg ? join16(xdg, "opencode") : null,
17227
- join16(homedir11(), ".cache", "opencode"),
17228
- join16(homedir11(), "Library", "Caches", "opencode")
17307
+ xdg ? join17(xdg, "opencode") : null,
17308
+ join17(homedir12(), ".cache", "opencode"),
17309
+ join17(homedir12(), "Library", "Caches", "opencode")
17229
17310
  ].filter((p) => Boolean(p));
17230
17311
  }
17231
17312
  function userOpencodeJsonPath() {
17232
- const xdgConfig = process.env.XDG_CONFIG_HOME ?? join16(homedir11(), ".config");
17233
- return join16(xdgConfig, "opencode", "opencode.json");
17313
+ const xdgConfig = process.env.XDG_CONFIG_HOME ?? join17(homedir12(), ".config");
17314
+ return join17(xdgConfig, "opencode", "opencode.json");
17234
17315
  }
17235
17316
  function userIntendsToUseUnscoped() {
17236
17317
  const cfg = userOpencodeJsonPath();
17237
17318
  if (!existsSync5(cfg)) return false;
17238
17319
  try {
17239
- const json = JSON.parse(readFileSync10(cfg, "utf8"));
17320
+ const json = JSON.parse(readFileSync11(cfg, "utf8"));
17240
17321
  const plugins = json.plugin;
17241
17322
  if (!Array.isArray(plugins)) return false;
17242
17323
  return plugins.some(
@@ -17257,19 +17338,19 @@ function ourLoadedDir() {
17257
17338
  function ourVersion(dir) {
17258
17339
  if (!dir) return null;
17259
17340
  try {
17260
- const pkg = JSON.parse(readFileSync10(join16(dir, "package.json"), "utf8"));
17341
+ const pkg = JSON.parse(readFileSync11(join17(dir, "package.json"), "utf8"));
17261
17342
  return typeof pkg.version === "string" && pkg.version ? pkg.version : null;
17262
17343
  } catch {
17263
17344
  return null;
17264
17345
  }
17265
17346
  }
17266
17347
  function markerPath() {
17267
- const stateRoot = process.env.XDG_STATE_HOME ?? join16(homedir11(), ".local", "state");
17268
- return join16(stateRoot, "opencode-claude-code-plugin", "cleanup-stale.json");
17348
+ const stateRoot = process.env.XDG_STATE_HOME ?? join17(homedir12(), ".local", "state");
17349
+ return join17(stateRoot, "opencode-claude-code-plugin", "cleanup-stale.json");
17269
17350
  }
17270
17351
  function markerVersion() {
17271
17352
  try {
17272
- const marker = JSON.parse(readFileSync10(markerPath(), "utf8"));
17353
+ const marker = JSON.parse(readFileSync11(markerPath(), "utf8"));
17273
17354
  return typeof marker.version === "string" ? marker.version : null;
17274
17355
  } catch {
17275
17356
  return null;
@@ -17278,8 +17359,8 @@ function markerVersion() {
17278
17359
  function writeMarker(version) {
17279
17360
  const file = markerPath();
17280
17361
  try {
17281
- mkdirSync6(dirname7(file), { recursive: true });
17282
- writeFileSync8(
17362
+ mkdirSync7(dirname7(file), { recursive: true });
17363
+ writeFileSync9(
17283
17364
  file,
17284
17365
  JSON.stringify({ version, at: (/* @__PURE__ */ new Date()).toISOString() }, null, 2) + "\n",
17285
17366
  "utf8"
@@ -17314,7 +17395,7 @@ function cleanupStaleUnscopedInstall() {
17314
17395
  }
17315
17396
  function cleanupOne(cacheRoot, ourDir) {
17316
17397
  if (!existsSync5(cacheRoot)) return;
17317
- const stalePath = join16(cacheRoot, "node_modules", STALE_PACKAGE_NAME);
17398
+ const stalePath = join17(cacheRoot, "node_modules", STALE_PACKAGE_NAME);
17318
17399
  if (!existsSync5(stalePath)) return;
17319
17400
  let realStalePath = stalePath;
17320
17401
  try {
@@ -17322,11 +17403,11 @@ function cleanupOne(cacheRoot, ourDir) {
17322
17403
  } catch {
17323
17404
  }
17324
17405
  if (ourDir && realStalePath === ourDir) return;
17325
- const pkgJsonPath = join16(stalePath, "package.json");
17406
+ const pkgJsonPath = join17(stalePath, "package.json");
17326
17407
  if (!existsSync5(pkgJsonPath)) return;
17327
17408
  let pkg = {};
17328
17409
  try {
17329
- pkg = JSON.parse(readFileSync10(pkgJsonPath, "utf8"));
17410
+ pkg = JSON.parse(readFileSync11(pkgJsonPath, "utf8"));
17330
17411
  } catch {
17331
17412
  return;
17332
17413
  }
@@ -17342,13 +17423,13 @@ function cleanupOne(cacheRoot, ourDir) {
17342
17423
  });
17343
17424
  return;
17344
17425
  }
17345
- const cachePkgJson = join16(cacheRoot, "package.json");
17426
+ const cachePkgJson = join17(cacheRoot, "package.json");
17346
17427
  if (!existsSync5(cachePkgJson)) return;
17347
17428
  try {
17348
- const cfg = JSON.parse(readFileSync10(cachePkgJson, "utf8"));
17429
+ const cfg = JSON.parse(readFileSync11(cachePkgJson, "utf8"));
17349
17430
  if (cfg?.dependencies?.[STALE_PACKAGE_NAME]) {
17350
17431
  delete cfg.dependencies[STALE_PACKAGE_NAME];
17351
- writeFileSync8(cachePkgJson, JSON.stringify(cfg, null, 2) + "\n");
17432
+ writeFileSync9(cachePkgJson, JSON.stringify(cfg, null, 2) + "\n");
17352
17433
  log.info("cleanup-stale: pruned dep from cache package.json");
17353
17434
  }
17354
17435
  } catch (err) {
@@ -18120,12 +18201,12 @@ function configModelsForProvider(providerModels, providerID, modelSuffix) {
18120
18201
  }
18121
18202
  return models;
18122
18203
  }
18123
- function isRecord5(value) {
18204
+ function isRecord6(value) {
18124
18205
  return typeof value === "object" && value !== null && !Array.isArray(value);
18125
18206
  }
18126
18207
  function completeUserModel(id, entry, providerID) {
18127
- const record = isRecord5(entry) ? entry : {};
18128
- const runtimeShaped = isRecord5(record.capabilities) && isRecord5(record.capabilities.input) && isRecord5(record.capabilities.output) && isRecord5(record.cost) && isRecord5(record.cost.cache) && isRecord5(record.api);
18208
+ const record = isRecord6(entry) ? entry : {};
18209
+ const runtimeShaped = isRecord6(record.capabilities) && isRecord6(record.capabilities.input) && isRecord6(record.capabilities.output) && isRecord6(record.cost) && isRecord6(record.cost.cache) && isRecord6(record.api);
18129
18210
  if (runtimeShaped) {
18130
18211
  return toConfigModel({ ...record, providerID });
18131
18212
  }
@@ -18138,8 +18219,8 @@ function completeUserModel(id, entry, providerID) {
18138
18219
  const merged = { ...base, ...record, id };
18139
18220
  for (const key of ["cost", "limit", "variants"]) {
18140
18221
  merged[key] = {
18141
- ...isRecord5(base[key]) ? base[key] : {},
18142
- ...isRecord5(record[key]) ? record[key] : {}
18222
+ ...isRecord6(base[key]) ? base[key] : {},
18223
+ ...isRecord6(record[key]) ? record[key] : {}
18143
18224
  };
18144
18225
  }
18145
18226
  return merged;