@khalilgharbaoui/opencode-claude-code-plugin 0.43.0 → 0.45.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.d.ts +17 -0
- package/dist/index.js +78 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/claude-code-plugin/SKILL.md +4 -2
package/dist/index.d.ts
CHANGED
|
@@ -362,6 +362,12 @@ interface ClaudeCodeConfig {
|
|
|
362
362
|
interactiveAllowTools?: string[];
|
|
363
363
|
/** With interactive: append this plugin's own prompts via --append-system-prompt-file. Defaults to true. */
|
|
364
364
|
interactiveSystemPrompt?: boolean;
|
|
365
|
+
/**
|
|
366
|
+
* With the interactive transport: also append what the operator wrote that
|
|
367
|
+
* opencode forwarded (instruction files and the active agent's own prompt),
|
|
368
|
+
* verbatim and chosen by provenance. Off by default (h #g212).
|
|
369
|
+
*/
|
|
370
|
+
interactiveUserInstructions?: boolean;
|
|
365
371
|
cwd?: string;
|
|
366
372
|
account?: string;
|
|
367
373
|
configDir?: string;
|
|
@@ -481,6 +487,12 @@ interface ClaudeCodeProviderSettings {
|
|
|
481
487
|
interactiveAllowTools?: string[];
|
|
482
488
|
/** With interactive: append this plugin's own prompts via --append-system-prompt-file. Defaults to true. */
|
|
483
489
|
interactiveSystemPrompt?: boolean;
|
|
490
|
+
/**
|
|
491
|
+
* With the interactive transport: also append what the operator wrote that
|
|
492
|
+
* opencode forwarded (instruction files and the active agent's own prompt),
|
|
493
|
+
* verbatim and chosen by provenance. Off by default (h #g212).
|
|
494
|
+
*/
|
|
495
|
+
interactiveUserInstructions?: boolean;
|
|
484
496
|
cwd?: string;
|
|
485
497
|
name?: string;
|
|
486
498
|
providerID?: string;
|
|
@@ -1370,6 +1382,11 @@ type AgentRecord = {
|
|
|
1370
1382
|
* refused. Same account throughout; see `src/model-fallback.ts`.
|
|
1371
1383
|
*/
|
|
1372
1384
|
fallbackModels?: string[];
|
|
1385
|
+
/**
|
|
1386
|
+
* The agent's own prompt as the operator wrote it: the markdown body, or
|
|
1387
|
+
* `prompt` in opencode.json. Read only by `interactiveUserInstructions`.
|
|
1388
|
+
*/
|
|
1389
|
+
prompt?: string;
|
|
1373
1390
|
};
|
|
1374
1391
|
declare function getAgentRegistry(): Record<string, AgentRecord>;
|
|
1375
1392
|
declare function getDefaultSubagentModel(): string | undefined;
|
package/dist/index.js
CHANGED
|
@@ -5897,7 +5897,7 @@ var PLUGIN_LOG_MESSAGES = /* @__PURE__ */ new Set([
|
|
|
5897
5897
|
"two different skills share a name; bridging the one the nearest root claimed",
|
|
5898
5898
|
"unknown permissionPreset; no preset applied and every permission setting is left exactly as configured",
|
|
5899
5899
|
"unsupported file part data type",
|
|
5900
|
-
"unsupported media type for Claude
|
|
5900
|
+
"unsupported media type for Claude attachment block, skipping"
|
|
5901
5901
|
]);
|
|
5902
5902
|
|
|
5903
5903
|
// src/diagnostic-bundle.ts
|
|
@@ -7253,7 +7253,7 @@ var baseCapabilities = {
|
|
|
7253
7253
|
temperature: false,
|
|
7254
7254
|
attachment: true,
|
|
7255
7255
|
toolcall: true,
|
|
7256
|
-
input: { text: true, audio: false, image: true, video: false, pdf:
|
|
7256
|
+
input: { text: true, audio: false, image: true, video: false, pdf: true },
|
|
7257
7257
|
output: { text: true, audio: false, image: false, video: false, pdf: false },
|
|
7258
7258
|
interleaved: false
|
|
7259
7259
|
};
|
|
@@ -7733,6 +7733,13 @@ function parseAgentFrontmatter(text) {
|
|
|
7733
7733
|
}
|
|
7734
7734
|
return record;
|
|
7735
7735
|
}
|
|
7736
|
+
function agentMarkdownBody(text) {
|
|
7737
|
+
if (!text.startsWith("---")) return text.trim() || void 0;
|
|
7738
|
+
const lines = text.split(/\r?\n/);
|
|
7739
|
+
const close = lines.findIndex((line, index) => index > 0 && line.trim() === "---");
|
|
7740
|
+
if (close === -1) return void 0;
|
|
7741
|
+
return lines.slice(close + 1).join("\n").trim() || void 0;
|
|
7742
|
+
}
|
|
7736
7743
|
function parseFallbackModelList(value) {
|
|
7737
7744
|
if (Array.isArray(value)) {
|
|
7738
7745
|
return value.map((entry) => typeof entry === "string" ? entry.trim() : "").filter(Boolean);
|
|
@@ -7756,6 +7763,8 @@ async function readAgentMarkdownRecords(directories) {
|
|
|
7756
7763
|
try {
|
|
7757
7764
|
const text = await readFile(path7.join(directory, entry), "utf8");
|
|
7758
7765
|
records2[name] = parseAgentFrontmatter(text);
|
|
7766
|
+
const prompt = agentMarkdownBody(text);
|
|
7767
|
+
if (prompt) records2[name].prompt = prompt;
|
|
7759
7768
|
} catch (err) {
|
|
7760
7769
|
log.debug("failed to read agent markdown", {
|
|
7761
7770
|
file: path7.join(directory, entry),
|
|
@@ -8001,13 +8010,14 @@ function stripContextReminders(prompt) {
|
|
|
8001
8010
|
});
|
|
8002
8011
|
return removed > 0 ? { prompt: out, removed } : { prompt, removed: 0 };
|
|
8003
8012
|
}
|
|
8013
|
+
var PDF_MEDIA_TYPE = "application/pdf";
|
|
8004
8014
|
var SUPPORTED_IMAGE_TYPES = /* @__PURE__ */ new Set([
|
|
8005
8015
|
"image/jpeg",
|
|
8006
8016
|
"image/png",
|
|
8007
8017
|
"image/gif",
|
|
8008
8018
|
"image/webp"
|
|
8009
8019
|
]);
|
|
8010
|
-
function
|
|
8020
|
+
function toAttachmentBlock(part) {
|
|
8011
8021
|
const raw = part.image ?? part.data ?? part.url ?? part.source?.data;
|
|
8012
8022
|
if (!raw) {
|
|
8013
8023
|
log.warn("file part without data, skipping");
|
|
@@ -8039,8 +8049,14 @@ function toImageBlock(part) {
|
|
|
8039
8049
|
log.warn("unsupported file part data type", { dataType: typeof raw });
|
|
8040
8050
|
return null;
|
|
8041
8051
|
}
|
|
8052
|
+
if (resolvedMediaType.toLowerCase() === PDF_MEDIA_TYPE) {
|
|
8053
|
+
return {
|
|
8054
|
+
type: "document",
|
|
8055
|
+
source: { type: "base64", media_type: PDF_MEDIA_TYPE, data: base64 }
|
|
8056
|
+
};
|
|
8057
|
+
}
|
|
8042
8058
|
if (!resolvedMediaType || !SUPPORTED_IMAGE_TYPES.has(resolvedMediaType)) {
|
|
8043
|
-
log.warn("unsupported media type for Claude
|
|
8059
|
+
log.warn("unsupported media type for Claude attachment block, skipping", {
|
|
8044
8060
|
mediaType: resolvedMediaType
|
|
8045
8061
|
});
|
|
8046
8062
|
return null;
|
|
@@ -8292,11 +8308,11 @@ Now continuing with the current message:
|
|
|
8292
8308
|
content.push({ type: "text", text: part.text });
|
|
8293
8309
|
}
|
|
8294
8310
|
} else if (part.type === "file" || part.type === "image") {
|
|
8295
|
-
const block =
|
|
8311
|
+
const block = toAttachmentBlock(part);
|
|
8296
8312
|
if (block) {
|
|
8297
8313
|
content.push(block);
|
|
8298
8314
|
} else {
|
|
8299
|
-
log.debug("skipped
|
|
8315
|
+
log.debug("skipped unsupported file part", {
|
|
8300
8316
|
mediaType: part.mediaType
|
|
8301
8317
|
});
|
|
8302
8318
|
}
|
|
@@ -8356,7 +8372,7 @@ function getTrailingUserMessages(prompt, opts = {}) {
|
|
|
8356
8372
|
if (part.type === "text") {
|
|
8357
8373
|
if (part.text && part.text.trim()) blocks.push({ type: "text", text: part.text });
|
|
8358
8374
|
} else if (part.type === "file" || part.type === "image") {
|
|
8359
|
-
const block =
|
|
8375
|
+
const block = toAttachmentBlock(part);
|
|
8360
8376
|
if (block) blocks.push(block);
|
|
8361
8377
|
}
|
|
8362
8378
|
}
|
|
@@ -9771,12 +9787,14 @@ function decodeUserEnvelope(chunk, saveImage) {
|
|
|
9771
9787
|
const images = [];
|
|
9772
9788
|
let dropped = 0;
|
|
9773
9789
|
for (const block of content) {
|
|
9774
|
-
const
|
|
9790
|
+
const base64 = block?.source?.type === "base64" && typeof block.source.data === "string";
|
|
9791
|
+
const mediaType = String(block?.source?.media_type).toLowerCase();
|
|
9792
|
+
const extension = base64 && block.type === "image" ? IMAGE_EXTENSIONS[mediaType] : base64 && block.type === "document" && mediaType === "application/pdf" ? "pdf" : void 0;
|
|
9775
9793
|
if (block?.type === "text" && typeof block.text === "string") {
|
|
9776
9794
|
parts.push(block.text);
|
|
9777
9795
|
} else if (extension && saveImage) {
|
|
9778
9796
|
const file = saveImage(Buffer.from(block.source.data, "base64"), extension);
|
|
9779
|
-
if (file) images.push(file);
|
|
9797
|
+
if (file) images.push(extension === "pdf" ? `@${file}` : file);
|
|
9780
9798
|
else dropped++;
|
|
9781
9799
|
} else if (block?.type === "tool_result") {
|
|
9782
9800
|
const v = block.content;
|
|
@@ -9799,7 +9817,7 @@ ${text2}`
|
|
|
9799
9817
|
}
|
|
9800
9818
|
function stageImage(data, extension) {
|
|
9801
9819
|
try {
|
|
9802
|
-
const file = join10(pluginTmpDir(), `
|
|
9820
|
+
const file = join10(pluginTmpDir(), `attachment-${randomUUID5()}.${extension}`);
|
|
9803
9821
|
writeFileSync5(file, data, { mode: 384, flag: "wx" });
|
|
9804
9822
|
return file;
|
|
9805
9823
|
} catch (err) {
|
|
@@ -10284,7 +10302,7 @@ function spawnInteractiveProcess(opts) {
|
|
|
10284
10302
|
import { readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
|
|
10285
10303
|
import { homedir as homedir8 } from "os";
|
|
10286
10304
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
10287
|
-
import { dirname as dirname6, join as join11 } from "path";
|
|
10305
|
+
import { basename as basename2, dirname as dirname6, isAbsolute as isAbsolute3, join as join11 } from "path";
|
|
10288
10306
|
function readPromptFileIfPresent(path11) {
|
|
10289
10307
|
try {
|
|
10290
10308
|
const content = readFileSync7(path11, "utf8").trim();
|
|
@@ -10390,6 +10408,36 @@ function extractSystemMessages(prompt) {
|
|
|
10390
10408
|
}
|
|
10391
10409
|
return out;
|
|
10392
10410
|
}
|
|
10411
|
+
var CLAUDE_NATIVE_INSTRUCTION_FILES = /* @__PURE__ */ new Set(["CLAUDE.md", "CLAUDE.local.md"]);
|
|
10412
|
+
function userAuthoredInstructions(forwarded, opts = {}) {
|
|
10413
|
+
const readFile2 = opts.readFile ?? ((path11) => {
|
|
10414
|
+
try {
|
|
10415
|
+
return readFileSync7(path11, "utf8");
|
|
10416
|
+
} catch {
|
|
10417
|
+
return null;
|
|
10418
|
+
}
|
|
10419
|
+
});
|
|
10420
|
+
const out = [];
|
|
10421
|
+
const agentPrompt = opts.agentPrompt?.trim();
|
|
10422
|
+
if (agentPrompt && forwarded.includes(agentPrompt)) {
|
|
10423
|
+
out.push({ text: agentPrompt, content: agentPrompt });
|
|
10424
|
+
}
|
|
10425
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10426
|
+
for (const match of forwarded.matchAll(/^Instructions from: (.+)$/gm)) {
|
|
10427
|
+
const path11 = match[1].trim();
|
|
10428
|
+
if (seen.has(path11) || !isAbsolute3(path11)) continue;
|
|
10429
|
+
if (CLAUDE_NATIVE_INSTRUCTION_FILES.has(basename2(path11))) continue;
|
|
10430
|
+
seen.add(path11);
|
|
10431
|
+
const file = readFile2(path11);
|
|
10432
|
+
if (!file?.trim()) continue;
|
|
10433
|
+
const start = match.index + match[0].length + 1;
|
|
10434
|
+
const content = forwarded.startsWith(file, start) ? file : forwarded.startsWith(file.trimEnd(), start) ? file.trimEnd() : null;
|
|
10435
|
+
if (content === null) continue;
|
|
10436
|
+
out.push({ text: `Instructions from: ${path11}
|
|
10437
|
+
${content}`, content });
|
|
10438
|
+
}
|
|
10439
|
+
return out;
|
|
10440
|
+
}
|
|
10393
10441
|
function buildAppendedSystemPrompt(cwd, includeMultiStepHint = true, extraSystemContent = [], options = {}) {
|
|
10394
10442
|
const parts = [];
|
|
10395
10443
|
if (options.compressionSummary?.trim()) {
|
|
@@ -10414,6 +10462,11 @@ ${options.compressionSummary.trim()}`
|
|
|
10414
10462
|
if (pushGlobal) parts.push(globalAgents);
|
|
10415
10463
|
if (pushWorkspace) parts.push(workspaceAgents);
|
|
10416
10464
|
if (pushGlobal || pushWorkspace) parts.push(AGENTS_MAINTENANCE_HINT);
|
|
10465
|
+
for (const instruction of options.userInstructions ?? []) {
|
|
10466
|
+
const authored = instruction.content.trim();
|
|
10467
|
+
if (authored === globalAgents || authored === workspaceAgents) continue;
|
|
10468
|
+
parts.push(instruction.text);
|
|
10469
|
+
}
|
|
10417
10470
|
if (includeMultiStepHint) parts.push(MULTI_STEP_TASK_HINT);
|
|
10418
10471
|
const content = parts.join("\n\n");
|
|
10419
10472
|
if (!content) return void 0;
|
|
@@ -13906,6 +13959,14 @@ var ClaudeCodeLanguageModel = class {
|
|
|
13906
13959
|
"WebFetch"
|
|
13907
13960
|
]
|
|
13908
13961
|
];
|
|
13962
|
+
const userInstructions = self.config.interactiveUserInstructions === true ? userAuthoredInstructions(extractSystemMessages(options.prompt).join("\n\n"), {
|
|
13963
|
+
agentPrompt: getAgentRegistry()[self.getOpencodeAgent(options) ?? ""]?.prompt
|
|
13964
|
+
}) : [];
|
|
13965
|
+
if (userInstructions.length > 0) {
|
|
13966
|
+
log.info("forwarding user-authored instructions to the interactive TUI", {
|
|
13967
|
+
count: userInstructions.length
|
|
13968
|
+
});
|
|
13969
|
+
}
|
|
13909
13970
|
const systemPromptFile = self.config.interactiveSystemPrompt === false ? void 0 : buildAppendedSystemPrompt(
|
|
13910
13971
|
cwd,
|
|
13911
13972
|
self.config.multiStepContinuation !== false,
|
|
@@ -13927,7 +13988,8 @@ var ClaudeCodeLanguageModel = class {
|
|
|
13927
13988
|
opencodeCompressEnabled: wiring.opencodeToolDefs.some(
|
|
13928
13989
|
(t) => t.name === "compress"
|
|
13929
13990
|
),
|
|
13930
|
-
compressionSummary: getCompressionSummary(sk)
|
|
13991
|
+
compressionSummary: getCompressionSummary(sk),
|
|
13992
|
+
userInstructions
|
|
13931
13993
|
}
|
|
13932
13994
|
);
|
|
13933
13995
|
if (self.config.interactiveSystemPrompt === false) {
|
|
@@ -15461,7 +15523,8 @@ function createClaudeCode(settings = {}) {
|
|
|
15461
15523
|
interactive: settings.interactive,
|
|
15462
15524
|
interactiveBypass: settings.interactiveBypass,
|
|
15463
15525
|
interactiveAllowTools: settings.interactiveAllowTools,
|
|
15464
|
-
interactiveSystemPrompt: settings.interactiveSystemPrompt
|
|
15526
|
+
interactiveSystemPrompt: settings.interactiveSystemPrompt,
|
|
15527
|
+
interactiveUserInstructions: settings.interactiveUserInstructions
|
|
15465
15528
|
});
|
|
15466
15529
|
};
|
|
15467
15530
|
const provider = function(modelId) {
|
|
@@ -15680,7 +15743,8 @@ async function buildAgentRegistry(config) {
|
|
|
15680
15743
|
forceModel: pick("forceModel") ?? records2[name]?.forceModel,
|
|
15681
15744
|
reasoningEffort: pick("reasoningEffort") ?? records2[name]?.reasoningEffort,
|
|
15682
15745
|
cacheTtl: pick("cacheTtl") ?? records2[name]?.cacheTtl,
|
|
15683
|
-
fallbackModels: declaredChain.length ? declaredChain : records2[name]?.fallbackModels
|
|
15746
|
+
fallbackModels: declaredChain.length ? declaredChain : records2[name]?.fallbackModels,
|
|
15747
|
+
prompt: pick("prompt") ?? records2[name]?.prompt
|
|
15684
15748
|
};
|
|
15685
15749
|
}
|
|
15686
15750
|
setAgentRegistry(records2);
|