@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 +123 -42
- package/dist/index.js.map +1 -1
- package/dist/tui.js +689 -0
- package/dist/tui.js.map +1 -0
- package/package.json +4 -1
- package/skills/claude-code-plugin/SKILL.md +30 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/claude-code-language-model.ts
|
|
2
|
-
import { join as
|
|
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
|
|
12231
|
-
import { homedir as
|
|
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
|
|
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 =
|
|
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(
|
|
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
|
|
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 ??
|
|
12398
|
-
const globalAgents = readPromptFileIfPresent(
|
|
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 =
|
|
12488
|
+
const path15 = join15(pluginTmpDir(), `opencode-cc-sys-${randomUUID7()}.md`);
|
|
12415
12489
|
try {
|
|
12416
|
-
|
|
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) =>
|
|
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
|
|
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 =
|
|
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
|
|
17212
|
-
readFileSync as
|
|
17292
|
+
mkdirSync as mkdirSync7,
|
|
17293
|
+
readFileSync as readFileSync11,
|
|
17213
17294
|
realpathSync as realpathSync4,
|
|
17214
17295
|
rmSync as rmSync3,
|
|
17215
|
-
writeFileSync as
|
|
17296
|
+
writeFileSync as writeFileSync9
|
|
17216
17297
|
} from "fs";
|
|
17217
|
-
import { homedir as
|
|
17218
|
-
import { dirname as dirname7, join as
|
|
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 ?
|
|
17227
|
-
|
|
17228
|
-
|
|
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 ??
|
|
17233
|
-
return
|
|
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(
|
|
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(
|
|
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 ??
|
|
17268
|
-
return
|
|
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(
|
|
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
|
-
|
|
17282
|
-
|
|
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 =
|
|
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 =
|
|
17406
|
+
const pkgJsonPath = join17(stalePath, "package.json");
|
|
17326
17407
|
if (!existsSync5(pkgJsonPath)) return;
|
|
17327
17408
|
let pkg = {};
|
|
17328
17409
|
try {
|
|
17329
|
-
pkg = JSON.parse(
|
|
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 =
|
|
17426
|
+
const cachePkgJson = join17(cacheRoot, "package.json");
|
|
17346
17427
|
if (!existsSync5(cachePkgJson)) return;
|
|
17347
17428
|
try {
|
|
17348
|
-
const cfg = JSON.parse(
|
|
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
|
-
|
|
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
|
|
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 =
|
|
18128
|
-
const runtimeShaped =
|
|
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
|
-
...
|
|
18142
|
-
...
|
|
18222
|
+
...isRecord6(base[key]) ? base[key] : {},
|
|
18223
|
+
...isRecord6(record[key]) ? record[key] : {}
|
|
18143
18224
|
};
|
|
18144
18225
|
}
|
|
18145
18226
|
return merged;
|