@lenasoftware/claudecode-plugin
v1.2.0
Published
Lena AgentOps telemetry plugin for Claude Code — captures hook events and sends them to the AgentOps ingestion API.
Readme
Lena AgentOps — Claude Code Plugin
Sends Claude Code session telemetry (sessions, turns, token usage, tool calls, context compaction, permissions, plans, file edits) to a Lena AgentOps backend, using the official Claude Code hooks + plugin system. The dashboard's setup guide lives under Organizations → Connectors → Claude Code Plugin.
Install
Inside Claude Code (the marketplace is served by the AgentOps backend and installs the
plugin from npm — @lenasoftware/claudecode-plugin):
/plugin marketplace add https://agentops.lena.solutions/claude/marketplace.json
/plugin install lena-agentops@lena-pluginsAlternative (git-based, e.g. for development): /plugin marketplace add
lenasoftware/lena-agentops then /plugin install lena-agentops@lena-agentops — this uses
the marketplace manifest at the repo root and clones the repo instead of npm.
Restart Claude Code, then configure the backend connection. Pick one (precedence: env — including project settings — over the config file):
Per-project (recommended, works in Claude Desktop):
.claude/settings.local.jsonin the repository (gitignored by default) — Claude Code passes theenvblock to the plugin hooks:{ "env": { "LENA_AGENTOPS_API_URL": "https://agentops.lena.solutions", "LENA_AGENTOPS_API_KEY": "<agentops-api-key>" } }Machine-wide:
~/.lena-agentops/claude-code.json(also works in Claude Desktop):{ "apiUrl": "https://agentops.lena.solutions", "apiKey": "<agentops-api-key>" }Shell profile (terminal launches only — GUI apps don't read it):
export LENA_AGENTOPS_API_URL="https://agentops.lena.solutions" export LENA_AGENTOPS_API_KEY="<agentops-api-key>" # created in the AgentOps web UI
How it works
Every subscribed Claude Code hook runs scripts/hook-handler.mjs, which maps the payload
to AgentOps events (source: "claude-code", same schemaVersion: "1.0" envelope as the
OpenCode plugin), appends them to a local queue at
~/.lena-agentops/claude-code/queue.jsonl, and spawns a detached flusher to deliver them.
If the backend is unreachable (or no API key is set yet) events buffer locally — up to
1000 — and flush automatically later. When events are queued but no API key is
configured, a throttled warning is written to ~/.lena-agentops/claude-code/plugin.log.
Per-session state (token snapshot, counters, tool spans) persists in
~/.lena-agentops/claude-code/state/.
Token usage and model are read incrementally from the session transcript: on every turn
end (Stop), at session end, and — so long agentic turns and interrupted turns still
report data — at most every 30 s during tool activity (PostToolUse). The session title
is derived from the first user prompt and upgraded to Claude Code's auto-generated
summary title when one appears in the transcript.
Tool arguments, prompts, and responses are credential-redacted and byte-capped locally before leaving the machine; what gets stored is additionally controlled by your organization's turn-capture consent and sampling settings on the backend.
Full contract: docs/claude-code-plugin-spec.md.
Troubleshooting
node scripts/flush-queue.mjs # drain the queue manually
LENA_AGENTOPS_DEBUG=1 claude # per-hook debug log
tail ~/.lena-agentops/claude-code/plugin.log # handler errors
tail ~/.lena-agentops/claude-code/debug.log # debug events (when enabled)Plugin update stuck on an old version. Claude Code installs npm-sourced plugins into
an internal npm project at ~/.claude/plugins/npm-cache/, whose package-lock.json pins
the version that was first installed. /plugin update (and even a marketplace remove +
re-add + reinstall) keeps resolving that pinned version and reports "already at the latest
version". Fix by bumping the pin, then reinstalling:
cd ~/.claude/plugins/npm-cache && npm install @lenasoftware/claudecode-plugin@<new-version>
claude plugin uninstall lena-agentops@lena-plugins
claude plugin install lena-agentops@lena-pluginsDo NOT work around it by putting the version inside the marketplace source.package
string ("@lenasoftware/[email protected]"): npm resolves it, but Claude Code then
looks for a node_modules directory named after the full pinned string and the install
fails with ENOENT. Keep the manifest on the package + source.version form.
Development
npm run smoke # node --test (no build step — plain Node 22 ESM)Publishing a release
- Bump the version in three places (they must stay in sync, or
/plugin updatewill not pick up the release):package.json,.claude-plugin/plugin.json, andweb/public/claude/marketplace.json(both the entryversionandsource.version). npm publish --access publicfrom this directory (prepublishOnlyruns the tests).- Redeploy the web/backend so the updated marketplace.json is served.
