Skip to content

Per-turn stats

The optional cost footer, and the CLI events that are no longer silent.

Off by default. With turnStats: true:

▌ **stats:** $0.0123 · 4.2 s · 2 CLI turns · in 1.2k · out 812 · cache read 45.1k · cache write 2.0k

One line at the end of a finished turn, from the numbers the CLI already reports on its result. Notes:

  • Never on a /compact turn (the footer would be appended to what opencode stores as the summary) and never on a turn that ended in error, where the error is the thing to read.
  • It is its own text part led by ▌ **stats:**, and the plugin strips it again if the conversation is ever replayed into a fresh Claude Code process. The model never reads its own accounting.
  • Token counts are the turn’s totals, which is what matches the cost. They are deliberately not what opencode records for the message: opencode reads that as context occupancy (its context gauge and its auto-compaction), so it gets the input and cache counts of the turn’s last API call, plus the whole turn’s output. opencode’s own cost figure for a multi-call turn therefore counts only the last call’s input and cache; the CLI’s real cost is this line and providerMetadata["claude-code"].costUsd.
  • The cost is what the CLI reported for the turn, not a billing guarantee.

The same numbers are logged at INFO whatever this option is set to, and total_cost_usd, duration_ms, duration_api_ms, num_turns, usage, modelUsage and permission_denials always reach providerMetadata (denials by tool name and id only, never their inputs).

Things the CLI says that are no longer silent

Four Claude Code stream events used to reach nothing but a debug log:

  • A rate-limit rejection. When the CLI reports status: "rejected" (or a rejected extra-usage state), the turn now carries a ▌ **rate limit:** line naming the window, the reason extra usage is unavailable, when it resets, and the four things that can be done about it. Warned once per identity per process. See Billing and which login bills what.
  • A context compaction Claude Code did on its own. A ▌ **context compacted:** note says so, with the before and after token counts, so an answer that suddenly forgets the start of the conversation has a visible cause.
  • A conversation Claude Code cleared. Sending /clear as a message, or a plan-mode exit that clears context, makes Claude Code start a fresh conversation while opencode still shows the old messages. A ▌ **claude code reset:** note says so. The plugin deliberately does not replay the earlier messages, since that would undo the clear. Start a new opencode session if you want the two to match.
  • A result whose subtype is not success (error_max_turns, error_during_execution, …). The subtype is named in the transcript and the turn finishes as an error instead of an ordinary reply.
  • A CLI-executed tool that failed. Its result is forwarded with the AI SDK’s error flag, so opencode renders the row as failed rather than as a success whose output happens to be an error message.
  • A turn that finished cleanly without saying anything. No text, no tool call, no error: opencode files it as an ordinary reply, so what you get is a blank message with nothing to distinguish it from a crash. A ▌ **no reply:** note now says which of the two shapes it was, thinking-with-no-answer or nothing at all, and that nothing failed and nothing is pending. It is its own text part and is stripped from any transcript rebuilt for the CLI. Never on a compaction turn, a failed turn, an aborted one, or one that ended on a question, and suppressed entirely by "autoContinueIncompleteTurns": false. There is deliberately no automatic retry: measured across the whole retained plugin log, every finished turn carried between 940 and 5,080 characters of reply and none was silent.

At session start the plugin also warns once per process for each MCP server Claude Code could not connect (its tools are simply absent otherwise) and once when the CLI’s own apiKeySource says an API key is in effect, which is the field that tells you pay-as-you-go billing is happening. See ignoreAnthropicApiKey.

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

Buy me a coffee