@khalilgharbaoui/opencode-claude-code-plugin 0.26.2 → 0.27.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/README.md +31 -4
- package/dist/index.d.ts +50 -9
- package/dist/index.js +398 -106
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/claude-code-plugin/SKILL.md +36 -11
package/dist/index.js
CHANGED
|
@@ -514,6 +514,9 @@ function translateStreamForHost(stream, dialect = "v1") {
|
|
|
514
514
|
);
|
|
515
515
|
}
|
|
516
516
|
|
|
517
|
+
// src/account-failover.ts
|
|
518
|
+
import { homedir as homedir2 } from "os";
|
|
519
|
+
|
|
517
520
|
// src/accounts.ts
|
|
518
521
|
import { chmod, lstat, mkdir, readlink, symlink, writeFile } from "fs/promises";
|
|
519
522
|
import path from "path";
|
|
@@ -550,6 +553,10 @@ function accountConfigDir(account) {
|
|
|
550
553
|
if (!normalized || normalized === DEFAULT_ACCOUNT) return void 0;
|
|
551
554
|
return `~/.claude-${normalized}`;
|
|
552
555
|
}
|
|
556
|
+
function accountConfigDirPath(account) {
|
|
557
|
+
const dir = accountConfigDir(account);
|
|
558
|
+
return dir ? expandHome(dir) : void 0;
|
|
559
|
+
}
|
|
553
560
|
function expandHome(value) {
|
|
554
561
|
const home = process.env.HOME ?? process.env.USERPROFILE;
|
|
555
562
|
if (value === "~") return home ?? value;
|
|
@@ -875,6 +882,27 @@ function reportCompactBoundary(msg) {
|
|
|
875
882
|
});
|
|
876
883
|
return formatCompactBoundaryNote(boundary);
|
|
877
884
|
}
|
|
885
|
+
var CONVERSATION_RESET_MARKER = "\u258C **claude code reset:**";
|
|
886
|
+
function parseConversationReset(msg) {
|
|
887
|
+
if (msg.type !== "conversation_reset") return null;
|
|
888
|
+
const newConversationId = str(msg.new_conversation_id);
|
|
889
|
+
if (!newConversationId) return null;
|
|
890
|
+
return { newConversationId, previousSessionId: str(msg.session_id) };
|
|
891
|
+
}
|
|
892
|
+
function formatConversationResetNote() {
|
|
893
|
+
return `
|
|
894
|
+
${CONVERSATION_RESET_MARKER} Claude Code cleared its conversation, so from here on Claude does not see the earlier messages this chat still shows. Start a new opencode session for a clean slate, or restate what matters.
|
|
895
|
+
`;
|
|
896
|
+
}
|
|
897
|
+
function reportConversationReset(msg) {
|
|
898
|
+
const reset = parseConversationReset(msg);
|
|
899
|
+
if (!reset) return null;
|
|
900
|
+
log.notice("claude code reset its conversation", {
|
|
901
|
+
newConversationId: reset.newConversationId,
|
|
902
|
+
previousSessionId: reset.previousSessionId ?? null
|
|
903
|
+
});
|
|
904
|
+
return formatConversationResetNote();
|
|
905
|
+
}
|
|
878
906
|
var RESULT_ERROR_MARKER = "\u258C **claude code error:**";
|
|
879
907
|
var RESULT_SUBTYPES = {
|
|
880
908
|
error_max_turns: "it hit its internal turn limit before finishing",
|
|
@@ -1082,6 +1110,35 @@ var ACCOUNT_LIMIT_PATTERNS = [
|
|
|
1082
1110
|
/third-party apps now draw from your extra usage/i,
|
|
1083
1111
|
/you[’'`]?ve hit your individual spend limit/i
|
|
1084
1112
|
];
|
|
1113
|
+
var ACCOUNT_BLOCK_MARKER = "\u258C **claude account:**";
|
|
1114
|
+
var ACCOUNT_BLOCKS = {
|
|
1115
|
+
authentication_failed: { what: "is not logged in (its login expired or was revoked)", login: true },
|
|
1116
|
+
oauth_org_not_allowed: { what: "belongs to an organization that does not allow this login", login: false },
|
|
1117
|
+
account_on_hold: { what: "is on hold", login: false },
|
|
1118
|
+
verification_required: { what: "needs to be verified at claude.ai", login: false },
|
|
1119
|
+
billing_error: { what: "has a billing problem", login: false }
|
|
1120
|
+
};
|
|
1121
|
+
function accountBlockKind(msg) {
|
|
1122
|
+
if (msg.type !== "assistant" || typeof msg.error !== "string") return null;
|
|
1123
|
+
return Object.prototype.hasOwnProperty.call(ACCOUNT_BLOCKS, msg.error) ? msg.error : null;
|
|
1124
|
+
}
|
|
1125
|
+
function describeAccountBlock(kind) {
|
|
1126
|
+
return ACCOUNT_BLOCKS[kind].what;
|
|
1127
|
+
}
|
|
1128
|
+
function loginCommandFor(configDir, home = homedir2()) {
|
|
1129
|
+
if (!configDir) return "claude auth login";
|
|
1130
|
+
const shown = configDir === home || configDir.startsWith(`${home}/`) ? `~${configDir.slice(home.length)}` : configDir;
|
|
1131
|
+
return `CLAUDE_CONFIG_DIR=${shown} claude auth login`;
|
|
1132
|
+
}
|
|
1133
|
+
function formatAccountBlockNote(input) {
|
|
1134
|
+
const block = ACCOUNT_BLOCKS[input.kind];
|
|
1135
|
+
const account = normalizeAccountName(input.account || DEFAULT_ACCOUNT);
|
|
1136
|
+
const fix = block.login ? `Log in again with \`${loginCommandFor(input.configDir)}\`, then resend your message.` : "Check the account at claude.ai, then resend your message.";
|
|
1137
|
+
const then = input.offeringSwitch ? " Or pick another account below." : "";
|
|
1138
|
+
return `
|
|
1139
|
+
${ACCOUNT_BLOCK_MARKER} the Claude account "${account}" ${block.what}. ${fix}${then}
|
|
1140
|
+
`;
|
|
1141
|
+
}
|
|
1085
1142
|
function isAccountLimitError(input) {
|
|
1086
1143
|
if (input.rateLimit && isRateLimitRejected(input.rateLimit)) return true;
|
|
1087
1144
|
const text = input.resultText;
|
|
@@ -1187,7 +1244,7 @@ function createAccountFailoverQuestionCall(sessionKey2, input, toolCallId = `${A
|
|
|
1187
1244
|
const candidates = input.candidates.map((c) => normalizeAccountName(c));
|
|
1188
1245
|
const resets = describeReset(input.resetsAt);
|
|
1189
1246
|
const until = resets ?? "opencode restarts";
|
|
1190
|
-
const question = [
|
|
1247
|
+
const question = input.reason ? `The Claude account "${source}" ${input.reason}. Continue this task on another configured account? Leaving this unanswered waits, at no cost.` : [
|
|
1191
1248
|
`The Claude account "${source}" is out of usage`,
|
|
1192
1249
|
input.window ? ` in ${input.window}` : "",
|
|
1193
1250
|
resets ? `, which resets at ${resets}` : "",
|
|
@@ -4689,11 +4746,13 @@ var PLUGIN_NOTE_MARKERS = [
|
|
|
4689
4746
|
...LEGACY_INLINE_ASIDE_MARKERS,
|
|
4690
4747
|
TURN_STATS_MARKER,
|
|
4691
4748
|
COMPACT_BOUNDARY_MARKER,
|
|
4749
|
+
CONVERSATION_RESET_MARKER,
|
|
4692
4750
|
RATE_LIMIT_MARKER,
|
|
4693
4751
|
RESULT_ERROR_MARKER,
|
|
4694
4752
|
DOCTOR_MARKER,
|
|
4695
4753
|
STREAM_TIMEOUT_MARKER,
|
|
4696
|
-
FAILOVER_MARKER
|
|
4754
|
+
FAILOVER_MARKER,
|
|
4755
|
+
ACCOUNT_BLOCK_MARKER
|
|
4697
4756
|
];
|
|
4698
4757
|
function isPluginNote(part) {
|
|
4699
4758
|
if (!part || part.type !== "text" || typeof part.text !== "string") return false;
|
|
@@ -5539,13 +5598,48 @@ function dirExists2(p) {
|
|
|
5539
5598
|
return false;
|
|
5540
5599
|
}
|
|
5541
5600
|
}
|
|
5542
|
-
function
|
|
5601
|
+
function realPath(p) {
|
|
5543
5602
|
try {
|
|
5544
|
-
return fs5.
|
|
5603
|
+
return fs5.realpathSync(p);
|
|
5545
5604
|
} catch {
|
|
5546
|
-
return
|
|
5605
|
+
return path7.resolve(p);
|
|
5547
5606
|
}
|
|
5548
5607
|
}
|
|
5608
|
+
function envEnabled(name) {
|
|
5609
|
+
const value = process.env[name];
|
|
5610
|
+
if (value === void 0) return false;
|
|
5611
|
+
const normalized = value.trim().toLowerCase();
|
|
5612
|
+
return normalized !== "" && normalized !== "0" && normalized !== "false";
|
|
5613
|
+
}
|
|
5614
|
+
var SAFE_SKILL_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
5615
|
+
var FRONTMATTER_BLOCK = /^---\r?\n([\s\S]*?)\r?\n---/;
|
|
5616
|
+
var FRONTMATTER_NAME = /^name:[ \t]*(.+?)[ \t]*$/m;
|
|
5617
|
+
function declaredSkillName(source) {
|
|
5618
|
+
const block = FRONTMATTER_BLOCK.exec(source);
|
|
5619
|
+
if (!block) return null;
|
|
5620
|
+
const line = FRONTMATTER_NAME.exec(block[1]);
|
|
5621
|
+
if (!line) return null;
|
|
5622
|
+
const name = line[1].trim().replace(/^["']|["']$/g, "").trim();
|
|
5623
|
+
return SAFE_SKILL_NAME.test(name) ? name : null;
|
|
5624
|
+
}
|
|
5625
|
+
function readSkill(root, entry) {
|
|
5626
|
+
const dir = path7.join(root, entry);
|
|
5627
|
+
let source;
|
|
5628
|
+
try {
|
|
5629
|
+
source = fs5.readFileSync(path7.join(dir, "SKILL.md"), "utf8");
|
|
5630
|
+
} catch {
|
|
5631
|
+
return null;
|
|
5632
|
+
}
|
|
5633
|
+
return {
|
|
5634
|
+
name: declaredSkillName(source) ?? entry,
|
|
5635
|
+
dir,
|
|
5636
|
+
realDir: realPath(dir),
|
|
5637
|
+
contentHash: crypto3.createHash("sha256").update(source).digest("hex")
|
|
5638
|
+
};
|
|
5639
|
+
}
|
|
5640
|
+
function sameSkill(a, b) {
|
|
5641
|
+
return a.realDir === b.realDir || a.contentHash === b.contentHash;
|
|
5642
|
+
}
|
|
5549
5643
|
function skillRoots(cwd) {
|
|
5550
5644
|
const roots = [];
|
|
5551
5645
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -5555,19 +5649,36 @@ function skillRoots(cwd) {
|
|
|
5555
5649
|
seen.add(abs);
|
|
5556
5650
|
if (dirExists2(abs)) roots.push(abs);
|
|
5557
5651
|
};
|
|
5652
|
+
const home = os3.homedir();
|
|
5653
|
+
const homeDir = home ? path7.resolve(home) : null;
|
|
5558
5654
|
let current = path7.resolve(cwd);
|
|
5559
5655
|
while (true) {
|
|
5560
5656
|
push(path7.join(current, ".opencode", "skills"));
|
|
5657
|
+
if (current !== homeDir) {
|
|
5658
|
+
push(path7.join(current, ".claude", "skills"));
|
|
5659
|
+
push(path7.join(current, ".agents", "skills"));
|
|
5660
|
+
}
|
|
5561
5661
|
const parent = path7.dirname(current);
|
|
5562
5662
|
if (parent === current) break;
|
|
5563
5663
|
current = parent;
|
|
5564
5664
|
}
|
|
5565
|
-
|
|
5566
|
-
if (home) push(path7.join(home, ".opencode", "skills"));
|
|
5665
|
+
if (homeDir) push(path7.join(homeDir, ".opencode", "skills"));
|
|
5567
5666
|
const envDir = process.env.OPENCODE_CONFIG_DIR;
|
|
5568
|
-
if (envDir)
|
|
5569
|
-
|
|
5570
|
-
|
|
5667
|
+
if (envDir) {
|
|
5668
|
+
push(path7.join(envDir, "skills"));
|
|
5669
|
+
push(path7.join(envDir, "skill"));
|
|
5670
|
+
}
|
|
5671
|
+
const xdg = process.env.XDG_CONFIG_HOME ?? (homeDir ? path7.join(homeDir, ".config") : null);
|
|
5672
|
+
if (xdg) {
|
|
5673
|
+
push(path7.join(xdg, "opencode", "skills"));
|
|
5674
|
+
push(path7.join(xdg, "opencode", "skill"));
|
|
5675
|
+
}
|
|
5676
|
+
if (homeDir && !envEnabled("OPENCODE_DISABLE_EXTERNAL_SKILLS")) {
|
|
5677
|
+
if (!envEnabled("OPENCODE_DISABLE_CLAUDE_CODE_SKILLS")) {
|
|
5678
|
+
push(path7.join(homeDir, ".claude", "skills"));
|
|
5679
|
+
}
|
|
5680
|
+
push(path7.join(homeDir, ".agents", "skills"));
|
|
5681
|
+
}
|
|
5571
5682
|
return roots;
|
|
5572
5683
|
}
|
|
5573
5684
|
function collectSkills(root, claimed, found) {
|
|
@@ -5579,19 +5690,28 @@ function collectSkills(root, claimed, found) {
|
|
|
5579
5690
|
}
|
|
5580
5691
|
for (const entry of entries) {
|
|
5581
5692
|
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
if (
|
|
5585
|
-
const
|
|
5586
|
-
if (
|
|
5587
|
-
|
|
5588
|
-
|
|
5693
|
+
if (entry.name.startsWith(".")) continue;
|
|
5694
|
+
const skill = readSkill(root, entry.name);
|
|
5695
|
+
if (!skill) continue;
|
|
5696
|
+
const winner = claimed.get(skill.name);
|
|
5697
|
+
if (winner) {
|
|
5698
|
+
if (!sameSkill(winner, skill)) {
|
|
5699
|
+
log.warn("two different skills share a name; bridging the one the nearest root claimed", {
|
|
5700
|
+
name: skill.name,
|
|
5701
|
+
bridged: winner.dir,
|
|
5702
|
+
ignored: skill.dir
|
|
5703
|
+
});
|
|
5704
|
+
}
|
|
5705
|
+
continue;
|
|
5706
|
+
}
|
|
5707
|
+
claimed.set(skill.name, skill);
|
|
5708
|
+
found.push(skill);
|
|
5589
5709
|
}
|
|
5590
5710
|
}
|
|
5591
5711
|
var byName = (a, b) => a.name.localeCompare(b.name);
|
|
5592
5712
|
function discoverOpencodeSkills(cwd) {
|
|
5593
5713
|
const found = [];
|
|
5594
|
-
const claimed = /* @__PURE__ */ new
|
|
5714
|
+
const claimed = /* @__PURE__ */ new Map();
|
|
5595
5715
|
for (const root of skillRoots(cwd)) collectSkills(root, claimed, found);
|
|
5596
5716
|
return found.sort(byName);
|
|
5597
5717
|
}
|
|
@@ -5599,17 +5719,105 @@ function discoverBundledSkills() {
|
|
|
5599
5719
|
const root = bundledSkillsDir();
|
|
5600
5720
|
if (!root) return [];
|
|
5601
5721
|
const found = [];
|
|
5602
|
-
collectSkills(root, /* @__PURE__ */ new
|
|
5722
|
+
collectSkills(root, /* @__PURE__ */ new Map(), found);
|
|
5603
5723
|
return found.sort(byName);
|
|
5604
5724
|
}
|
|
5605
|
-
function
|
|
5606
|
-
const
|
|
5607
|
-
if (
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
return
|
|
5725
|
+
function claudeConfigDir(configDir) {
|
|
5726
|
+
const explicit = configDir ?? process.env.CLAUDE_CONFIG_DIR;
|
|
5727
|
+
if (explicit) return path7.resolve(expandHome(explicit));
|
|
5728
|
+
const home = os3.homedir();
|
|
5729
|
+
return home ? path7.resolve(home, ".claude") : null;
|
|
5730
|
+
}
|
|
5731
|
+
function installedPluginSkillRoots(configDir, cwd) {
|
|
5732
|
+
if (!configDir) return [];
|
|
5733
|
+
let parsed;
|
|
5734
|
+
try {
|
|
5735
|
+
parsed = JSON.parse(
|
|
5736
|
+
fs5.readFileSync(path7.join(configDir, "plugins", "installed_plugins.json"), "utf8")
|
|
5737
|
+
);
|
|
5738
|
+
} catch {
|
|
5739
|
+
return [];
|
|
5740
|
+
}
|
|
5741
|
+
const plugins = parsed?.plugins;
|
|
5742
|
+
if (!plugins || typeof plugins !== "object") return [];
|
|
5743
|
+
const workspace = path7.resolve(cwd);
|
|
5744
|
+
const roots = [];
|
|
5745
|
+
for (const installs of Object.values(plugins)) {
|
|
5746
|
+
if (!Array.isArray(installs)) continue;
|
|
5747
|
+
for (const install of installs) {
|
|
5748
|
+
const entry = install;
|
|
5749
|
+
if (typeof entry?.installPath !== "string" || !entry.installPath) continue;
|
|
5750
|
+
if (entry.scope !== "user" && !coversWorkspace(entry.projectPath, workspace)) continue;
|
|
5751
|
+
roots.push(path7.join(entry.installPath, "skills"));
|
|
5752
|
+
}
|
|
5753
|
+
}
|
|
5754
|
+
return roots;
|
|
5755
|
+
}
|
|
5756
|
+
function coversWorkspace(projectPath, workspace) {
|
|
5757
|
+
if (typeof projectPath !== "string" || !projectPath) return false;
|
|
5758
|
+
const root = path7.resolve(projectPath);
|
|
5759
|
+
if (root === workspace) return true;
|
|
5760
|
+
const rel = path7.relative(root, workspace);
|
|
5761
|
+
return rel !== "" && !rel.startsWith("..") && !path7.isAbsolute(rel);
|
|
5762
|
+
}
|
|
5763
|
+
function discoverNativeClaudeSkills(opts) {
|
|
5764
|
+
const found = [];
|
|
5765
|
+
const scanned = /* @__PURE__ */ new Set();
|
|
5766
|
+
const scan = (root, scope) => {
|
|
5767
|
+
const abs = path7.resolve(root);
|
|
5768
|
+
if (scanned.has(abs)) return;
|
|
5769
|
+
scanned.add(abs);
|
|
5770
|
+
let entries;
|
|
5771
|
+
try {
|
|
5772
|
+
entries = fs5.readdirSync(abs, { withFileTypes: true });
|
|
5773
|
+
} catch {
|
|
5774
|
+
return;
|
|
5775
|
+
}
|
|
5776
|
+
for (const entry of entries) {
|
|
5777
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
5778
|
+
if (entry.name.startsWith(".")) continue;
|
|
5779
|
+
const skill = readSkill(abs, entry.name);
|
|
5780
|
+
if (skill) found.push({ ...skill, scope });
|
|
5781
|
+
}
|
|
5782
|
+
};
|
|
5783
|
+
const configDir = claudeConfigDir(opts.configDir);
|
|
5784
|
+
if (configDir) scan(path7.join(configDir, "skills"), "user");
|
|
5785
|
+
const home = os3.homedir();
|
|
5786
|
+
const homeDir = home ? path7.resolve(home) : null;
|
|
5787
|
+
let current = path7.resolve(opts.cwd);
|
|
5788
|
+
while (true) {
|
|
5789
|
+
if (current !== homeDir) scan(path7.join(current, ".claude", "skills"), "project");
|
|
5790
|
+
const parent = path7.dirname(current);
|
|
5791
|
+
if (parent === current) break;
|
|
5792
|
+
current = parent;
|
|
5793
|
+
}
|
|
5794
|
+
for (const root of installedPluginSkillRoots(configDir, opts.cwd)) scan(root, "plugin");
|
|
5795
|
+
return found;
|
|
5796
|
+
}
|
|
5797
|
+
function dropNativelyLoadedSkills(skills, native) {
|
|
5798
|
+
if (native.length === 0) return { bridged: skills, skipped: [] };
|
|
5799
|
+
const bridged = [];
|
|
5800
|
+
const skipped = [];
|
|
5801
|
+
for (const skill of skills) {
|
|
5802
|
+
const sameCopy = native.find((n) => n.realDir === skill.realDir);
|
|
5803
|
+
const sameContent = sameCopy ?? native.find((n) => n.contentHash === skill.contentHash);
|
|
5804
|
+
const nameTaken = sameContent ?? native.find((n) => n.scope !== "plugin" && n.name === skill.name);
|
|
5805
|
+
if (!nameTaken) {
|
|
5806
|
+
bridged.push(skill);
|
|
5807
|
+
continue;
|
|
5808
|
+
}
|
|
5809
|
+
const reason = sameCopy ? "same-copy" : sameContent ? "same-content" : "name-taken";
|
|
5810
|
+
skipped.push({ skill, native: nameTaken, reason });
|
|
5811
|
+
if (reason === "name-taken") {
|
|
5812
|
+
log.warn("claude already registers a different skill under this name; not bridging ours", {
|
|
5813
|
+
name: skill.name,
|
|
5814
|
+
claudeLoads: nameTaken.dir,
|
|
5815
|
+
notBridged: skill.dir,
|
|
5816
|
+
scope: nameTaken.scope
|
|
5817
|
+
});
|
|
5818
|
+
}
|
|
5819
|
+
}
|
|
5820
|
+
return { bridged, skipped };
|
|
5613
5821
|
}
|
|
5614
5822
|
function readBundledSkillInfos() {
|
|
5615
5823
|
return discoverBundledSkills().flatMap((skill) => {
|
|
@@ -5646,6 +5854,13 @@ function linkSkill(source, target) {
|
|
|
5646
5854
|
fs5.cpSync(source, target, { recursive: true, dereference: true });
|
|
5647
5855
|
}
|
|
5648
5856
|
}
|
|
5857
|
+
function fileExists2(p) {
|
|
5858
|
+
try {
|
|
5859
|
+
return fs5.statSync(p).isFile();
|
|
5860
|
+
} catch {
|
|
5861
|
+
return false;
|
|
5862
|
+
}
|
|
5863
|
+
}
|
|
5649
5864
|
function buildSkillPluginDir(skills) {
|
|
5650
5865
|
if (skills.length === 0) return null;
|
|
5651
5866
|
const fingerprint = skills.map((s) => `${s.name}\0${s.dir}`).join("\n");
|
|
@@ -5681,11 +5896,36 @@ function buildSkillPluginDir(skills) {
|
|
|
5681
5896
|
}
|
|
5682
5897
|
return root;
|
|
5683
5898
|
}
|
|
5899
|
+
function registerBundledSkillPath(config) {
|
|
5900
|
+
const dir = bundledSkillsDir();
|
|
5901
|
+
if (!dir) return false;
|
|
5902
|
+
config.skills ??= {};
|
|
5903
|
+
const paths = config.skills.paths ??= [];
|
|
5904
|
+
if (paths.some((p) => path7.resolve(p) === dir)) return false;
|
|
5905
|
+
paths.push(dir);
|
|
5906
|
+
return true;
|
|
5907
|
+
}
|
|
5684
5908
|
async function resolveSkillPluginDirs(opts) {
|
|
5685
5909
|
const user = opts.enabled ? discoverOpencodeSkills(opts.cwd) : [];
|
|
5686
5910
|
const claimed = new Set(user.map((s) => s.name));
|
|
5687
5911
|
const bundled = discoverBundledSkills().filter((s) => !claimed.has(s.name));
|
|
5688
|
-
|
|
5912
|
+
let skills = [...user, ...bundled].sort(byName);
|
|
5913
|
+
if (opts.skipNative !== false && skills.length > 0) {
|
|
5914
|
+
const native = discoverNativeClaudeSkills({ cwd: opts.cwd, configDir: opts.configDir });
|
|
5915
|
+
const { bridged, skipped } = dropNativelyLoadedSkills(skills, native);
|
|
5916
|
+
if (skipped.length > 0) {
|
|
5917
|
+
log.info("skills claude code already loads; left unbridged", {
|
|
5918
|
+
count: skipped.length,
|
|
5919
|
+
skipped: skipped.map((s) => ({
|
|
5920
|
+
name: s.skill.name,
|
|
5921
|
+
reason: s.reason,
|
|
5922
|
+
opencode: s.skill.dir,
|
|
5923
|
+
claude: s.native.dir
|
|
5924
|
+
}))
|
|
5925
|
+
});
|
|
5926
|
+
}
|
|
5927
|
+
skills = bridged;
|
|
5928
|
+
}
|
|
5689
5929
|
if (skills.length === 0) return [];
|
|
5690
5930
|
const supported = await detectCliSupportsFlag(opts.cliPath, "--plugin-dir");
|
|
5691
5931
|
if (!supported) {
|
|
@@ -6289,7 +6529,7 @@ function spawnInteractiveProcess(opts) {
|
|
|
6289
6529
|
// src/claude-code-language-model.ts
|
|
6290
6530
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
6291
6531
|
import { unlink as unlink3 } from "fs/promises";
|
|
6292
|
-
import { homedir as
|
|
6532
|
+
import { homedir as homedir6, tmpdir as tmpdir2 } from "os";
|
|
6293
6533
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
6294
6534
|
import { dirname as dirname5, join as join8 } from "path";
|
|
6295
6535
|
async function isProxyCallStillServed(callId) {
|
|
@@ -6653,7 +6893,7 @@ ${options.compressionSummary.trim()}`
|
|
|
6653
6893
|
for (const s of extraSystemContent) {
|
|
6654
6894
|
if (s.trim()) parts.push(s.trim());
|
|
6655
6895
|
}
|
|
6656
|
-
const configRoot = process.env.XDG_CONFIG_HOME ?? join8(
|
|
6896
|
+
const configRoot = process.env.XDG_CONFIG_HOME ?? join8(homedir6(), ".config");
|
|
6657
6897
|
const globalAgents = readPromptFileIfPresent(join8(configRoot, "opencode", "AGENTS.md"));
|
|
6658
6898
|
const workspaceAgents = nearestWorkspaceAgentsPrompt(cwd);
|
|
6659
6899
|
const forwarded = extraSystemContent.join("\n\n");
|
|
@@ -6927,6 +7167,18 @@ var ClaudeCodeLanguageModel = class {
|
|
|
6927
7167
|
proxyOpencodeTools: this.config.proxyOpencodeTools
|
|
6928
7168
|
});
|
|
6929
7169
|
}
|
|
7170
|
+
/**
|
|
7171
|
+
* Arguments the skill bridge needs beyond `cwd` / `cliPath`: which
|
|
7172
|
+
* `CLAUDE_CONFIG_DIR` this spawn reads its native skills from, and whether
|
|
7173
|
+
* to drop the ones it already loads. A failover moves the spawn to another
|
|
7174
|
+
* account, and therefore to that account's config dir.
|
|
7175
|
+
*/
|
|
7176
|
+
skillBridgeSpawn(failover) {
|
|
7177
|
+
return {
|
|
7178
|
+
configDir: failover.failedOver && failover.target ? accountConfigDirPath(failover.target) : this.config.configDir,
|
|
7179
|
+
skipNative: this.config.bridgeSkipNativeSkills !== false
|
|
7180
|
+
};
|
|
7181
|
+
}
|
|
6930
7182
|
/** Share one lazy registry request within a turn without making it stale. */
|
|
6931
7183
|
createLiveToolInfoLoader() {
|
|
6932
7184
|
let pending;
|
|
@@ -7445,7 +7697,8 @@ var ClaudeCodeLanguageModel = class {
|
|
|
7445
7697
|
const skillPluginDirs = await resolveSkillPluginDirs({
|
|
7446
7698
|
cwd,
|
|
7447
7699
|
cliPath,
|
|
7448
|
-
enabled: this.config.bridgeOpencodeSkills === true
|
|
7700
|
+
enabled: this.config.bridgeOpencodeSkills === true,
|
|
7701
|
+
...this.skillBridgeSpawn(failover)
|
|
7449
7702
|
});
|
|
7450
7703
|
const cliArgs = buildCliArgs({
|
|
7451
7704
|
sessionKey: sk,
|
|
@@ -8175,7 +8428,8 @@ ${plan}
|
|
|
8175
8428
|
const skillPluginDirs = await resolveSkillPluginDirs({
|
|
8176
8429
|
cwd,
|
|
8177
8430
|
cliPath,
|
|
8178
|
-
enabled: self.config.bridgeOpencodeSkills === true
|
|
8431
|
+
enabled: self.config.bridgeOpencodeSkills === true,
|
|
8432
|
+
...self.skillBridgeSpawn(failover)
|
|
8179
8433
|
});
|
|
8180
8434
|
const ap = spawnInteractiveProcess({
|
|
8181
8435
|
cwd,
|
|
@@ -8331,7 +8585,8 @@ ${plan}
|
|
|
8331
8585
|
const skillPluginDirs = await resolveSkillPluginDirs({
|
|
8332
8586
|
cwd,
|
|
8333
8587
|
cliPath,
|
|
8334
|
-
enabled: self.config.bridgeOpencodeSkills === true
|
|
8588
|
+
enabled: self.config.bridgeOpencodeSkills === true,
|
|
8589
|
+
...self.skillBridgeSpawn(failover)
|
|
8335
8590
|
});
|
|
8336
8591
|
cliArgs = buildCliArgs({
|
|
8337
8592
|
sessionKey: sk,
|
|
@@ -8591,6 +8846,7 @@ ${plan}
|
|
|
8591
8846
|
let resultMeta = {};
|
|
8592
8847
|
let resultFailure;
|
|
8593
8848
|
let accountLimitHit = null;
|
|
8849
|
+
let accountBlock = null;
|
|
8594
8850
|
const drainBuffer = [];
|
|
8595
8851
|
let drainTimer = null;
|
|
8596
8852
|
const DRAIN_QUIET_MS = 100;
|
|
@@ -8752,20 +9008,21 @@ ${plan}
|
|
|
8752
9008
|
});
|
|
8753
9009
|
}
|
|
8754
9010
|
activeProcess?.pendingProxyCompletions?.clear();
|
|
8755
|
-
if (accountLimitHit && failoverAskActive && msg.is_error === true) {
|
|
9011
|
+
if ((accountLimitHit || accountBlock) && failoverAskActive && msg.is_error === true) {
|
|
8756
9012
|
const call = createAccountFailoverQuestionCall(sk, {
|
|
8757
9013
|
sourceAccount,
|
|
8758
9014
|
candidates: failoverAccounts,
|
|
8759
|
-
resetsAt: accountLimitHit
|
|
8760
|
-
window: accountLimitHit
|
|
9015
|
+
resetsAt: accountLimitHit?.resetsAt,
|
|
9016
|
+
window: accountLimitHit?.window,
|
|
9017
|
+
reason: accountLimitHit || !accountBlock ? void 0 : describeAccountBlock(accountBlock)
|
|
8761
9018
|
});
|
|
8762
9019
|
log.warn(
|
|
8763
|
-
`Claude account "${sourceAccount}" is out of usage; asking which account to continue on.`,
|
|
9020
|
+
`Claude account "${sourceAccount}" ${accountLimitHit || !accountBlock ? "is out of usage" : `cannot serve (${accountBlock})`}; asking which account to continue on.`,
|
|
8764
9021
|
{
|
|
8765
9022
|
sessionKey: sk,
|
|
8766
9023
|
candidates: failoverAccounts,
|
|
8767
9024
|
toolCallId: call.toolCallId,
|
|
8768
|
-
resetsAt: accountLimitHit
|
|
9025
|
+
resetsAt: accountLimitHit?.resetsAt ?? null
|
|
8769
9026
|
}
|
|
8770
9027
|
);
|
|
8771
9028
|
finishWithQuestionCall(call);
|
|
@@ -8903,6 +9160,20 @@ ${plan}
|
|
|
8903
9160
|
endTextBlock();
|
|
8904
9161
|
}
|
|
8905
9162
|
}
|
|
9163
|
+
if (msg.type === "conversation_reset") {
|
|
9164
|
+
const note = reportConversationReset(msg);
|
|
9165
|
+
if (note) {
|
|
9166
|
+
toolCallMap.clear();
|
|
9167
|
+
reasoningIds.clear();
|
|
9168
|
+
reasoningStarted.clear();
|
|
9169
|
+
textBlockIndices.clear();
|
|
9170
|
+
controller.enqueue({ type: "text-delta", id: startTextBlock(), delta: note });
|
|
9171
|
+
endTextBlock();
|
|
9172
|
+
}
|
|
9173
|
+
return;
|
|
9174
|
+
}
|
|
9175
|
+
const block = accountBlockKind(msg);
|
|
9176
|
+
if (block) accountBlock = block;
|
|
8906
9177
|
if (msg.type === "rate_limit_event") {
|
|
8907
9178
|
const info = parseRateLimitEvent(msg);
|
|
8908
9179
|
if (info && isRateLimitRejected(info)) {
|
|
@@ -8919,58 +9190,58 @@ ${plan}
|
|
|
8919
9190
|
return;
|
|
8920
9191
|
}
|
|
8921
9192
|
if (msg.type === "content_block_start" && msg.content_block && msg.index !== void 0) {
|
|
8922
|
-
const
|
|
9193
|
+
const block2 = msg.content_block;
|
|
8923
9194
|
const idx = msg.index;
|
|
8924
|
-
if (
|
|
9195
|
+
if (block2.type === "thinking") {
|
|
8925
9196
|
noteReasoning();
|
|
8926
9197
|
const reasoningId = generateId();
|
|
8927
9198
|
reasoningIds.set(idx, reasoningId);
|
|
8928
9199
|
}
|
|
8929
|
-
if (
|
|
9200
|
+
if (block2.type === "text") {
|
|
8930
9201
|
textBlockIndices.add(idx);
|
|
8931
9202
|
resetLastVisibleTextBlock();
|
|
8932
|
-
if (
|
|
9203
|
+
if (block2.text) {
|
|
8933
9204
|
if (!currentTextId) startTextBlock();
|
|
8934
9205
|
controller.enqueue({
|
|
8935
9206
|
type: "text-delta",
|
|
8936
9207
|
id: currentTextId,
|
|
8937
|
-
delta:
|
|
9208
|
+
delta: block2.text
|
|
8938
9209
|
});
|
|
8939
|
-
noteVisibleText(
|
|
9210
|
+
noteVisibleText(block2.text);
|
|
8940
9211
|
hasReceivedContent = true;
|
|
8941
9212
|
}
|
|
8942
9213
|
}
|
|
8943
|
-
if (
|
|
9214
|
+
if (block2.type === "tool_use" && block2.id && block2.name) {
|
|
8944
9215
|
noteToolActivity();
|
|
8945
9216
|
const entry = {
|
|
8946
|
-
id:
|
|
8947
|
-
name:
|
|
9217
|
+
id: block2.id,
|
|
9218
|
+
name: block2.name,
|
|
8948
9219
|
inputJson: "",
|
|
8949
9220
|
started: false
|
|
8950
9221
|
};
|
|
8951
9222
|
toolCallMap.set(idx, entry);
|
|
8952
|
-
if (
|
|
9223
|
+
if (block2.name !== "AskUserQuestion" && block2.name !== "ask_user_question" && block2.name !== "ExitPlanMode" && !block2.name.startsWith(PROXY_TOOL_PREFIX)) {
|
|
8953
9224
|
const { name: mappedName, skip, executed } = mapTool(
|
|
8954
|
-
|
|
9225
|
+
block2.name,
|
|
8955
9226
|
void 0,
|
|
8956
9227
|
{
|
|
8957
9228
|
webSearch: self.config.webSearch,
|
|
8958
9229
|
sessionId: getClaudeSessionId(sk),
|
|
8959
|
-
toolUseId:
|
|
9230
|
+
toolUseId: block2.id
|
|
8960
9231
|
}
|
|
8961
9232
|
);
|
|
8962
9233
|
if (!skip) {
|
|
8963
9234
|
entry.started = true;
|
|
8964
9235
|
controller.enqueue({
|
|
8965
9236
|
type: "tool-input-start",
|
|
8966
|
-
id:
|
|
9237
|
+
id: block2.id,
|
|
8967
9238
|
toolName: mappedName,
|
|
8968
9239
|
providerExecuted: executed
|
|
8969
9240
|
});
|
|
8970
9241
|
log.info("tool started", {
|
|
8971
|
-
name:
|
|
9242
|
+
name: block2.name,
|
|
8972
9243
|
mappedName,
|
|
8973
|
-
id:
|
|
9244
|
+
id: block2.id
|
|
8974
9245
|
});
|
|
8975
9246
|
}
|
|
8976
9247
|
}
|
|
@@ -9161,8 +9432,8 @@ ${plan}
|
|
|
9161
9432
|
hadStreamThinking: hadThinkingTextFromStream
|
|
9162
9433
|
});
|
|
9163
9434
|
if (!hadThinkingTextFromStream) {
|
|
9164
|
-
for (const
|
|
9165
|
-
if (
|
|
9435
|
+
for (const block2 of thinkingBlocks) {
|
|
9436
|
+
if (block2.thinking && block2.thinking.length > 0) {
|
|
9166
9437
|
noteReasoning();
|
|
9167
9438
|
hadThinkingTextFromStream = true;
|
|
9168
9439
|
const thinkingId = generateId();
|
|
@@ -9173,7 +9444,7 @@ ${plan}
|
|
|
9173
9444
|
controller.enqueue({
|
|
9174
9445
|
type: "reasoning-delta",
|
|
9175
9446
|
id: thinkingId,
|
|
9176
|
-
delta:
|
|
9447
|
+
delta: block2.thinking
|
|
9177
9448
|
});
|
|
9178
9449
|
controller.enqueue({
|
|
9179
9450
|
type: "reasoning-end",
|
|
@@ -9200,20 +9471,20 @@ ${plan}
|
|
|
9200
9471
|
if (hasToolUse) {
|
|
9201
9472
|
clearFallbackTimer();
|
|
9202
9473
|
}
|
|
9203
|
-
for (const
|
|
9204
|
-
if (
|
|
9474
|
+
for (const block2 of msg.message.content) {
|
|
9475
|
+
if (block2.type === "text" && block2.text) {
|
|
9205
9476
|
resetLastVisibleTextBlock();
|
|
9206
9477
|
const blockId = startTextBlock();
|
|
9207
9478
|
controller.enqueue({
|
|
9208
9479
|
type: "text-delta",
|
|
9209
9480
|
id: blockId,
|
|
9210
|
-
delta:
|
|
9481
|
+
delta: block2.text
|
|
9211
9482
|
});
|
|
9212
9483
|
endTextBlock();
|
|
9213
|
-
noteVisibleText(
|
|
9484
|
+
noteVisibleText(block2.text);
|
|
9214
9485
|
hasReceivedContent = true;
|
|
9215
9486
|
}
|
|
9216
|
-
if (
|
|
9487
|
+
if (block2.type === "thinking" && block2.thinking) {
|
|
9217
9488
|
noteReasoning();
|
|
9218
9489
|
const thinkingId = generateId();
|
|
9219
9490
|
controller.enqueue({
|
|
@@ -9223,17 +9494,17 @@ ${plan}
|
|
|
9223
9494
|
controller.enqueue({
|
|
9224
9495
|
type: "reasoning-delta",
|
|
9225
9496
|
id: thinkingId,
|
|
9226
|
-
delta:
|
|
9497
|
+
delta: block2.thinking
|
|
9227
9498
|
});
|
|
9228
9499
|
controller.enqueue({
|
|
9229
9500
|
type: "reasoning-end",
|
|
9230
9501
|
id: thinkingId
|
|
9231
9502
|
});
|
|
9232
9503
|
}
|
|
9233
|
-
if (
|
|
9504
|
+
if (block2.type === "tool_use" && block2.id && block2.name) {
|
|
9234
9505
|
noteToolActivity();
|
|
9235
|
-
const parsedInput =
|
|
9236
|
-
if (isAskUserQuestionTool(
|
|
9506
|
+
const parsedInput = block2.input ?? {};
|
|
9507
|
+
if (isAskUserQuestionTool(block2.name)) {
|
|
9237
9508
|
const askId = startTextBlock();
|
|
9238
9509
|
controller.enqueue({
|
|
9239
9510
|
type: "text-delta",
|
|
@@ -9241,12 +9512,12 @@ ${plan}
|
|
|
9241
9512
|
delta: formatAskUserQuestion(parsedInput)
|
|
9242
9513
|
});
|
|
9243
9514
|
endTextBlock();
|
|
9244
|
-
} else if (
|
|
9515
|
+
} else if (block2.name === "ExitPlanMode") {
|
|
9245
9516
|
const plan = parsedInput?.plan || "";
|
|
9246
9517
|
if (planModeQuestionActive) {
|
|
9247
9518
|
const questionCall = createExitPlanModeQuestionCall(
|
|
9248
9519
|
sk,
|
|
9249
|
-
|
|
9520
|
+
block2.id,
|
|
9250
9521
|
plan
|
|
9251
9522
|
);
|
|
9252
9523
|
const planId2 = startTextBlock();
|
|
@@ -9271,8 +9542,8 @@ ${plan}
|
|
|
9271
9542
|
`
|
|
9272
9543
|
});
|
|
9273
9544
|
endTextBlock();
|
|
9274
|
-
} else if (isWebSearchTool(
|
|
9275
|
-
toolCallsById.delete(
|
|
9545
|
+
} else if (isWebSearchTool(block2.name) && isWebSearchHandledByCli(self.config.webSearch)) {
|
|
9546
|
+
toolCallsById.delete(block2.id);
|
|
9276
9547
|
const query = typeof parsedInput?.query === "string" ? parsedInput.query : JSON.stringify(parsedInput);
|
|
9277
9548
|
const searchId = startTextBlock();
|
|
9278
9549
|
controller.enqueue({
|
|
@@ -9283,11 +9554,11 @@ ${plan}
|
|
|
9283
9554
|
`
|
|
9284
9555
|
});
|
|
9285
9556
|
endTextBlock();
|
|
9286
|
-
} else if (
|
|
9557
|
+
} else if (block2.name.startsWith(PROXY_TOOL_PREFIX)) {
|
|
9287
9558
|
noteProxyActivity();
|
|
9288
9559
|
log.debug("ignoring proxy tool_use from assistant message", {
|
|
9289
|
-
name:
|
|
9290
|
-
id:
|
|
9560
|
+
name: block2.name,
|
|
9561
|
+
id: block2.id
|
|
9291
9562
|
});
|
|
9292
9563
|
} else {
|
|
9293
9564
|
const {
|
|
@@ -9295,61 +9566,61 @@ ${plan}
|
|
|
9295
9566
|
input: mappedInput,
|
|
9296
9567
|
executed,
|
|
9297
9568
|
skip
|
|
9298
|
-
} = mapTool(
|
|
9569
|
+
} = mapTool(block2.name, parsedInput, {
|
|
9299
9570
|
webSearch: self.config.webSearch,
|
|
9300
9571
|
sessionId: getClaudeSessionId(sk),
|
|
9301
|
-
toolUseId:
|
|
9572
|
+
toolUseId: block2.id
|
|
9302
9573
|
});
|
|
9303
9574
|
if (!skip) {
|
|
9304
|
-
toolCallsById.set(
|
|
9305
|
-
id:
|
|
9306
|
-
name:
|
|
9575
|
+
toolCallsById.set(block2.id, {
|
|
9576
|
+
id: block2.id,
|
|
9577
|
+
name: block2.name,
|
|
9307
9578
|
input: parsedInput
|
|
9308
9579
|
});
|
|
9309
|
-
if (!executed) skipResultForIds.add(
|
|
9580
|
+
if (!executed) skipResultForIds.add(block2.id);
|
|
9310
9581
|
controller.enqueue({
|
|
9311
9582
|
type: "tool-input-start",
|
|
9312
|
-
id:
|
|
9583
|
+
id: block2.id,
|
|
9313
9584
|
toolName: mappedName,
|
|
9314
9585
|
providerExecuted: executed
|
|
9315
9586
|
});
|
|
9316
9587
|
controller.enqueue({
|
|
9317
9588
|
type: "tool-call",
|
|
9318
|
-
toolCallId:
|
|
9589
|
+
toolCallId: block2.id,
|
|
9319
9590
|
toolName: mappedName,
|
|
9320
9591
|
input: JSON.stringify(mappedInput),
|
|
9321
9592
|
providerExecuted: executed
|
|
9322
9593
|
});
|
|
9323
9594
|
}
|
|
9324
9595
|
log.info("tool_use from assistant message", {
|
|
9325
|
-
name:
|
|
9596
|
+
name: block2.name,
|
|
9326
9597
|
mappedName,
|
|
9327
|
-
id:
|
|
9598
|
+
id: block2.id,
|
|
9328
9599
|
executed
|
|
9329
9600
|
});
|
|
9330
9601
|
}
|
|
9331
9602
|
}
|
|
9332
|
-
if (
|
|
9603
|
+
if (block2.type === "tool_result") {
|
|
9333
9604
|
log.debug("tool_result", {
|
|
9334
|
-
toolUseId:
|
|
9605
|
+
toolUseId: block2.tool_use_id
|
|
9335
9606
|
});
|
|
9336
9607
|
}
|
|
9337
9608
|
}
|
|
9338
9609
|
}
|
|
9339
9610
|
if (msg.type === "user" && msg.message?.content) {
|
|
9340
|
-
for (const
|
|
9341
|
-
if (
|
|
9342
|
-
if (skipResultForIds.has(
|
|
9611
|
+
for (const block2 of msg.message.content) {
|
|
9612
|
+
if (block2.type === "tool_result" && block2.tool_use_id) {
|
|
9613
|
+
if (skipResultForIds.has(block2.tool_use_id)) {
|
|
9343
9614
|
log.debug("skipping tool-result (opencode runs it)", {
|
|
9344
|
-
toolUseId:
|
|
9615
|
+
toolUseId: block2.tool_use_id
|
|
9345
9616
|
});
|
|
9346
9617
|
continue;
|
|
9347
9618
|
}
|
|
9348
9619
|
let resultText = "";
|
|
9349
|
-
if (typeof
|
|
9350
|
-
resultText =
|
|
9351
|
-
} else if (Array.isArray(
|
|
9352
|
-
resultText =
|
|
9620
|
+
if (typeof block2.content === "string") {
|
|
9621
|
+
resultText = block2.content;
|
|
9622
|
+
} else if (Array.isArray(block2.content)) {
|
|
9623
|
+
resultText = block2.content.filter(
|
|
9353
9624
|
(c) => c.type === "text" && typeof c.text === "string"
|
|
9354
9625
|
).map((c) => c.text).join("\n");
|
|
9355
9626
|
}
|
|
@@ -9357,11 +9628,11 @@ ${plan}
|
|
|
9357
9628
|
if (claudeSessionId) {
|
|
9358
9629
|
const list2 = applyTaskCreateToolResult(
|
|
9359
9630
|
claudeSessionId,
|
|
9360
|
-
|
|
9631
|
+
block2.tool_use_id,
|
|
9361
9632
|
resultText
|
|
9362
9633
|
);
|
|
9363
9634
|
if (list2) {
|
|
9364
|
-
const synthId = `todowrite_${
|
|
9635
|
+
const synthId = `todowrite_${block2.tool_use_id}`;
|
|
9365
9636
|
controller.enqueue({
|
|
9366
9637
|
type: "tool-input-start",
|
|
9367
9638
|
id: synthId,
|
|
@@ -9385,12 +9656,12 @@ ${plan}
|
|
|
9385
9656
|
noteToolActivity();
|
|
9386
9657
|
}
|
|
9387
9658
|
}
|
|
9388
|
-
const toolCall = toolCallsById.get(
|
|
9659
|
+
const toolCall = toolCallsById.get(block2.tool_use_id);
|
|
9389
9660
|
if (toolCall) {
|
|
9390
|
-
const isError =
|
|
9661
|
+
const isError = block2.is_error === true;
|
|
9391
9662
|
controller.enqueue({
|
|
9392
9663
|
type: "tool-result",
|
|
9393
|
-
toolCallId:
|
|
9664
|
+
toolCallId: block2.tool_use_id,
|
|
9394
9665
|
toolName: toolCall.name,
|
|
9395
9666
|
result: {
|
|
9396
9667
|
output: resultText,
|
|
@@ -9402,11 +9673,11 @@ ${plan}
|
|
|
9402
9673
|
});
|
|
9403
9674
|
noteToolActivity();
|
|
9404
9675
|
log.info("tool result emitted", {
|
|
9405
|
-
toolUseId:
|
|
9676
|
+
toolUseId: block2.tool_use_id,
|
|
9406
9677
|
name: toolCall.name,
|
|
9407
9678
|
isError
|
|
9408
9679
|
});
|
|
9409
|
-
toolCallsById.delete(
|
|
9680
|
+
toolCallsById.delete(block2.tool_use_id);
|
|
9410
9681
|
}
|
|
9411
9682
|
}
|
|
9412
9683
|
}
|
|
@@ -9432,6 +9703,26 @@ ${plan}
|
|
|
9432
9703
|
})) {
|
|
9433
9704
|
accountLimitHit = {};
|
|
9434
9705
|
}
|
|
9706
|
+
if (accountBlock && msg.is_error) {
|
|
9707
|
+
resultFailure ??= accountBlock;
|
|
9708
|
+
const offeringSwitch = failoverAskActive;
|
|
9709
|
+
controller.enqueue({
|
|
9710
|
+
type: "text-delta",
|
|
9711
|
+
id: startTextBlock(),
|
|
9712
|
+
delta: formatAccountBlockNote({
|
|
9713
|
+
kind: accountBlock,
|
|
9714
|
+
account: sourceAccount,
|
|
9715
|
+
configDir: self.config.configDir,
|
|
9716
|
+
offeringSwitch
|
|
9717
|
+
})
|
|
9718
|
+
});
|
|
9719
|
+
endTextBlock();
|
|
9720
|
+
log.warn(`Claude account "${sourceAccount}" cannot serve requests`, {
|
|
9721
|
+
sessionKey: sk,
|
|
9722
|
+
kind: accountBlock,
|
|
9723
|
+
offeringSwitch
|
|
9724
|
+
});
|
|
9725
|
+
}
|
|
9435
9726
|
const failure = describeResultFailure(msg);
|
|
9436
9727
|
if (failure) {
|
|
9437
9728
|
resultFailure = msg.subtype;
|
|
@@ -9873,11 +10164,11 @@ ${plan}
|
|
|
9873
10164
|
import {
|
|
9874
10165
|
existsSync as existsSync4,
|
|
9875
10166
|
readFileSync as readFileSync6,
|
|
9876
|
-
realpathSync,
|
|
10167
|
+
realpathSync as realpathSync2,
|
|
9877
10168
|
rmSync as rmSync3,
|
|
9878
10169
|
writeFileSync as writeFileSync5
|
|
9879
10170
|
} from "fs";
|
|
9880
|
-
import { homedir as
|
|
10171
|
+
import { homedir as homedir7 } from "os";
|
|
9881
10172
|
import { join as join9, resolve as resolve4 } from "path";
|
|
9882
10173
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
9883
10174
|
var STALE_PACKAGE_NAME = "opencode-claude-code-plugin";
|
|
@@ -9887,12 +10178,12 @@ function candidateCacheRoots() {
|
|
|
9887
10178
|
const xdg = process.env.XDG_CACHE_HOME;
|
|
9888
10179
|
return [
|
|
9889
10180
|
xdg ? join9(xdg, "opencode") : null,
|
|
9890
|
-
join9(
|
|
9891
|
-
join9(
|
|
10181
|
+
join9(homedir7(), ".cache", "opencode"),
|
|
10182
|
+
join9(homedir7(), "Library", "Caches", "opencode")
|
|
9892
10183
|
].filter((p) => Boolean(p));
|
|
9893
10184
|
}
|
|
9894
10185
|
function userOpencodeJsonPath() {
|
|
9895
|
-
const xdgConfig = process.env.XDG_CONFIG_HOME ?? join9(
|
|
10186
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME ?? join9(homedir7(), ".config");
|
|
9896
10187
|
return join9(xdgConfig, "opencode", "opencode.json");
|
|
9897
10188
|
}
|
|
9898
10189
|
function userIntendsToUseUnscoped() {
|
|
@@ -9912,7 +10203,7 @@ function userIntendsToUseUnscoped() {
|
|
|
9912
10203
|
function ourLoadedDir() {
|
|
9913
10204
|
try {
|
|
9914
10205
|
const filePath = fileURLToPath3(import.meta.url);
|
|
9915
|
-
return
|
|
10206
|
+
return realpathSync2(resolve4(filePath, "..", ".."));
|
|
9916
10207
|
} catch {
|
|
9917
10208
|
return null;
|
|
9918
10209
|
}
|
|
@@ -9940,7 +10231,7 @@ function cleanupOne(cacheRoot, ourDir) {
|
|
|
9940
10231
|
if (!existsSync4(stalePath)) return;
|
|
9941
10232
|
let realStalePath = stalePath;
|
|
9942
10233
|
try {
|
|
9943
|
-
realStalePath =
|
|
10234
|
+
realStalePath = realpathSync2(stalePath);
|
|
9944
10235
|
} catch {
|
|
9945
10236
|
}
|
|
9946
10237
|
if (ourDir && realStalePath === ourDir) return;
|
|
@@ -10499,6 +10790,7 @@ function createClaudeCode(settings = {}) {
|
|
|
10499
10790
|
ignoreAnthropicApiKey: settings.ignoreAnthropicApiKey,
|
|
10500
10791
|
idleProcessTimeoutMs: settings.idleProcessTimeoutMs,
|
|
10501
10792
|
bridgeOpencodeSkills: settings.bridgeOpencodeSkills === true,
|
|
10793
|
+
bridgeSkipNativeSkills: settings.bridgeSkipNativeSkills !== false,
|
|
10502
10794
|
turnStats: settings.turnStats === true,
|
|
10503
10795
|
interactive: settings.interactive,
|
|
10504
10796
|
interactiveBypass: settings.interactiveBypass,
|