@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 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) console.error(fmt("INFO", msg, data));
49
+ if (DEBUG) emit("INFO", msg, data);
50
+ else writeToFile(fmt("INFO", msg, data));
17
51
  },
18
52
  notice(msg, data) {
19
- console.error(fmt("NOTICE", msg, data));
53
+ emit("NOTICE", msg, data, true);
20
54
  },
21
55
  warn(msg, data) {
22
- console.error(fmt("WARN", msg, data));
56
+ emit("WARN", msg, data, true);
23
57
  },
24
58
  error(msg, data) {
25
- console.error(fmt("ERROR", msg, data));
59
+ emit("ERROR", msg, data, true);
26
60
  },
27
61
  debug(msg, data) {
28
- if (DEBUG) console.error(fmt("DEBUG", msg, data));
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 homedir2, tmpdir as tmpdir2 } from "os";
1498
+ import { homedir as homedir3, tmpdir as tmpdir2 } from "os";
1464
1499
  import { randomUUID as randomUUID2 } from "crypto";
1465
- import { dirname as dirname2, join as join4 } from "path";
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(text)) {
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(join4(dir, "AGENTS.md"));
1604
+ const content = readPromptFileIfPresent(join5(dir, "AGENTS.md"));
1569
1605
  if (content) return content;
1570
- const parent = dirname2(dir);
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 ?? join4(homedir2(), ".config");
1586
- const globalAgents = readPromptFileIfPresent(join4(configRoot, "opencode", "AGENTS.md"));
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 = join4(tmpdir2(), `opencode-cc-sys-${randomUUID2()}.md`);
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.info("auto-continuing incomplete claude result", {
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.info("auto-continuation stopped", {
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 homedir3 } from "os";
3572
- import { join as join5, resolve as resolve2 } from "path";
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 ? join5(xdg, "opencode") : null,
3581
- join5(homedir3(), ".cache", "opencode"),
3582
- join5(homedir3(), "Library", "Caches", "opencode")
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 ?? join5(homedir3(), ".config");
3587
- return join5(xdgConfig, "opencode", "opencode.json");
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 = join5(cacheRoot, "node_modules", STALE_PACKAGE_NAME);
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 = join5(stalePath, "package.json");
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 = join5(cacheRoot, "package.json");
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"));