Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .markdownlint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
default: true
MD013: false # line-length — too strict for tables and long URLs
MD024: # no-duplicate-heading
siblings_only: true # allow duplicate headings in different sections (e.g. CHANGELOG)
MD041: false # first-line-heading — not all files need an H1 (e.g. CLAUDE.md)
1 change: 0 additions & 1 deletion .npmrc

This file was deleted.

31 changes: 31 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-merge-conflict
- id: check-case-conflict
- id: check-json
exclude: tsconfig.json # tsconfig uses JSONC (comments allowed)
- id: pretty-format-json
args: [--autofix, --indent=2]
exclude: ^(bun\.lock|tsconfig\.json)$
- id: check-yaml
- id: mixed-line-ending
args: [--fix=lf]

- repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.44.0
hooks:
- id: markdownlint
args: [--fix]

- repo: local
hooks:
- id: typecheck
name: TypeScript typecheck
language: system
entry: bun run typecheck
pass_filenames: false
types: [ts]
6 changes: 3 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ bun test

## Project layout

```
```text
src/
├── index.ts — Plugin entrypoint
├── types.ts — Shared types
Expand Down Expand Up @@ -52,7 +52,7 @@ src/

All commits must follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/):

```
```text
<type>[optional scope]: <description>
```

Expand All @@ -62,7 +62,7 @@ Use `!` or a `BREAKING CHANGE:` footer for breaking changes.

Examples:

```
```text
feat(handlers): add support for file.edited event
fix(probe): handle malformed endpoint URL without throwing
chore(deps): bump @opentelemetry/api to 1.10.0
Expand Down
29 changes: 14 additions & 15 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and

## [0.3.0](https://github.com/DEVtheOPS/opencode-plugin-otel/compare/v0.2.1...v0.3.0) (2026-03-14)


### Features

* **observability:** add debug logging and enhanced metrics ([a1b0a8c](https://github.com/DEVtheOPS/opencode-plugin-otel/commit/a1b0a8cf5263080cf9623355e5161fb88f20e2f1))
Expand All @@ -21,37 +20,37 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and

### Changed

- **BREAKING** — Package renamed to `@devtheops/opencode-plugin-otel`. Update your opencode config from `"opencode-plugin-otel"` to `"@devtheops/opencode-plugin-otel"`.
* **BREAKING** — Package renamed to `@devtheops/opencode-plugin-otel`. Update your opencode config from `"opencode-plugin-otel"` to `"@devtheops/opencode-plugin-otel"`.

---

## [0.1.1] — 2026-03-11

### Fixed

- Release workflow now uses npm trusted publishing (OIDC) with Node 22.14.0 and creates a GitHub release with changelog notes and npm package link.
* Release workflow now uses npm trusted publishing (OIDC) with Node 22.14.0 and creates a GitHub release with changelog notes and npm package link.

---

## [0.1.0] — 2026-03-11

### Added

- **Release workflow** — `.github/workflows/release.yml` publishes to npm automatically when a `v*` tag is pushed, gated by typecheck and tests.
- **`OPENCODE_OTLP_HEADERS`** — new env var for comma-separated `key=value` OTLP auth headers (e.g. `x-honeycomb-team=abc,x-tenant=org`). Copied to `OTEL_EXPORTER_OTLP_HEADERS` before the SDK initialises.
- **`OPENCODE_RESOURCE_ATTRIBUTES`** — new env var for comma-separated `key=value` OTel resource attributes (e.g. `service.version=1.2.3,deployment.environment=production`). Copied to `OTEL_RESOURCE_ATTRIBUTES` before the SDK initialises.
- JSDoc on all exported functions, types, and constants.
- Regression tests covering `OTEL_*` passthrough behaviour — pre-existing values are preserved when `OPENCODE_*` vars are unset; `OPENCODE_*` vars overwrite when set.
- README table of contents, usage examples for headers and resource attributes, and a security note advising that `OPENCODE_OTLP_HEADERS` may contain sensitive tokens and should not be committed to version control.
* **Release workflow** — `.github/workflows/release.yml` publishes to npm automatically when a `v*` tag is pushed, gated by typecheck and tests.
* **`OPENCODE_OTLP_HEADERS`** — new env var for comma-separated `key=value` OTLP auth headers (e.g. `x-honeycomb-team=abc,x-tenant=org`). Copied to `OTEL_EXPORTER_OTLP_HEADERS` before the SDK initialises.
* **`OPENCODE_RESOURCE_ATTRIBUTES`** — new env var for comma-separated `key=value` OTel resource attributes (e.g. `service.version=1.2.3,deployment.environment=production`). Copied to `OTEL_RESOURCE_ATTRIBUTES` before the SDK initialises.
* JSDoc on all exported functions, types, and constants.
* Regression tests covering `OTEL_*` passthrough behaviour — pre-existing values are preserved when `OPENCODE_*` vars are unset; `OPENCODE_*` vars overwrite when set.
* README table of contents, usage examples for headers and resource attributes, and a security note advising that `OPENCODE_OTLP_HEADERS` may contain sensitive tokens and should not be committed to version control.

### Changed

- `package.json` `main`/`module` now point directly at `src/index.ts`; root `index.ts` re-export removed.
- `files` field added to `package.json` — published package contains only `src/`, reducing install size.
- All user-facing env vars are now consistently `OPENCODE_`-prefixed. `loadConfig` copies `OPENCODE_OTLP_HEADERS` → `OTEL_EXPORTER_OTLP_HEADERS` and `OPENCODE_RESOURCE_ATTRIBUTES` → `OTEL_RESOURCE_ATTRIBUTES` so the OTel SDK picks them up natively.
- `parseEnvInt` now rejects partial numeric strings such as `"1.5"` or `"5000ms"`, returning the fallback instead of silently truncating.
* `package.json` `main`/`module` now point directly at `src/index.ts`; root `index.ts` re-export removed.
* `files` field added to `package.json` — published package contains only `src/`, reducing install size.
* All user-facing env vars are now consistently `OPENCODE_`-prefixed. `loadConfig` copies `OPENCODE_OTLP_HEADERS` → `OTEL_EXPORTER_OTLP_HEADERS` and `OPENCODE_RESOURCE_ATTRIBUTES` → `OTEL_RESOURCE_ATTRIBUTES` so the OTel SDK picks them up natively.
* `parseEnvInt` now rejects partial numeric strings such as `"1.5"` or `"5000ms"`, returning the fallback instead of silently truncating.

### Removed

- `parseHeaders` removed from `src/otel.ts` — the OTel SDK reads `OTEL_EXPORTER_OTLP_HEADERS` natively once `loadConfig` copies the value across.
- Manual `release:patch` / `release:minor` / `release:major` npm scripts removed in favour of the tag-based CI workflow.
* `parseHeaders` removed from `src/otel.ts` — the OTel SDK reads `OTEL_EXPORTER_OTLP_HEADERS` natively once `loadConfig` copies the value across.
* Manual `release:patch` / `release:minor` / `release:major` npm scripts removed in favour of the tag-based CI workflow.
8 changes: 4 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ opencode loads TypeScript natively via Bun, so there is no build step required d

## Project structure

```
```text
src/
├── index.ts — Plugin entrypoint, wires everything together
├── types.ts — Shared types (Level, HandlerContext, Instruments, etc.)
Expand Down Expand Up @@ -65,7 +65,7 @@ Then set `OPENCODE_ENABLE_TELEMETRY=1` and start opencode. The collector will pr

This project follows [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/). All commits must be structured as:

```
```text
<type>[optional scope]: <description>

[optional body]
Expand All @@ -91,15 +91,15 @@ This project follows [Conventional Commits](https://www.conventionalcommits.org/

Append `!` after the type or add a `BREAKING CHANGE:` footer:

```
```text
feat!: drop support for OTLP HTTP

BREAKING CHANGE: only OTLP/gRPC is supported going forward
```

### Examples

```
```text
feat(handlers): add support for file.edited event
fix(probe): handle malformed endpoint URL without throwing
docs: update Datadog configuration example
Expand Down
69 changes: 55 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ An [opencode](https://opencode.ai) plugin that exports telemetry via OpenTelemet
- [Configuration](#configuration)
- [Quick start](#quick-start)
- [Headers and resource attributes](#headers-and-resource-attributes)
- [Disabling specific metrics](#disabling-specific-metrics)
- [Datadog example](#datadog-example)
- [Honeycomb example](#honeycomb-example)
- [Claude Code dashboard compatibility](#claude-code-dashboard-compatibility)
Expand All @@ -18,21 +19,28 @@ An [opencode](https://opencode.ai) plugin that exports telemetry via OpenTelemet

### Metrics

| Metric | Description |
|--------|-------------|
| `opencode.session.count` | Counter — incremented on each `session.created` event |
| `opencode.token.usage` | Counter — per token type: `input`, `output`, `reasoning`, `cacheRead`, `cacheCreation` |
| `opencode.cost.usage` | Counter — USD cost per completed assistant message |
| `opencode.lines_of_code.count` | Counter — lines added/removed per `session.diff` event |
| `opencode.commit.count` | Counter — git commits detected via bash tool |
| `opencode.tool.duration` | Histogram — tool execution time in milliseconds |
| Metric | Type | Description |
|--------|------|-------------|
| `opencode.session.count` | Counter | Incremented on each `session.created` event |
| `opencode.token.usage` | Counter | Per token type: `input`, `output`, `reasoning`, `cacheRead`, `cacheCreation` |
| `opencode.cost.usage` | Counter | USD cost per completed assistant message |
| `opencode.lines_of_code.count` | Counter | Lines added/removed per `session.diff` event |
| `opencode.commit.count` | Counter | Git commits detected via bash tool |
| `opencode.tool.duration` | Histogram | Tool execution time in milliseconds |
| `opencode.cache.count` | Counter | Cache activity per message: `type=cacheRead` or `type=cacheCreation` |
| `opencode.session.duration` | Histogram | Session duration from created to idle in milliseconds |
| `opencode.message.count` | Counter | Completed assistant messages per session |
| `opencode.session.token.total` | Histogram | Total tokens consumed per session, recorded on idle |
| `opencode.session.cost.total` | Histogram | Total cost per session in USD, recorded on idle |
| `opencode.model.usage` | Counter | Messages per model and provider |
| `opencode.retry.count` | Counter | API retries observed via `session.status` events |

### Log events

| Event | Description |
|-------|-------------|
| `session.created` | Session started |
| `session.idle` | Session went idle |
| `session.idle` | Session went idle (includes total tokens, cost, messages) |
| `session.error` | Session error |
| `user_prompt` | User sent a message (includes `prompt_length`, `model`, `agent`) |
| `api_request` | Completed assistant message (tokens, cost, duration) |
Expand Down Expand Up @@ -72,9 +80,18 @@ All configuration is via environment variables. Set them in your shell profile (
| `OPENCODE_OTLP_METRICS_INTERVAL` | `60000` | Metrics export interval in milliseconds |
| `OPENCODE_OTLP_LOGS_INTERVAL` | `5000` | Logs export interval in milliseconds |
| `OPENCODE_METRIC_PREFIX` | `opencode.` | Prefix for all metric names (e.g. set to `claude_code.` for Claude Code dashboard compatibility) |
| `OPENCODE_OTLP_HEADERS` | _(unset)_ | Comma-separated `key=value` headers added to all OTLP exports. Example: `api-key=abc123,x-tenant=my-org`. **Keep out of version control — may contain sensitive auth tokens.** |
| `OPENCODE_DISABLE_METRICS` | _(unset)_ | Comma-separated list of metric name suffixes to disable (e.g. `cache.count,session.duration`) |
| `OPENCODE_OTLP_HEADERS` | _(unset)_ | Comma-separated `key=value` headers added to all OTLP exports. **Keep out of version control — may contain sensitive auth tokens.** |
| `OPENCODE_RESOURCE_ATTRIBUTES` | _(unset)_ | Comma-separated `key=value` pairs merged into the OTel resource. Example: `service.version=1.2.3,deployment.environment=production` |

### Quick start

```bash
export OPENCODE_ENABLE_TELEMETRY=1
export OPENCODE_OTLP_ENDPOINT=http://localhost:4317
opencode
```

### Headers and resource attributes

```bash
Expand All @@ -87,14 +104,38 @@ export OPENCODE_RESOURCE_ATTRIBUTES="service.version=1.2.3,deployment.environmen

> **Security note:** `OPENCODE_OTLP_HEADERS` typically contains auth tokens. Set it in your shell profile (`~/.zshrc`, `~/.bashrc`) or a secrets manager — never commit it to version control or print it in CI logs.

### Quick start
### Disabling specific metrics

Use `OPENCODE_DISABLE_METRICS` to suppress individual metrics. The value is a comma-separated list of metric name suffixes (without the prefix).

Disabling a metric only stops the counter/histogram from being incremented — the corresponding log events are still emitted.

```bash
export OPENCODE_ENABLE_TELEMETRY=1
export OPENCODE_OTLP_ENDPOINT=http://localhost:4317
opencode
# Disable a single metric
export OPENCODE_DISABLE_METRICS="retry.count"

# Disable multiple metrics
export OPENCODE_DISABLE_METRICS="cache.count,session.duration,session.token.total,session.cost.total,model.usage,retry.count,message.count"
```

#### opencode-only metrics

The following metrics are specific to opencode and have no equivalent in Claude Code's built-in monitoring. If you are using a Claude Code dashboard and want to avoid cluttering it with opencode-only metrics, you can disable them:

```bash
export OPENCODE_DISABLE_METRICS="cache.count,session.duration,session.token.total,session.cost.total,model.usage,retry.count,message.count"
```

| Metric suffix | Why it's opencode-only |
|---------------|------------------------|
| `cache.count` | Tracks cache read/write activity as occurrence counts — not a Claude Code signal |
| `session.duration` | Session wall-clock duration — not emitted by Claude Code |
| `session.token.total` | Per-session token histogram — not emitted by Claude Code |
| `session.cost.total` | Per-session cost histogram — not emitted by Claude Code |
| `model.usage` | Per-model message counter — not emitted by Claude Code |
| `retry.count` | API retry counter — not emitted by Claude Code |
| `message.count` | Completed message counter — not emitted by Claude Code |

### Datadog example

```bash
Expand Down
38 changes: 19 additions & 19 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,22 +1,4 @@
{
"name": "@devtheops/opencode-plugin-otel",
"version": "0.3.0",
"module": "src/index.ts",
"main": "src/index.ts",
"type": "module",
"repository": {
"type": "git",
"url": "https://github.com/DEVtheOPS/opencode-plugin-otel.git"
},
"files": [
"src/"
],
"devDependencies": {
"@types/bun": "latest"
},
"scripts": {
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@opencode-ai/plugin": "^1.2.23",
"@opencode-ai/sdk": "^1.2.23",
Expand All @@ -28,5 +10,23 @@
"@opentelemetry/sdk-metrics": "^2.6.0",
"@opentelemetry/semantic-conventions": "^1.40.0",
"typescript": "^5.9.3"
}
},
"devDependencies": {
"@types/bun": "latest"
},
"files": [
"src/"
],
"main": "src/index.ts",
"module": "src/index.ts",
"name": "@devtheops/opencode-plugin-otel",
"repository": {
"type": "git",
"url": "https://github.com/DEVtheOPS/opencode-plugin-otel.git"
},
"scripts": {
"typecheck": "tsc --noEmit"
},
"type": "module",
"version": "0.3.0"
}
49 changes: 38 additions & 11 deletions release-please-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,45 @@
"bootstrap-sha": "b7bf72d1b8ee652acb714431087d24b475ca6460",
"packages": {
".": {
"release-type": "node",
"package-name": "@devtheops/opencode-plugin-otel",
"changelog-sections": [
{ "type": "feat", "section": "Features" },
{ "type": "fix", "section": "Bug Fixes" },
{ "type": "perf", "section": "Performance Improvements" },
{ "type": "refactor", "section": "Code Refactoring" },
{ "type": "docs", "section": "Documentation" },
{ "type": "ci", "section": "Continuous Integration", "hidden": true },
{ "type": "build", "section": "Build System", "hidden": true },
{ "type": "chore", "section": "Miscellaneous Chores", "hidden": true }
]
{
"section": "Features",
"type": "feat"
},
{
"section": "Bug Fixes",
"type": "fix"
},
{
"section": "Performance Improvements",
"type": "perf"
},
{
"section": "Code Refactoring",
"type": "refactor"
},
{
"section": "Documentation",
"type": "docs"
},
{
"hidden": true,
"section": "Continuous Integration",
"type": "ci"
},
{
"hidden": true,
"section": "Build System",
"type": "build"
},
{
"hidden": true,
"section": "Miscellaneous Chores",
"type": "chore"
}
],
"package-name": "@devtheops/opencode-plugin-otel",
"release-type": "node"
}
}
}
Loading
Loading