Skip to content

Logging

Four orthogonal knobs, the env-var overrides, and the startup diagnostics block.

Configure via opencode.jsonc (launch-method-independent) or env vars (temporary override for a single process). The plugin has four orthogonal knobs:

Field Values Default Effect
file true | false false Persist log entries to disk
dir path string ~/.local/share/opencode-claude-code/ Custom file location
mode "silent" | "debug" "silent" TUI policy
level "debug" | "info" | "notice" | "warn" | "error" "info" Minimum level to emit

Rails-style threshold: anything below level is dropped before either destination decides what to do. mode: "silent" routes DEBUG/INFO/NOTICE to file only and lets WARN/ERROR bubble in the TUI (they always do). mode: "debug" additionally echoes every emitted level to the TUI (which opencode surfaces as warning bubbles).

logging is an ordinary provider option, so it goes under provider.claude-code.options like every other one. Keying it on the package name instead is the common mistake: opencode accepts that config without complaint and the plugin never reads it, so you get no log and no error.

Recommended dev setup, capturing an audit trail to disk while keeping the TUI quiet:

{
"provider": {
"claude-code": {
"options": {
"logging": { "file": true }
}
}
}
}

The snippets below abbreviate to the logging value alone; each one belongs at that same path.

Full firehose for deep debugging (every DEBUG stream event captured):

"logging": { "file": true, "level": "debug" }

Live TUI noise (everything echoes to opencode’s stderr → warning bubbles):

"logging": { "file": true, "mode": "debug" }

Env-var overrides

Set explicitly to override config for one process, which is useful for one-off debugging without editing opencode.jsonc:

Terminal window
OPENCODE_CLAUDE_CODE_LOG_FILE=1 opencode # file on
OPENCODE_CLAUDE_CODE_LOG_FILE=0 opencode # file off (overrides config:true)
OPENCODE_CLAUDE_CODE_LOG_DIR=/tmp/cc opencode # custom dir
OPENCODE_CLAUDE_CODE_LOG_LEVEL=debug opencode # capture every level
DEBUG=opencode-claude-code opencode # promote to mode:"debug"

Boolean env vars accept 1/true/on/yes for on and 0/false/no/off for off; empty / unset falls through to config. Invalid level values fall through to config.

Startup diagnostics

Once per process, right after the provider(s) register, the plugin logs a single NOTICE: claude-code plugin ready line summarizing everything worth knowing before you start debugging anything else:

Terminal window
OPENCODE_CLAUDE_CODE_LOG_FILE=1 opencode
grep "plugin ready" ~/.local/share/opencode-claude-code/plugin.log
{
"plugin": "0.11.1",
"opencode": "1.18.5",
"cwd": { "resolved": "/Users/you/code/app", "source": "process" },
"providers": ["claude-code-default", "claude-code-work"],
"accounts": ["default", "work"],
"proxyTools": ["Bash", "Edit", "Write", "WebFetch", "Task"],
"mcpServers": ["github", "slack"],
"permissionPresets": [
{ "provider": "claude-code-default", "preset": "none", "applied": false, "overrides": [] },
{
"provider": "claude-code-work",
"preset": "read-only",
"applied": true,
"overrides": ["skipPermissions: forced to false; ..."]
}
],
"interactiveTransport": false,
"anthropicApiKeyInEnv": false,
"claudeCli": { "path": "claude", "version": "2.1.211 (Claude Code)" }
}

Reading it:

  • cwd.source is which rule picked the working directory Claude will be spawned in: configured (you pinned options.cwd), process (normal), captured (process.cwd() was unusable and opencode’s project directory rescued it, the macOS GUI-launch case), or unresolved (neither worked). The per-session tier that opencode serve uses is resolved per call and so cannot appear here; this line mirrors the synchronous order only.
  • claudeCli.version reading not detected means the claude binary at that path didn’t answer --version, which also disables version-gated flags like --thinking-display.
  • mcpServers is the on-disk merge, before opencode’s runtime toggles are applied (those aren’t settled yet at startup).
  • permissionPresets is one row per provider rather than a single value, because a preset is a safety posture and two accounts can be configured with different ones. preset is the configured name or none; applied is false for none and for a name the plugin does not recognise, which applies nothing at all; overrides are the operator settings the preset replaced, the same lines logged at NOTICE when it was applied.
  • opencode is read from the running opencode binary (--version), since opencode still does not hand its version to plugins. It reads unknown when opencode is run from source rather than as the packaged binary.

This block is logged once, to a file that is off by default. For the same fields plus live process and proxy state, without enabling logging, run /claude-code-doctor in the session.

Default behavior (no config, no env)

Nothing persists; only WARN and ERROR bubble in the TUI. The plugin doesn’t accrete a log file on every user’s disk by default. Opt in when you need to inspect auto-continue decisions, broker state, or other plugin internals.

Free and MIT-licensed. If the plugin saves you time, you can buy its maintainer a coffee.

Buy me a coffee