Send Claude Code, OpenCode, or Pi session traces to Langfuse for observability.
Runs as a Claude Code Stop hook, an OpenCode plugin, or a Pi extension — after each assistant response, it assembles conversational turns and sends them to Langfuse as structured traces with generations and tool spans.
Written in Rust for fast startup and zero runtime dependencies. The process forks after assembling the payload — the parent exits immediately while the child sends the HTTP request in the background, adding minimal latency to your workflow.
| Agent | Integration |
|---|---|
| Claude Code | Stop hook (settings.json) |
| OpenCode | Plugin (~/.config/opencode/plugins/) |
| Pi | Extension (~/.pi/agent/extensions/) |
Each turn produces:
- Trace with session grouping, input/output, and tags
- Generation span with model name and token-level cost tracking
- Tool spans for each tool call (Bash, Read, Edit, etc.) with input/output
Traces are automatically tagged with:
| Tag | Example |
|---|---|
repo:<name> |
repo:my-project |
branch:<name> |
branch:main |
user:<name> |
user:doug |
host:<hostname> |
host:codex |
os:<platform> |
os:linux |
cc-version:<ver> |
cc-version:2.1.100 (Claude Code) |
oc-version:<ver> |
oc-version:0.4.5 (OpenCode) |
pi-version:<ver> |
pi-version:1.2.0 (Pi) |
curl -sfL https://raw.githubusercontent.com/isotoma/code-trace/main/install.sh | bashThis installs the binary to ~/.local/bin/code-trace. Make sure ~/.local/bin is in your PATH.
To also install the OpenCode plugin:
curl -sfL https://raw.githubusercontent.com/isotoma/code-trace/main/install.sh | bash -s -- --opencodeTo also install the Pi extension:
curl -sfL https://raw.githubusercontent.com/isotoma/code-trace/main/install.sh | bash -s -- --picargo install --git https://github.com/isotoma/code-trace.gitgit clone https://github.com/isotoma/code-trace.git
cd code-trace
cargo build --release
cp target/release/code-trace ~/.local/bin/Add your credentials to ~/.config/code-trace/config (created by the install script):
TRACE_TO_LANGFUSE=true
LANGFUSE_PUBLIC_KEY=pk-lf-...
LANGFUSE_SECRET_KEY=sk-lf-...
# LANGFUSE_BASE_URL=https://cloud.langfuse.com
# CODE_TRACE_DEBUG=false
This file is read by the binary at startup and works the same regardless of which agent you're using.
Respects $XDG_CONFIG_HOME: if set, the file is read from $XDG_CONFIG_HOME/code-trace/config.
Each agent also needs the hook/plugin installed so it invokes the binary.
The install script does this automatically. If not, add to ~/.claude/settings.json:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "code-trace"
}
]
}
]
}
}Copy plugin/opencode/code-trace.ts to your OpenCode plugins directory:
mkdir -p ~/.config/opencode/plugins/
cp plugin/opencode/code-trace.ts ~/.config/opencode/plugins/code-trace.tsOr use the install script with --opencode (see above).
Copy plugin/pi-agent/code-trace.ts to your Pi extensions directory:
mkdir -p ~/.pi/agent/extensions/
cp plugin/pi-agent/code-trace.ts ~/.pi/agent/extensions/code-trace.tsOr use the install script with --pi (see above).
Environment variables take precedence over the config file. This is useful for per-project credentials or CI environments.
| Variable | Required | Description |
|---|---|---|
TRACE_TO_LANGFUSE |
Yes | Set to true to enable tracing |
LANGFUSE_PUBLIC_KEY |
Yes | Langfuse public key |
LANGFUSE_SECRET_KEY |
Yes | Langfuse secret key |
LANGFUSE_BASE_URL |
No | Langfuse host (default: https://cloud.langfuse.com) |
CODE_TRACE_DEBUG |
No | Set to true for debug logging (alias: CC_TRACE_DEBUG) |
The CC_LANGFUSE_ prefix is also accepted for all Langfuse variables (e.g. CC_LANGFUSE_PUBLIC_KEY).
- Claude Code calls the hook after each assistant response, passing a JSON payload on stdin with the
sessionIdandtranscriptPath - The binary reads new lines from the transcript JSONL file (tracking offset in
~/.local/share/code-trace/state.json) - Messages are grouped into turns (user message + assistant responses + tool results)
- A batch of Langfuse ingestion events is built (
trace-create,generation-create,span-create) - The process forks — the parent exits immediately, while the child sends the batch to the Langfuse API via HTTP and logs the result
- The OpenCode plugin hooks into the
session.idleevent after each assistant response - It fetches new messages since the last processed message (tracked per-session in
~/.local/share/code-trace/opencode_cursor.json) - Messages are assembled into turns and piped to the
code-tracebinary over stdin - The binary forks — the parent returns immediately, while the child sends the batch to the Langfuse API via HTTP and logs the result
- The Pi extension hooks into the
agent_endevent after each user prompt completes - It reads new session entries since the last processed entry (tracked per-session in
~/.local/share/code-trace/pi_agent_cursor.json) - Entries are piped to the
code-tracebinary over stdin - The binary normalises Pi's session entry format into turns and forks — the parent returns immediately, while the child sends the batch to Langfuse
State is stored in ~/.local/share/code-trace/:
state.json— turn cursor per sessionstate.lock— file lock for concurrent accessopencode_cursor.json— OpenCode per-session message cursorpi_agent_cursor.json— Pi per-session entry cursorcode_trace.log— trace log
Note: State was previously stored in ~/.claude/state/. On first run, existing state is migrated automatically.
Enable debug logging with CODE_TRACE_DEBUG=true.
On first run after an update, any existing state in ~/.claude/state/code_trace_state.json is migrated to ~/.local/share/code-trace/state.json.
MIT