Other·terminal · desktop·Apache-2.0
autotel
OpenTelemetry traces of every hook dispatch, and each tool call's duration on its row.

autotel records what the hooks around a Claude Code session did, as OpenTelemetry traces: which plugin sat where in each dispatch, how long each link took, and which one denied a tool call. Claude Code’s own telemetry covers the model and the tools; this covers the hooks around them. It is for plugin authors who want traces of their own hooks, and for anyone who wants each tool call’s duration next to its row in the transcript. It never talks to the model, so it costs no tokens.
What it does
- One trace per turn. A
claude_code.turnroot span opens atprompt.submitand closes atturn.completewith the turn id, reason and duration. - One span per dispatch worth recording. Tool calls and checks, model calls,
process.run,http.fetch,fs.read,fs.write,mcp.call,command.run,agent.spawnand everyclassic.*shell hook. Getters, draws and per-tick events are left out. - The chain beneath, as span events. One
hookevent per link with the plugin name, tier, outcome (returned,passed,skipped,expired), own wall time and skip reason. A denied tool call names the plugin that decided it inclaude_code.decided_by. - A duration badge on tool rows. Once a call settles,
1.2sordenied by hook · 3msappears in dim text beside its row, in the terminal and in the desktop app. $.autotel.spanfor plugins. A noun added in theengine.createfold, so any plugin can put its own spans in the current turn’s trace.
Install
Needs Claude Code with function hooks enabled, npm, and an OTLP/HTTP collector to receive the spans. The author does not declare a minimum Claude Code version.
npm install autotel-claude-code
claude --plugin-dir node_modules/autotel-claude-code
Point OTEL_EXPORTER_OTLP_ENDPOINT at your collector before starting, or let autotel-devtools, the local trace viewer from the same monorepo, set it for you:
npx autotel-devtools claude --plugin-dir node_modules/autotel-claude-code
There is no marketplace. The package README suggests pointing one at the package directory if you prefer claude plugin install.
How it works
At session.start the mod reads OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_HEADERS and OTEL_SERVICE_NAME (default claude-code, so the spans sit beside Claude Code’s own). A * hook then wraps every dispatch, records the ones on its list and skips its own. A ui.render hook on the ToolUse component appends the badge.
What a span carries is narrow. Each dispatch gets the event name, the plugin that raised it and its tier. A tool.call adds the tool name, the tool_use_id and, in a subagent, the agent id. The turn root gets the turn id, reason, aborted flag and duration. A dispatch that throws gets the error message; a dropped prompt gets the reason a hook returned, not the prompt. Chain links carry plugin name, tier, outcome, milliseconds and skip reason. Nothing else: no tool arguments, file paths, commands, URLs, prompt text or model output. What you put in your own $.autotel.span attributes is your call.
Spans queue up and go out as one POST to <endpoint>/v1/traces a second after the first one lands, through $.http.fetch and $.clock.after; the queue also flushes when a turn ends and at session end. The resource carries service.name and the session id. Up to 1,000 spans wait for an unreachable collector, oldest dropped first. Nothing is written to disk. With no endpoint set the mod is inert: no spans, no badge, and $.autotel.span only runs its body.
For a plugin, the call takes a name, a body that receives the span, and optional attributes. The span joins the current turn’s trace, ends when the body settles, and a throw marks it failed and rethrows.
const result = await $.autotel.span('summarise', async (span) => {
span.setAttribute('plugin.feature', 'summary');
return $.model.complete(request);
});
For types, add node_modules/autotel-claude-code/types to the include of your tsconfig, or import Autotel from the package. Where the mod is not installed, $.autotel is absent and the call throws.
Limitations
- A hook’s own
$calls during a dispatch (a file read insidetool.call, say) land as siblings under the turn, not as children of that dispatch: the sandbox has no async context to carry a parent. - One attempt per batch, no retry. A collector that is down loses that batch.
More mods
The whole directory →agent-flow
Every subagent of the session in a live tree beside the transcript. Zero tokens.
claude plugin install agent-flow@claude-agent-flowanchorwatch-mod
Blocks rm -rf, force pushes, DROP TABLE, curl | sh and cat .env before they run.
claude --plugin-dir ./anchorwatch/plugins/anchorwatch-modautodev-core
Autonomous sprint loop: brainstorm, auto, iterate, audit, review, ship. Redacts secrets.
claude plugin install autodev-core@autodevcatalyst-probes
Model judges over subagent dispatches, defined in TOML. Plus your own prompt rules.
claude plugin install catalyst-probes@catalyst