@khalilgharbaoui/opencode-claude-code-plugin 0.4.5 → 0.4.7
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 +87 -26
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2,7 +2,33 @@
|
|
|
2
2
|
import { generateId } from "@ai-sdk/provider-utils";
|
|
3
3
|
|
|
4
4
|
// src/logger.ts
|
|
5
|
+
import { appendFileSync, mkdirSync, renameSync, statSync } from "fs";
|
|
6
|
+
import { homedir } from "os";
|
|
7
|
+
import { dirname, join } from "path";
|
|
5
8
|
var DEBUG = process.env.DEBUG?.includes("opencode-claude-code") ?? false;
|
|
9
|
+
var LOG_DIR = process.env.OPENCODE_CLAUDE_CODE_LOG_DIR ?? join(homedir(), ".local", "share", "opencode-claude-code");
|
|
10
|
+
var LOG_FILE = join(LOG_DIR, "plugin.log");
|
|
11
|
+
var MAX_LOG_BYTES = 5 * 1024 * 1024;
|
|
12
|
+
var fileLoggingDisabled = false;
|
|
13
|
+
function rotateIfNeeded() {
|
|
14
|
+
try {
|
|
15
|
+
const stat = statSync(LOG_FILE);
|
|
16
|
+
if (stat.size > MAX_LOG_BYTES) {
|
|
17
|
+
renameSync(LOG_FILE, `${LOG_FILE}.1`);
|
|
18
|
+
}
|
|
19
|
+
} catch {
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function writeToFile(line) {
|
|
23
|
+
if (fileLoggingDisabled) return;
|
|
24
|
+
try {
|
|
25
|
+
mkdirSync(dirname(LOG_FILE), { recursive: true });
|
|
26
|
+
rotateIfNeeded();
|
|
27
|
+
appendFileSync(LOG_FILE, line + "\n", "utf8");
|
|
28
|
+
} catch {
|
|
29
|
+
fileLoggingDisabled = true;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
6
32
|
function fmt(level, msg, data) {
|
|
7
33
|
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
8
34
|
const base = `[${ts}] [opencode-claude-code] ${level}: ${msg}`;
|
|
@@ -11,21 +37,30 @@ function fmt(level, msg, data) {
|
|
|
11
37
|
}
|
|
12
38
|
return base;
|
|
13
39
|
}
|
|
40
|
+
function emit(level, msg, data, alwaysStderr = false) {
|
|
41
|
+
const line = fmt(level, msg, data);
|
|
42
|
+
if (alwaysStderr || DEBUG) {
|
|
43
|
+
console.error(line);
|
|
44
|
+
}
|
|
45
|
+
writeToFile(line);
|
|
46
|
+
}
|
|
14
47
|
var log = {
|
|
15
48
|
info(msg, data) {
|
|
16
|
-
if (DEBUG)
|
|
49
|
+
if (DEBUG) emit("INFO", msg, data);
|
|
50
|
+
else writeToFile(fmt("INFO", msg, data));
|
|
17
51
|
},
|
|
18
52
|
notice(msg, data) {
|
|
19
|
-
|
|
53
|
+
emit("NOTICE", msg, data, true);
|
|
20
54
|
},
|
|
21
55
|
warn(msg, data) {
|
|
22
|
-
|
|
56
|
+
emit("WARN", msg, data, true);
|
|
23
57
|
},
|
|
24
58
|
error(msg, data) {
|
|
25
|
-
|
|
59
|
+
emit("ERROR", msg, data, true);
|
|
26
60
|
},
|
|
27
61
|
debug(msg, data) {
|
|
28
|
-
if (DEBUG)
|
|
62
|
+
if (DEBUG) emit("DEBUG", msg, data);
|
|
63
|
+
else writeToFile(fmt("DEBUG", msg, data));
|
|
29
64
|
}
|
|
30
65
|
};
|
|
31
66
|
|
|
@@ -1460,13 +1495,22 @@ function rejectAllPendingProxyCallsForSession(sessionKey2, error) {
|
|
|
1460
1495
|
// src/claude-code-language-model.ts
|
|
1461
1496
|
import { readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
1462
1497
|
import { unlink as unlink2 } from "fs/promises";
|
|
1463
|
-
import { homedir as
|
|
1498
|
+
import { homedir as homedir3, tmpdir as tmpdir2 } from "os";
|
|
1464
1499
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
1465
|
-
import { dirname as
|
|
1500
|
+
import { dirname as dirname3, join as join5 } from "path";
|
|
1466
1501
|
function hasNewUserContent(prompt) {
|
|
1467
1502
|
for (let i = prompt.length - 1; i >= 0; i--) {
|
|
1468
1503
|
const msg = prompt[i];
|
|
1469
1504
|
if (msg.role === "assistant") return false;
|
|
1505
|
+
if (msg.role === "tool") {
|
|
1506
|
+
const content2 = msg.content;
|
|
1507
|
+
if (Array.isArray(content2)) {
|
|
1508
|
+
for (const part of content2) {
|
|
1509
|
+
if (part?.type === "tool-result") return true;
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
continue;
|
|
1513
|
+
}
|
|
1470
1514
|
if (msg.role !== "user") continue;
|
|
1471
1515
|
const content = msg.content;
|
|
1472
1516
|
if (typeof content === "string") {
|
|
@@ -1528,9 +1572,10 @@ function shouldAutoContinueIncompleteTurn(state, snapshot) {
|
|
|
1528
1572
|
return { continue: false, reason: "max-elapsed" };
|
|
1529
1573
|
}
|
|
1530
1574
|
const text = normalizeVisibleText(snapshot.text);
|
|
1575
|
+
const lastText = normalizeVisibleText(snapshot.lastVisibleText);
|
|
1531
1576
|
if (looksLikeQuestion(text)) return { continue: false, reason: "question" };
|
|
1532
1577
|
if (looksLikeBlocker(text)) return { continue: false, reason: "blocker" };
|
|
1533
|
-
if (looksLikeFinalAnswer(
|
|
1578
|
+
if (looksLikeFinalAnswer(lastText)) {
|
|
1534
1579
|
return { continue: false, reason: "final-answer" };
|
|
1535
1580
|
}
|
|
1536
1581
|
const hadActivity = snapshot.hadReasoning || snapshot.hadToolActivity || snapshot.hadProxyActivity;
|
|
@@ -1565,9 +1610,9 @@ function readPromptFileIfPresent(path5) {
|
|
|
1565
1610
|
function nearestWorkspaceAgentsPrompt(cwd) {
|
|
1566
1611
|
let dir = cwd;
|
|
1567
1612
|
while (true) {
|
|
1568
|
-
const content = readPromptFileIfPresent(
|
|
1613
|
+
const content = readPromptFileIfPresent(join5(dir, "AGENTS.md"));
|
|
1569
1614
|
if (content) return content;
|
|
1570
|
-
const parent =
|
|
1615
|
+
const parent = dirname3(dir);
|
|
1571
1616
|
if (parent === dir) return void 0;
|
|
1572
1617
|
dir = parent;
|
|
1573
1618
|
}
|
|
@@ -1582,15 +1627,15 @@ blocker. The user can interrupt or abort at any time; turn endings should
|
|
|
1582
1627
|
mark meaningful checkpoints, not every completed substep.`;
|
|
1583
1628
|
function buildAppendedSystemPrompt(cwd, includeMultiStepHint = true) {
|
|
1584
1629
|
const parts = [];
|
|
1585
|
-
const configRoot = process.env.XDG_CONFIG_HOME ??
|
|
1586
|
-
const globalAgents = readPromptFileIfPresent(
|
|
1630
|
+
const configRoot = process.env.XDG_CONFIG_HOME ?? join5(homedir3(), ".config");
|
|
1631
|
+
const globalAgents = readPromptFileIfPresent(join5(configRoot, "opencode", "AGENTS.md"));
|
|
1587
1632
|
const workspaceAgents = nearestWorkspaceAgentsPrompt(cwd);
|
|
1588
1633
|
if (globalAgents) parts.push(globalAgents);
|
|
1589
1634
|
if (workspaceAgents && workspaceAgents !== globalAgents) parts.push(workspaceAgents);
|
|
1590
1635
|
if (includeMultiStepHint) parts.push(MULTI_STEP_TASK_HINT);
|
|
1591
1636
|
const content = parts.join("\n\n");
|
|
1592
1637
|
if (!content) return void 0;
|
|
1593
|
-
const path5 =
|
|
1638
|
+
const path5 = join5(tmpdir2(), `opencode-cc-sys-${randomUUID2()}.md`);
|
|
1594
1639
|
try {
|
|
1595
1640
|
writeFileSync3(path5, content, "utf8");
|
|
1596
1641
|
return path5;
|
|
@@ -2492,6 +2537,7 @@ ${plan}
|
|
|
2492
2537
|
let resultFallbackTimer = null;
|
|
2493
2538
|
let hasReceivedContent = false;
|
|
2494
2539
|
let visibleTextSinceContinue = "";
|
|
2540
|
+
let lastVisibleTextSinceContinue = "";
|
|
2495
2541
|
let hadReasoningSinceContinue = false;
|
|
2496
2542
|
let hadToolActivitySinceContinue = false;
|
|
2497
2543
|
let hadProxyActivitySinceContinue = false;
|
|
@@ -2575,6 +2621,10 @@ ${plan}
|
|
|
2575
2621
|
};
|
|
2576
2622
|
const noteVisibleText = (text) => {
|
|
2577
2623
|
visibleTextSinceContinue += text;
|
|
2624
|
+
lastVisibleTextSinceContinue += text;
|
|
2625
|
+
};
|
|
2626
|
+
const resetLastVisibleTextBlock = () => {
|
|
2627
|
+
lastVisibleTextSinceContinue = "";
|
|
2578
2628
|
};
|
|
2579
2629
|
const noteReasoning = () => {
|
|
2580
2630
|
hadReasoningSinceContinue = true;
|
|
@@ -2587,6 +2637,7 @@ ${plan}
|
|
|
2587
2637
|
};
|
|
2588
2638
|
const resetAutoContinueWindow = () => {
|
|
2589
2639
|
visibleTextSinceContinue = "";
|
|
2640
|
+
lastVisibleTextSinceContinue = "";
|
|
2590
2641
|
hadReasoningSinceContinue = false;
|
|
2591
2642
|
hadToolActivitySinceContinue = false;
|
|
2592
2643
|
hadProxyActivitySinceContinue = false;
|
|
@@ -2632,6 +2683,7 @@ ${plan}
|
|
|
2632
2683
|
}
|
|
2633
2684
|
if (block.type === "text") {
|
|
2634
2685
|
textBlockIndices.add(idx);
|
|
2686
|
+
resetLastVisibleTextBlock();
|
|
2635
2687
|
if (block.text) {
|
|
2636
2688
|
if (!currentTextId) startTextBlock();
|
|
2637
2689
|
controller.enqueue({
|
|
@@ -2816,6 +2868,7 @@ ${plan}
|
|
|
2816
2868
|
}
|
|
2817
2869
|
for (const block of msg.message.content) {
|
|
2818
2870
|
if (block.type === "text" && block.text) {
|
|
2871
|
+
resetLastVisibleTextBlock();
|
|
2819
2872
|
const blockId = startTextBlock();
|
|
2820
2873
|
controller.enqueue({
|
|
2821
2874
|
type: "text-delta",
|
|
@@ -3026,6 +3079,7 @@ ${plan}
|
|
|
3026
3079
|
autoContinueState,
|
|
3027
3080
|
{
|
|
3028
3081
|
text: visibleTextSinceContinue,
|
|
3082
|
+
lastVisibleText: lastVisibleTextSinceContinue,
|
|
3029
3083
|
hadReasoning: hadReasoningSinceContinue,
|
|
3030
3084
|
hadToolActivity: hadToolActivitySinceContinue,
|
|
3031
3085
|
hadProxyActivity: hadProxyActivitySinceContinue,
|
|
@@ -3035,6 +3089,7 @@ ${plan}
|
|
|
3035
3089
|
if (autoDecision.continue) {
|
|
3036
3090
|
const signature = continuationSignature({
|
|
3037
3091
|
text: visibleTextSinceContinue,
|
|
3092
|
+
lastVisibleText: lastVisibleTextSinceContinue,
|
|
3038
3093
|
hadReasoning: hadReasoningSinceContinue,
|
|
3039
3094
|
hadToolActivity: hadToolActivitySinceContinue,
|
|
3040
3095
|
hadProxyActivity: hadProxyActivitySinceContinue,
|
|
@@ -3043,11 +3098,12 @@ ${plan}
|
|
|
3043
3098
|
autoContinueState.noProgressCount = signature === autoContinueState.lastSignature ? autoContinueState.noProgressCount + 1 : 0;
|
|
3044
3099
|
autoContinueState.lastSignature = signature;
|
|
3045
3100
|
autoContinueState.attempts++;
|
|
3046
|
-
log.
|
|
3101
|
+
log.notice("auto-continuing incomplete claude result", {
|
|
3047
3102
|
sessionKey: sk,
|
|
3048
3103
|
reason: autoDecision.reason,
|
|
3049
3104
|
attempts: autoContinueState.attempts,
|
|
3050
3105
|
textLength: visibleTextSinceContinue.length,
|
|
3106
|
+
lastTextLength: lastVisibleTextSinceContinue.length,
|
|
3051
3107
|
hadReasoning: hadReasoningSinceContinue,
|
|
3052
3108
|
hadToolActivity: hadToolActivitySinceContinue,
|
|
3053
3109
|
hadProxyActivity: hadProxyActivitySinceContinue
|
|
@@ -3057,10 +3113,15 @@ ${plan}
|
|
|
3057
3113
|
proc.stdin?.write(makeAutoContinueMessage() + "\n");
|
|
3058
3114
|
return;
|
|
3059
3115
|
}
|
|
3060
|
-
log.
|
|
3116
|
+
log.notice("auto-continuation stopped", {
|
|
3061
3117
|
sessionKey: sk,
|
|
3062
3118
|
reason: autoDecision.reason,
|
|
3063
|
-
attempts: autoContinueState.attempts
|
|
3119
|
+
attempts: autoContinueState.attempts,
|
|
3120
|
+
textLength: visibleTextSinceContinue.length,
|
|
3121
|
+
lastTextLength: lastVisibleTextSinceContinue.length,
|
|
3122
|
+
hadReasoning: hadReasoningSinceContinue,
|
|
3123
|
+
hadToolActivity: hadToolActivitySinceContinue,
|
|
3124
|
+
hadProxyActivity: hadProxyActivitySinceContinue
|
|
3064
3125
|
});
|
|
3065
3126
|
for (const [idx, reasoningId] of reasoningIds) {
|
|
3066
3127
|
if (reasoningStarted.get(idx)) {
|
|
@@ -3568,8 +3629,8 @@ import {
|
|
|
3568
3629
|
rmSync as rmSync2,
|
|
3569
3630
|
writeFileSync as writeFileSync4
|
|
3570
3631
|
} from "fs";
|
|
3571
|
-
import { homedir as
|
|
3572
|
-
import { join as
|
|
3632
|
+
import { homedir as homedir4 } from "os";
|
|
3633
|
+
import { join as join6, resolve as resolve2 } from "path";
|
|
3573
3634
|
import { fileURLToPath } from "url";
|
|
3574
3635
|
var STALE_PACKAGE_NAME = "opencode-claude-code-plugin";
|
|
3575
3636
|
var SUSPECT_DESCRIPTION_TOKEN = "Claude Code";
|
|
@@ -3577,14 +3638,14 @@ var alreadyRan = false;
|
|
|
3577
3638
|
function candidateCacheRoots() {
|
|
3578
3639
|
const xdg = process.env.XDG_CACHE_HOME;
|
|
3579
3640
|
return [
|
|
3580
|
-
xdg ?
|
|
3581
|
-
|
|
3582
|
-
|
|
3641
|
+
xdg ? join6(xdg, "opencode") : null,
|
|
3642
|
+
join6(homedir4(), ".cache", "opencode"),
|
|
3643
|
+
join6(homedir4(), "Library", "Caches", "opencode")
|
|
3583
3644
|
].filter((p) => Boolean(p));
|
|
3584
3645
|
}
|
|
3585
3646
|
function userOpencodeJsonPath() {
|
|
3586
|
-
const xdgConfig = process.env.XDG_CONFIG_HOME ??
|
|
3587
|
-
return
|
|
3647
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME ?? join6(homedir4(), ".config");
|
|
3648
|
+
return join6(xdgConfig, "opencode", "opencode.json");
|
|
3588
3649
|
}
|
|
3589
3650
|
function userIntendsToUseUnscoped() {
|
|
3590
3651
|
const cfg = userOpencodeJsonPath();
|
|
@@ -3627,7 +3688,7 @@ function cleanupStaleUnscopedInstall() {
|
|
|
3627
3688
|
}
|
|
3628
3689
|
function cleanupOne(cacheRoot, ourDir) {
|
|
3629
3690
|
if (!existsSync3(cacheRoot)) return;
|
|
3630
|
-
const stalePath =
|
|
3691
|
+
const stalePath = join6(cacheRoot, "node_modules", STALE_PACKAGE_NAME);
|
|
3631
3692
|
if (!existsSync3(stalePath)) return;
|
|
3632
3693
|
let realStalePath = stalePath;
|
|
3633
3694
|
try {
|
|
@@ -3635,7 +3696,7 @@ function cleanupOne(cacheRoot, ourDir) {
|
|
|
3635
3696
|
} catch {
|
|
3636
3697
|
}
|
|
3637
3698
|
if (ourDir && realStalePath === ourDir) return;
|
|
3638
|
-
const pkgJsonPath =
|
|
3699
|
+
const pkgJsonPath = join6(stalePath, "package.json");
|
|
3639
3700
|
if (!existsSync3(pkgJsonPath)) return;
|
|
3640
3701
|
let pkg = {};
|
|
3641
3702
|
try {
|
|
@@ -3655,7 +3716,7 @@ function cleanupOne(cacheRoot, ourDir) {
|
|
|
3655
3716
|
});
|
|
3656
3717
|
return;
|
|
3657
3718
|
}
|
|
3658
|
-
const cachePkgJson =
|
|
3719
|
+
const cachePkgJson = join6(cacheRoot, "package.json");
|
|
3659
3720
|
if (!existsSync3(cachePkgJson)) return;
|
|
3660
3721
|
try {
|
|
3661
3722
|
const cfg = JSON.parse(readFileSync3(cachePkgJson, "utf8"));
|