@khalilgharbaoui/opencode-claude-code-plugin 0.4.5 → 0.4.6
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 +78 -26
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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,9 +1495,9 @@ 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];
|
|
@@ -1528,9 +1563,10 @@ function shouldAutoContinueIncompleteTurn(state, snapshot) {
|
|
|
1528
1563
|
return { continue: false, reason: "max-elapsed" };
|
|
1529
1564
|
}
|
|
1530
1565
|
const text = normalizeVisibleText(snapshot.text);
|
|
1566
|
+
const lastText = normalizeVisibleText(snapshot.lastVisibleText);
|
|
1531
1567
|
if (looksLikeQuestion(text)) return { continue: false, reason: "question" };
|
|
1532
1568
|
if (looksLikeBlocker(text)) return { continue: false, reason: "blocker" };
|
|
1533
|
-
if (looksLikeFinalAnswer(
|
|
1569
|
+
if (looksLikeFinalAnswer(lastText)) {
|
|
1534
1570
|
return { continue: false, reason: "final-answer" };
|
|
1535
1571
|
}
|
|
1536
1572
|
const hadActivity = snapshot.hadReasoning || snapshot.hadToolActivity || snapshot.hadProxyActivity;
|
|
@@ -1565,9 +1601,9 @@ function readPromptFileIfPresent(path5) {
|
|
|
1565
1601
|
function nearestWorkspaceAgentsPrompt(cwd) {
|
|
1566
1602
|
let dir = cwd;
|
|
1567
1603
|
while (true) {
|
|
1568
|
-
const content = readPromptFileIfPresent(
|
|
1604
|
+
const content = readPromptFileIfPresent(join5(dir, "AGENTS.md"));
|
|
1569
1605
|
if (content) return content;
|
|
1570
|
-
const parent =
|
|
1606
|
+
const parent = dirname3(dir);
|
|
1571
1607
|
if (parent === dir) return void 0;
|
|
1572
1608
|
dir = parent;
|
|
1573
1609
|
}
|
|
@@ -1582,15 +1618,15 @@ blocker. The user can interrupt or abort at any time; turn endings should
|
|
|
1582
1618
|
mark meaningful checkpoints, not every completed substep.`;
|
|
1583
1619
|
function buildAppendedSystemPrompt(cwd, includeMultiStepHint = true) {
|
|
1584
1620
|
const parts = [];
|
|
1585
|
-
const configRoot = process.env.XDG_CONFIG_HOME ??
|
|
1586
|
-
const globalAgents = readPromptFileIfPresent(
|
|
1621
|
+
const configRoot = process.env.XDG_CONFIG_HOME ?? join5(homedir3(), ".config");
|
|
1622
|
+
const globalAgents = readPromptFileIfPresent(join5(configRoot, "opencode", "AGENTS.md"));
|
|
1587
1623
|
const workspaceAgents = nearestWorkspaceAgentsPrompt(cwd);
|
|
1588
1624
|
if (globalAgents) parts.push(globalAgents);
|
|
1589
1625
|
if (workspaceAgents && workspaceAgents !== globalAgents) parts.push(workspaceAgents);
|
|
1590
1626
|
if (includeMultiStepHint) parts.push(MULTI_STEP_TASK_HINT);
|
|
1591
1627
|
const content = parts.join("\n\n");
|
|
1592
1628
|
if (!content) return void 0;
|
|
1593
|
-
const path5 =
|
|
1629
|
+
const path5 = join5(tmpdir2(), `opencode-cc-sys-${randomUUID2()}.md`);
|
|
1594
1630
|
try {
|
|
1595
1631
|
writeFileSync3(path5, content, "utf8");
|
|
1596
1632
|
return path5;
|
|
@@ -2492,6 +2528,7 @@ ${plan}
|
|
|
2492
2528
|
let resultFallbackTimer = null;
|
|
2493
2529
|
let hasReceivedContent = false;
|
|
2494
2530
|
let visibleTextSinceContinue = "";
|
|
2531
|
+
let lastVisibleTextSinceContinue = "";
|
|
2495
2532
|
let hadReasoningSinceContinue = false;
|
|
2496
2533
|
let hadToolActivitySinceContinue = false;
|
|
2497
2534
|
let hadProxyActivitySinceContinue = false;
|
|
@@ -2575,6 +2612,10 @@ ${plan}
|
|
|
2575
2612
|
};
|
|
2576
2613
|
const noteVisibleText = (text) => {
|
|
2577
2614
|
visibleTextSinceContinue += text;
|
|
2615
|
+
lastVisibleTextSinceContinue += text;
|
|
2616
|
+
};
|
|
2617
|
+
const resetLastVisibleTextBlock = () => {
|
|
2618
|
+
lastVisibleTextSinceContinue = "";
|
|
2578
2619
|
};
|
|
2579
2620
|
const noteReasoning = () => {
|
|
2580
2621
|
hadReasoningSinceContinue = true;
|
|
@@ -2587,6 +2628,7 @@ ${plan}
|
|
|
2587
2628
|
};
|
|
2588
2629
|
const resetAutoContinueWindow = () => {
|
|
2589
2630
|
visibleTextSinceContinue = "";
|
|
2631
|
+
lastVisibleTextSinceContinue = "";
|
|
2590
2632
|
hadReasoningSinceContinue = false;
|
|
2591
2633
|
hadToolActivitySinceContinue = false;
|
|
2592
2634
|
hadProxyActivitySinceContinue = false;
|
|
@@ -2632,6 +2674,7 @@ ${plan}
|
|
|
2632
2674
|
}
|
|
2633
2675
|
if (block.type === "text") {
|
|
2634
2676
|
textBlockIndices.add(idx);
|
|
2677
|
+
resetLastVisibleTextBlock();
|
|
2635
2678
|
if (block.text) {
|
|
2636
2679
|
if (!currentTextId) startTextBlock();
|
|
2637
2680
|
controller.enqueue({
|
|
@@ -2816,6 +2859,7 @@ ${plan}
|
|
|
2816
2859
|
}
|
|
2817
2860
|
for (const block of msg.message.content) {
|
|
2818
2861
|
if (block.type === "text" && block.text) {
|
|
2862
|
+
resetLastVisibleTextBlock();
|
|
2819
2863
|
const blockId = startTextBlock();
|
|
2820
2864
|
controller.enqueue({
|
|
2821
2865
|
type: "text-delta",
|
|
@@ -3026,6 +3070,7 @@ ${plan}
|
|
|
3026
3070
|
autoContinueState,
|
|
3027
3071
|
{
|
|
3028
3072
|
text: visibleTextSinceContinue,
|
|
3073
|
+
lastVisibleText: lastVisibleTextSinceContinue,
|
|
3029
3074
|
hadReasoning: hadReasoningSinceContinue,
|
|
3030
3075
|
hadToolActivity: hadToolActivitySinceContinue,
|
|
3031
3076
|
hadProxyActivity: hadProxyActivitySinceContinue,
|
|
@@ -3035,6 +3080,7 @@ ${plan}
|
|
|
3035
3080
|
if (autoDecision.continue) {
|
|
3036
3081
|
const signature = continuationSignature({
|
|
3037
3082
|
text: visibleTextSinceContinue,
|
|
3083
|
+
lastVisibleText: lastVisibleTextSinceContinue,
|
|
3038
3084
|
hadReasoning: hadReasoningSinceContinue,
|
|
3039
3085
|
hadToolActivity: hadToolActivitySinceContinue,
|
|
3040
3086
|
hadProxyActivity: hadProxyActivitySinceContinue,
|
|
@@ -3043,11 +3089,12 @@ ${plan}
|
|
|
3043
3089
|
autoContinueState.noProgressCount = signature === autoContinueState.lastSignature ? autoContinueState.noProgressCount + 1 : 0;
|
|
3044
3090
|
autoContinueState.lastSignature = signature;
|
|
3045
3091
|
autoContinueState.attempts++;
|
|
3046
|
-
log.
|
|
3092
|
+
log.notice("auto-continuing incomplete claude result", {
|
|
3047
3093
|
sessionKey: sk,
|
|
3048
3094
|
reason: autoDecision.reason,
|
|
3049
3095
|
attempts: autoContinueState.attempts,
|
|
3050
3096
|
textLength: visibleTextSinceContinue.length,
|
|
3097
|
+
lastTextLength: lastVisibleTextSinceContinue.length,
|
|
3051
3098
|
hadReasoning: hadReasoningSinceContinue,
|
|
3052
3099
|
hadToolActivity: hadToolActivitySinceContinue,
|
|
3053
3100
|
hadProxyActivity: hadProxyActivitySinceContinue
|
|
@@ -3057,10 +3104,15 @@ ${plan}
|
|
|
3057
3104
|
proc.stdin?.write(makeAutoContinueMessage() + "\n");
|
|
3058
3105
|
return;
|
|
3059
3106
|
}
|
|
3060
|
-
log.
|
|
3107
|
+
log.notice("auto-continuation stopped", {
|
|
3061
3108
|
sessionKey: sk,
|
|
3062
3109
|
reason: autoDecision.reason,
|
|
3063
|
-
attempts: autoContinueState.attempts
|
|
3110
|
+
attempts: autoContinueState.attempts,
|
|
3111
|
+
textLength: visibleTextSinceContinue.length,
|
|
3112
|
+
lastTextLength: lastVisibleTextSinceContinue.length,
|
|
3113
|
+
hadReasoning: hadReasoningSinceContinue,
|
|
3114
|
+
hadToolActivity: hadToolActivitySinceContinue,
|
|
3115
|
+
hadProxyActivity: hadProxyActivitySinceContinue
|
|
3064
3116
|
});
|
|
3065
3117
|
for (const [idx, reasoningId] of reasoningIds) {
|
|
3066
3118
|
if (reasoningStarted.get(idx)) {
|
|
@@ -3568,8 +3620,8 @@ import {
|
|
|
3568
3620
|
rmSync as rmSync2,
|
|
3569
3621
|
writeFileSync as writeFileSync4
|
|
3570
3622
|
} from "fs";
|
|
3571
|
-
import { homedir as
|
|
3572
|
-
import { join as
|
|
3623
|
+
import { homedir as homedir4 } from "os";
|
|
3624
|
+
import { join as join6, resolve as resolve2 } from "path";
|
|
3573
3625
|
import { fileURLToPath } from "url";
|
|
3574
3626
|
var STALE_PACKAGE_NAME = "opencode-claude-code-plugin";
|
|
3575
3627
|
var SUSPECT_DESCRIPTION_TOKEN = "Claude Code";
|
|
@@ -3577,14 +3629,14 @@ var alreadyRan = false;
|
|
|
3577
3629
|
function candidateCacheRoots() {
|
|
3578
3630
|
const xdg = process.env.XDG_CACHE_HOME;
|
|
3579
3631
|
return [
|
|
3580
|
-
xdg ?
|
|
3581
|
-
|
|
3582
|
-
|
|
3632
|
+
xdg ? join6(xdg, "opencode") : null,
|
|
3633
|
+
join6(homedir4(), ".cache", "opencode"),
|
|
3634
|
+
join6(homedir4(), "Library", "Caches", "opencode")
|
|
3583
3635
|
].filter((p) => Boolean(p));
|
|
3584
3636
|
}
|
|
3585
3637
|
function userOpencodeJsonPath() {
|
|
3586
|
-
const xdgConfig = process.env.XDG_CONFIG_HOME ??
|
|
3587
|
-
return
|
|
3638
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME ?? join6(homedir4(), ".config");
|
|
3639
|
+
return join6(xdgConfig, "opencode", "opencode.json");
|
|
3588
3640
|
}
|
|
3589
3641
|
function userIntendsToUseUnscoped() {
|
|
3590
3642
|
const cfg = userOpencodeJsonPath();
|
|
@@ -3627,7 +3679,7 @@ function cleanupStaleUnscopedInstall() {
|
|
|
3627
3679
|
}
|
|
3628
3680
|
function cleanupOne(cacheRoot, ourDir) {
|
|
3629
3681
|
if (!existsSync3(cacheRoot)) return;
|
|
3630
|
-
const stalePath =
|
|
3682
|
+
const stalePath = join6(cacheRoot, "node_modules", STALE_PACKAGE_NAME);
|
|
3631
3683
|
if (!existsSync3(stalePath)) return;
|
|
3632
3684
|
let realStalePath = stalePath;
|
|
3633
3685
|
try {
|
|
@@ -3635,7 +3687,7 @@ function cleanupOne(cacheRoot, ourDir) {
|
|
|
3635
3687
|
} catch {
|
|
3636
3688
|
}
|
|
3637
3689
|
if (ourDir && realStalePath === ourDir) return;
|
|
3638
|
-
const pkgJsonPath =
|
|
3690
|
+
const pkgJsonPath = join6(stalePath, "package.json");
|
|
3639
3691
|
if (!existsSync3(pkgJsonPath)) return;
|
|
3640
3692
|
let pkg = {};
|
|
3641
3693
|
try {
|
|
@@ -3655,7 +3707,7 @@ function cleanupOne(cacheRoot, ourDir) {
|
|
|
3655
3707
|
});
|
|
3656
3708
|
return;
|
|
3657
3709
|
}
|
|
3658
|
-
const cachePkgJson =
|
|
3710
|
+
const cachePkgJson = join6(cacheRoot, "package.json");
|
|
3659
3711
|
if (!existsSync3(cachePkgJson)) return;
|
|
3660
3712
|
try {
|
|
3661
3713
|
const cfg = JSON.parse(readFileSync3(cachePkgJson, "utf8"));
|