@khalilgharbaoui/opencode-claude-code-plugin 0.4.16 → 0.4.17
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 +55 -17
- package/dist/index.d.ts +39 -0
- package/dist/index.js +87 -27
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -319,30 +319,68 @@ Set `permissionMode: "plan"` to forward `--permission-mode plan` to Claude. The
|
|
|
319
319
|
- **Lazy `cwd`.** The working directory is re-resolved at every request, so opencode's project-aware behavior works without restarting the plugin.
|
|
320
320
|
- **Variants survive merge.** opencode recalculates variant lists after the plugin loads; the plugin re-injects defaults into runtime config so your variants don't disappear.
|
|
321
321
|
|
|
322
|
-
##
|
|
322
|
+
## Logging
|
|
323
323
|
|
|
324
|
-
|
|
324
|
+
Configure via `opencode.jsonc` (launch-method-independent) or env vars
|
|
325
|
+
(temporary override for a single process). The plugin has four orthogonal
|
|
326
|
+
knobs:
|
|
325
327
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
328
|
+
| Field | Values | Default | Effect |
|
|
329
|
+
|---|---|---|---|
|
|
330
|
+
| `file` | `true \| false` | `false` | Persist log entries to disk |
|
|
331
|
+
| `dir` | path string | `~/.local/share/opencode-claude-code/` | Custom file location |
|
|
332
|
+
| `mode` | `"silent" \| "debug"` | `"silent"` | TUI policy |
|
|
333
|
+
| `level` | `"debug" \| "info" \| "notice" \| "warn" \| "error"` | `"info"` | Minimum level to emit |
|
|
334
|
+
|
|
335
|
+
Rails-style threshold: anything below `level` is dropped before either
|
|
336
|
+
destination decides what to do. `mode: "silent"` routes DEBUG/INFO/NOTICE
|
|
337
|
+
to file only and lets WARN/ERROR bubble in the TUI (they always do).
|
|
338
|
+
`mode: "debug"` additionally echoes every emitted level to the TUI (which
|
|
339
|
+
opencode surfaces as warning bubbles).
|
|
340
|
+
|
|
341
|
+
**Recommended dev setup** — capture audit trail to disk, keep TUI quiet:
|
|
342
|
+
|
|
343
|
+
```jsonc
|
|
344
|
+
"@khalilgharbaoui/opencode-claude-code-plugin": {
|
|
345
|
+
"logging": { "file": true }
|
|
346
|
+
}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
**Full firehose for deep debugging** (every DEBUG stream event captured):
|
|
350
|
+
|
|
351
|
+
```jsonc
|
|
352
|
+
"logging": { "file": true, "level": "debug" }
|
|
353
|
+
```
|
|
329
354
|
|
|
330
|
-
|
|
331
|
-
OPENCODE_CLAUDE_CODE_LOG_FILE=1 opencode
|
|
355
|
+
**Live TUI noise** (everything echoes to opencode's stderr → warning bubbles):
|
|
332
356
|
|
|
333
|
-
|
|
334
|
-
|
|
357
|
+
```jsonc
|
|
358
|
+
"logging": { "file": true, "mode": "debug" }
|
|
335
359
|
```
|
|
336
360
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
361
|
+
### Env-var overrides
|
|
362
|
+
|
|
363
|
+
Set explicitly to override config for one process — useful for one-off
|
|
364
|
+
debugging without editing `opencode.jsonc`:
|
|
365
|
+
|
|
366
|
+
```bash
|
|
367
|
+
OPENCODE_CLAUDE_CODE_LOG_FILE=1 opencode # file on
|
|
368
|
+
OPENCODE_CLAUDE_CODE_LOG_FILE=0 opencode # file off (overrides config:true)
|
|
369
|
+
OPENCODE_CLAUDE_CODE_LOG_DIR=/tmp/cc opencode # custom dir
|
|
370
|
+
OPENCODE_CLAUDE_CODE_LOG_LEVEL=debug opencode # capture every level
|
|
371
|
+
DEBUG=opencode-claude-code opencode # promote to mode:"debug"
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
Boolean env vars accept `1/true/on/yes` for on and `0/false/no/off` for
|
|
375
|
+
off; empty / unset falls through to config. Invalid `level` values fall
|
|
376
|
+
through to config.
|
|
377
|
+
|
|
378
|
+
### Default behavior (no config, no env)
|
|
342
379
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
state, or other
|
|
380
|
+
Nothing persists; only WARN and ERROR bubble in the TUI. The plugin
|
|
381
|
+
doesn't accrete a log file on every user's disk by default — opt in when
|
|
382
|
+
you need to inspect auto-continue decisions, broker state, or other
|
|
383
|
+
plugin internals.
|
|
346
384
|
|
|
347
385
|
## Known limitations
|
|
348
386
|
|
package/dist/index.d.ts
CHANGED
|
@@ -117,7 +117,38 @@ interface ClaudeCodeConfig {
|
|
|
117
117
|
proxyOpencodeMcpTools?: boolean;
|
|
118
118
|
multiStepContinuation?: boolean;
|
|
119
119
|
autoContinueIncompleteTurns?: boolean | "smart";
|
|
120
|
+
logging?: LoggingConfig;
|
|
120
121
|
}
|
|
122
|
+
interface LoggingConfig {
|
|
123
|
+
/**
|
|
124
|
+
* Persist log activity (DEBUG / INFO / NOTICE / WARN / ERROR — those
|
|
125
|
+
* passing `level`) to a file. Default: `false`. When `false`, entries
|
|
126
|
+
* below WARN vanish entirely; WARN / ERROR still surface in the TUI via
|
|
127
|
+
* stderr. Set to `true` to capture the audit trail to disk for review
|
|
128
|
+
* via `tail` / `grep`.
|
|
129
|
+
*/
|
|
130
|
+
file?: boolean;
|
|
131
|
+
/**
|
|
132
|
+
* Optional custom directory for the file log. Defaults to
|
|
133
|
+
* `~/.local/share/opencode-claude-code/`. Has no effect when `file:false`.
|
|
134
|
+
*/
|
|
135
|
+
dir?: string;
|
|
136
|
+
/**
|
|
137
|
+
* TUI policy. `"silent"` (default) routes DEBUG / INFO / NOTICE to file
|
|
138
|
+
* only; WARN / ERROR still bubble in the TUI as they always do. `"debug"`
|
|
139
|
+
* additionally echoes every emitted level to stderr (which opencode's TUI
|
|
140
|
+
* surfaces as warning bubbles).
|
|
141
|
+
*/
|
|
142
|
+
mode?: LogMode;
|
|
143
|
+
/**
|
|
144
|
+
* Minimum level to emit anywhere. Anything below the threshold is dropped
|
|
145
|
+
* before either destination decides what to do. Order:
|
|
146
|
+
* `debug` < `info` < `notice` < `warn` < `error`. Default: `"info"`.
|
|
147
|
+
*/
|
|
148
|
+
level?: LogLevel;
|
|
149
|
+
}
|
|
150
|
+
type LogLevel = "debug" | "info" | "notice" | "warn" | "error";
|
|
151
|
+
type LogMode = "silent" | "debug";
|
|
121
152
|
type WebSearchRouting = "claude" | "disabled" | (string & {});
|
|
122
153
|
interface ClaudeCodeProviderSettings {
|
|
123
154
|
cliPath?: string;
|
|
@@ -233,6 +264,14 @@ interface ClaudeCodeProviderSettings {
|
|
|
233
264
|
* Set to `false` to disable.
|
|
234
265
|
*/
|
|
235
266
|
autoContinueIncompleteTurns?: boolean | "smart";
|
|
267
|
+
/**
|
|
268
|
+
* Logger configuration. See `LoggingConfig` for fields. Env vars
|
|
269
|
+
* (`OPENCODE_CLAUDE_CODE_LOG_FILE`, `OPENCODE_CLAUDE_CODE_LOG_DIR`,
|
|
270
|
+
* `OPENCODE_CLAUDE_CODE_LOG_LEVEL`, `DEBUG=opencode-claude-code`) override
|
|
271
|
+
* these values when explicitly set, so a developer can flip behavior for
|
|
272
|
+
* one process without editing opencode.jsonc.
|
|
273
|
+
*/
|
|
274
|
+
logging?: LoggingConfig;
|
|
236
275
|
}
|
|
237
276
|
type PermissionMode = "acceptEdits" | "auto" | "bypassPermissions" | "default" | "dontAsk" | "plan";
|
|
238
277
|
type ControlRequestBehavior = "allow" | "deny";
|
package/dist/index.js
CHANGED
|
@@ -5,34 +5,80 @@ import { generateId } from "@ai-sdk/provider-utils";
|
|
|
5
5
|
import { appendFileSync, mkdirSync, renameSync, statSync } from "fs";
|
|
6
6
|
import { homedir } from "os";
|
|
7
7
|
import { dirname, join } from "path";
|
|
8
|
-
var
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
var LEVEL_RANK = {
|
|
9
|
+
debug: 0,
|
|
10
|
+
info: 1,
|
|
11
|
+
notice: 2,
|
|
12
|
+
warn: 3,
|
|
13
|
+
error: 4
|
|
14
|
+
};
|
|
11
15
|
var MAX_LOG_BYTES = 5 * 1024 * 1024;
|
|
12
|
-
|
|
13
|
-
|
|
16
|
+
var DEFAULT_DIR = join(homedir(), ".local", "share", "opencode-claude-code");
|
|
17
|
+
var DEFAULT_CONFIG = {
|
|
18
|
+
file: false,
|
|
19
|
+
dir: null,
|
|
20
|
+
mode: "silent",
|
|
21
|
+
level: "info"
|
|
22
|
+
};
|
|
23
|
+
function parseBoolEnv(v) {
|
|
24
|
+
if (v == null) return void 0;
|
|
25
|
+
const s = v.toLowerCase().trim();
|
|
26
|
+
if (s === "") return void 0;
|
|
27
|
+
if (s === "0" || s === "false" || s === "no" || s === "off") return false;
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
function parseLevelEnv(v) {
|
|
31
|
+
if (v == null) return void 0;
|
|
14
32
|
const s = v.toLowerCase().trim();
|
|
15
|
-
if (s === "") return
|
|
16
|
-
|
|
33
|
+
if (s === "") return void 0;
|
|
34
|
+
if (s === "debug" || s === "info" || s === "notice" || s === "warn" || s === "error") {
|
|
35
|
+
return s;
|
|
36
|
+
}
|
|
37
|
+
return void 0;
|
|
17
38
|
}
|
|
18
|
-
|
|
39
|
+
function parseModeFromDebugEnv(v) {
|
|
40
|
+
if (v == null || v === "") return void 0;
|
|
41
|
+
return v.includes("opencode-claude-code") ? "debug" : void 0;
|
|
42
|
+
}
|
|
43
|
+
function withEnvOverrides(base) {
|
|
44
|
+
const result = { ...base };
|
|
45
|
+
const envFile = parseBoolEnv(process.env.OPENCODE_CLAUDE_CODE_LOG_FILE);
|
|
46
|
+
if (envFile !== void 0) result.file = envFile;
|
|
47
|
+
const envDir = process.env.OPENCODE_CLAUDE_CODE_LOG_DIR;
|
|
48
|
+
if (envDir !== void 0 && envDir !== "") result.dir = envDir;
|
|
49
|
+
const envMode = parseModeFromDebugEnv(process.env.DEBUG);
|
|
50
|
+
if (envMode !== void 0) result.mode = envMode;
|
|
51
|
+
const envLevel = parseLevelEnv(process.env.OPENCODE_CLAUDE_CODE_LOG_LEVEL);
|
|
52
|
+
if (envLevel !== void 0) result.level = envLevel;
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
var activeConfig = withEnvOverrides(DEFAULT_CONFIG);
|
|
19
56
|
var fileLoggingDisabled = false;
|
|
20
|
-
function
|
|
57
|
+
function configureLogger(input) {
|
|
58
|
+
const merged = { ...DEFAULT_CONFIG, ...input };
|
|
59
|
+
activeConfig = withEnvOverrides(merged);
|
|
60
|
+
fileLoggingDisabled = false;
|
|
61
|
+
}
|
|
62
|
+
function resolvedLogFile() {
|
|
63
|
+
return join(activeConfig.dir ?? DEFAULT_DIR, "plugin.log");
|
|
64
|
+
}
|
|
65
|
+
function rotateIfNeeded(logFile) {
|
|
21
66
|
try {
|
|
22
|
-
const stat = statSync(
|
|
67
|
+
const stat = statSync(logFile);
|
|
23
68
|
if (stat.size > MAX_LOG_BYTES) {
|
|
24
|
-
renameSync(
|
|
69
|
+
renameSync(logFile, `${logFile}.1`);
|
|
25
70
|
}
|
|
26
71
|
} catch {
|
|
27
72
|
}
|
|
28
73
|
}
|
|
29
74
|
function writeToFile(line) {
|
|
30
|
-
if (!
|
|
75
|
+
if (!activeConfig.file) return;
|
|
31
76
|
if (fileLoggingDisabled) return;
|
|
32
77
|
try {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
78
|
+
const logFile = resolvedLogFile();
|
|
79
|
+
mkdirSync(dirname(logFile), { recursive: true });
|
|
80
|
+
rotateIfNeeded(logFile);
|
|
81
|
+
appendFileSync(logFile, line + "\n", "utf8");
|
|
36
82
|
} catch {
|
|
37
83
|
fileLoggingDisabled = true;
|
|
38
84
|
}
|
|
@@ -45,30 +91,36 @@ function fmt(level, msg, data) {
|
|
|
45
91
|
}
|
|
46
92
|
return base;
|
|
47
93
|
}
|
|
48
|
-
function
|
|
49
|
-
|
|
50
|
-
|
|
94
|
+
function shouldEmit(level) {
|
|
95
|
+
return LEVEL_RANK[level] >= LEVEL_RANK[activeConfig.level];
|
|
96
|
+
}
|
|
97
|
+
function shouldTui(level) {
|
|
98
|
+
if (level === "warn" || level === "error") return true;
|
|
99
|
+
return activeConfig.mode === "debug";
|
|
100
|
+
}
|
|
101
|
+
function emit(level, msg, data) {
|
|
102
|
+
if (!shouldEmit(level)) return;
|
|
103
|
+
const line = fmt(level.toUpperCase(), msg, data);
|
|
104
|
+
if (shouldTui(level)) {
|
|
51
105
|
console.error(line);
|
|
52
106
|
}
|
|
53
107
|
writeToFile(line);
|
|
54
108
|
}
|
|
55
109
|
var log = {
|
|
110
|
+
debug(msg, data) {
|
|
111
|
+
emit("debug", msg, data);
|
|
112
|
+
},
|
|
56
113
|
info(msg, data) {
|
|
57
|
-
|
|
58
|
-
else writeToFile(fmt("INFO", msg, data));
|
|
114
|
+
emit("info", msg, data);
|
|
59
115
|
},
|
|
60
116
|
notice(msg, data) {
|
|
61
|
-
emit("
|
|
117
|
+
emit("notice", msg, data);
|
|
62
118
|
},
|
|
63
119
|
warn(msg, data) {
|
|
64
|
-
emit("
|
|
120
|
+
emit("warn", msg, data);
|
|
65
121
|
},
|
|
66
122
|
error(msg, data) {
|
|
67
|
-
emit("
|
|
68
|
-
},
|
|
69
|
-
debug(msg, data) {
|
|
70
|
-
if (DEBUG) emit("DEBUG", msg, data);
|
|
71
|
-
else writeToFile(fmt("DEBUG", msg, data));
|
|
123
|
+
emit("error", msg, data);
|
|
72
124
|
}
|
|
73
125
|
};
|
|
74
126
|
|
|
@@ -3791,6 +3843,14 @@ function pickOpencodeDirectory(input) {
|
|
|
3791
3843
|
return void 0;
|
|
3792
3844
|
}
|
|
3793
3845
|
function createClaudeCode(settings = {}) {
|
|
3846
|
+
if (settings.logging) {
|
|
3847
|
+
configureLogger({
|
|
3848
|
+
file: settings.logging.file ?? false,
|
|
3849
|
+
dir: settings.logging.dir ?? null,
|
|
3850
|
+
mode: settings.logging.mode ?? "silent",
|
|
3851
|
+
level: settings.logging.level ?? "info"
|
|
3852
|
+
});
|
|
3853
|
+
}
|
|
3794
3854
|
const cliPath = settings.cliPath ?? process.env.CLAUDE_CLI_PATH ?? "claude";
|
|
3795
3855
|
const providerName = settings.providerID ?? settings.name ?? "claude-code";
|
|
3796
3856
|
const proxyTools = settings.proxyTools ?? ["Bash", "Edit", "Write", "WebFetch"];
|