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
12 changes: 6 additions & 6 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
{
"$schema": "https://anthropic.com/claude-code/marketplace.schema.json",
"name": "promptlayer-claude-plugin",
"version": "0.1.0",
"description": "PromptLayer Claude Code tracing plugin with OTLP/HTTP JSON transport",
"name": "promptlayer-claude-plugins",
"version": "1.0.0",
"description": "Automatically trace Claude Code conversations to PromptLayer. Captures sessions, user messages, assistant responses, and tool calls for observability.",
"owner": {
"name": "PromptLayer",
"email": "support@promptlayer.com"
},
"plugins": [
{
"name": "pl-trace-claude-code",
"description": "Trace Claude Code sessions and send OTLP/HTTP JSON spans to PromptLayer.",
"source": "./plugins/pl-trace-claude-code",
"name": "trace",
"description": "Automatically trace Claude Code conversations to PromptLayer. Captures user messages, assistant responses, and tool calls as structured spans.",
"source": "./plugins/trace",
"category": "observability"
}
]
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ validate:
./scripts/validate-manifests.sh

lint:
if command -v shellcheck >/dev/null 2>&1; then shellcheck -x plugins/pl-trace-claude-code/hooks/*.sh plugins/pl-trace-claude-code/setup.sh scripts/*.sh; else echo "shellcheck not installed, skipping"; fi
if command -v shfmt >/dev/null 2>&1; then shfmt -d plugins/pl-trace-claude-code/hooks/*.sh plugins/pl-trace-claude-code/setup.sh scripts/*.sh; else echo "shfmt not installed, skipping"; fi
if command -v shellcheck >/dev/null 2>&1; then shellcheck -x plugins/trace/hooks/*.sh plugins/trace/setup.sh scripts/*.sh; else echo "shellcheck not installed, skipping"; fi
if command -v shfmt >/dev/null 2>&1; then shfmt -d plugins/trace/hooks/*.sh plugins/trace/setup.sh scripts/*.sh; else echo "shfmt not installed, skipping"; fi

test: validate lint
./scripts/replay-fixtures.sh
Expand Down
89 changes: 46 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,73 +1,76 @@
# PromptLayer Claude Code Plugin Marketplace
# PromptLayer Claude Code Plugin

Open source Claude Code plugin marketplace for PromptLayer tracing.
See every Claude Code session as structured traces in [PromptLayer](https://promptlayer.com) — user messages, assistant responses, tool calls, and token usage, all captured automatically.

## Plugin
## Quick Start

- `pl-trace-claude-code`: traces Claude Code sessions and emits OTLP/HTTP JSON spans to PromptLayer.

## Prerequisites

- Claude Code CLI installed
- PromptLayer API key (`PROMPTLAYER_API_KEY`)
- `jq`, `curl`, `uuidgen`, and `python3` available in your shell

## Install
### 1. Install

```bash
claude plugin marketplace add promptlayer/promptlayer-claude-plugin
claude plugin install pl-trace-claude-code@promptlayer-claude-plugin
claude plugin marketplace add promptlayer/promptlayer-claude-plugins
claude plugin install trace@promptlayer-claude-plugins
```

## Configure
### 2. Configure

Run the setup script from the project where you want tracing enabled:

```bash
$HOME/.claude/plugins/marketplaces/promptlayer-claude-plugin/plugins/pl-trace-claude-code/setup.sh
$HOME/.claude/plugins/marketplaces/promptlayer-claude-plugins/plugins/trace/setup.sh
```

The script writes `.claude/settings.local.json` with:
You will be prompted for your PromptLayer API key. You can find or create one at [dashboard.promptlayer.com](https://dashboard.promptlayer.com).

- `TRACE_TO_PROMPTLAYER=true`
- `PROMPTLAYER_API_KEY`
- `PROMPTLAYER_OTLP_ENDPOINT` (default: `https://api.promptlayer.com/v1/traces`)
- `PROMPTLAYER_CC_DEBUG`
### 3. Verify

## Verify
1. Start Claude Code: `claude`
2. Send a prompt
3. Check your traces at [dashboard.promptlayer.com](https://dashboard.promptlayer.com)

1. Start Claude Code in the configured directory: `claude`
2. Send a prompt and use at least one tool
3. Check hook logs: `tail -f ~/.claude/state/promptlayer_hook.log`
## What Gets Traced

## Troubleshooting
The plugin hooks into Claude Code's lifecycle and emits [OTLP/HTTP JSON](docs/otlp-mapping.md) spans for:

See [docs/troubleshooting.md](docs/troubleshooting.md).
- **Sessions** — one root span per Claude Code session
- **LLM calls** — model, token counts, prompts, and completions
- **Tool calls** — tool name, input, and output

## Local Development
## Prerequisites

- Claude Code CLI installed
- A PromptLayer account and API key ([dashboard.promptlayer.com](https://dashboard.promptlayer.com))
- `jq`, `curl`, `uuidgen`, and `python3` available in your shell

Install from this local repo and run setup:
## Configuration

```bash
make dev
```
The setup script writes `~/.claude/settings.json` with:

`make dev` symlinks this repo as the marketplace source, installs `pl-trace-claude-code`, and runs the setup script.
| Variable | Purpose | Default |
|----------|---------|---------|
| `TRACE_TO_PROMPTLAYER` | Enable/disable tracing | `true` |
| `PROMPTLAYER_API_KEY` | Your API key | _(required)_ |
| `PROMPTLAYER_OTLP_ENDPOINT` | OTLP ingestion endpoint | `https://api.promptlayer.com/v1/traces` |
| `PROMPTLAYER_CC_DEBUG` | Enable debug logging | `false` |

Remove the local plugin install and cleanup local artifacts:
Because this file lives in your home directory, your API key stays out of version control and tracing is enabled across all projects.

```bash
make uninstall
```
## OTLP-Native Tracing

Run validation + lint + fixture replay:
This plugin is **OpenTelemetry (OTLP/HTTP JSON)** compatible:

```bash
make test
```
- **Open standard** — traces follow the [OTLP specification](https://opentelemetry.io/docs/specs/otlp/), not a vendor-specific format
- **Portable** — swap or fan-out to any OTLP-compatible backend (Datadog, Honeycomb, Grafana Tempo, etc.) by changing one endpoint URL
- **No SDK lock-in** — the plugin uses plain `curl` to send `ExportTraceServiceRequest` payloads; no proprietary client libraries required

## Troubleshooting

Run end-to-end SDK smoke test (requires `ANTHROPIC_API_KEY`):
See [docs/troubleshooting.md](docs/troubleshooting.md).

## Local Development

```bash
make smoke
make dev # Symlink repo as marketplace source, install plugin, run setup
make uninstall # Remove local install and cleanup artifacts
make test # Validate manifests + lint + fixture replay
make smoke # E2E smoke test (requires ANTHROPIC_API_KEY)
```
8 changes: 0 additions & 8 deletions plugins/pl-trace-claude-code/.claude-plugin/plugin.json

This file was deleted.

8 changes: 8 additions & 0 deletions plugins/trace/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "trace",
"description": "Automatically trace Claude Code conversations to PromptLayer. Captures user messages, assistant responses, and tool calls as structured spans.",
"version": "1.0.0",
"author": {
"name": "PromptLayer"
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/bin/bash
# Shared runtime helpers for pl-trace-claude-code hooks.
# Shared runtime helpers for trace plugin hooks.

set -euo pipefail
umask 077
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=plugins/pl-trace-claude-code/hooks/lib.sh
# shellcheck source=plugins/trace/hooks/lib.sh
source "$SCRIPT_DIR/lib.sh"

tracing_enabled || exit 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=plugins/pl-trace-claude-code/hooks/lib.sh
# shellcheck source=plugins/trace/hooks/lib.sh
source "$SCRIPT_DIR/lib.sh"

tracing_enabled || exit 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=plugins/pl-trace-claude-code/hooks/lib.sh
# shellcheck source=plugins/trace/hooks/lib.sh
source "$SCRIPT_DIR/lib.sh"

tracing_enabled || exit 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=plugins/pl-trace-claude-code/hooks/lib.sh
# shellcheck source=plugins/trace/hooks/lib.sh
source "$SCRIPT_DIR/lib.sh"

tracing_enabled || exit 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=plugins/pl-trace-claude-code/hooks/lib.sh
# shellcheck source=plugins/trace/hooks/lib.sh
source "$SCRIPT_DIR/lib.sh"

tracing_enabled || exit 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ if [[ -n "$default_key" ]]; then
echo ""
api_key="${input_key:-$default_key}"
else
echo "You can find or create an API key at: https://dashboard.promptlayer.com"
read -r -s -p "Enter PROMPTLAYER_API_KEY (input hidden): " input_key
echo ""
api_key="$input_key"
Expand Down Expand Up @@ -156,8 +157,8 @@ else
debug="false"
fi

mkdir -p .claude
settings_file=".claude/settings.local.json"
mkdir -p "$HOME/.claude"
settings_file="$HOME/.claude/settings.json"

if [[ -f "$settings_file" ]] && ! jq empty "$settings_file" >/dev/null 2>&1; then
echo "Error: $settings_file exists but is not valid JSON."
Expand Down Expand Up @@ -196,4 +197,6 @@ echo "Setup complete."
echo "Next:"
echo " 1. Start Claude Code in this directory: claude"
echo " 2. Run one prompt and tool call"
echo " 3. Inspect logs: tail -f ~/.claude/state/promptlayer_hook.log"
echo " 3. View your traces at: https://dashboard.promptlayer.com"
echo ""
echo "Debug logs: tail -f ~/.claude/state/promptlayer_hook.log"
6 changes: 3 additions & 3 deletions scripts/dev-install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
set -euo pipefail

REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
TARGET="$HOME/.claude/plugins/marketplaces/promptlayer-claude-plugin"
PLUGIN="pl-trace-claude-code@promptlayer-claude-plugin"
SETUP_SCRIPT="$TARGET/plugins/pl-trace-claude-code/setup.sh"
TARGET="$HOME/.claude/plugins/marketplaces/promptlayer-claude-plugins"
PLUGIN="trace@promptlayer-claude-plugins"
SETUP_SCRIPT="$TARGET/plugins/trace/setup.sh"

mkdir -p "$(dirname "$TARGET")"
rm -rf "$TARGET"
Expand Down
4 changes: 2 additions & 2 deletions scripts/dev-uninstall.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#!/bin/bash
set -euo pipefail

PLUGIN="pl-trace-claude-code@promptlayer-claude-plugin"
MARKETPLACE="promptlayer-claude-plugin"
PLUGIN="trace@promptlayer-claude-plugins"
MARKETPLACE="promptlayer-claude-plugins"

for scope in user project local; do
claude plugin uninstall --scope "$scope" "$PLUGIN" >/dev/null 2>&1 || true
Expand Down
4 changes: 2 additions & 2 deletions scripts/e2e_smoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# ]
# ///

"""End-to-end smoke test for pl-trace-claude-code via Claude Agent SDK.
"""End-to-end smoke test for trace plugin via Claude Agent SDK.

The smoke test launches a local OTLP HTTP collector, runs a short Claude session
with the plugin enabled, then asserts that OTLP payloads were actually received.
Expand All @@ -28,7 +28,7 @@


REPO_ROOT = Path(__file__).resolve().parents[1]
PLUGIN_PATH = REPO_ROOT / "plugins" / "pl-trace-claude-code"
PLUGIN_PATH = REPO_ROOT / "plugins" / "trace"


class _CollectorHandler(BaseHTTPRequestHandler):
Expand Down
2 changes: 1 addition & 1 deletion scripts/replay-fixtures.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ export PROMPTLAYER_API_KEY="pl_test_key"
export PROMPTLAYER_OTLP_ENDPOINT="http://127.0.0.1:9/v1/traces"

# Expect failure to send (no server), but script should still run.
cat plugins/pl-trace-claude-code/testdata/session_start_input.json | bash plugins/pl-trace-claude-code/hooks/session_start.sh || true
cat plugins/trace/testdata/session_start_input.json | bash plugins/trace/hooks/session_start.sh || true

echo "Fixture replay completed"
4 changes: 2 additions & 2 deletions scripts/validate-manifests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ validate_json() {
}

validate_json .claude-plugin/marketplace.json
validate_json plugins/pl-trace-claude-code/.claude-plugin/plugin.json
validate_json plugins/pl-trace-claude-code/hooks/hooks.json
validate_json plugins/trace/.claude-plugin/plugin.json
validate_json plugins/trace/hooks/hooks.json

echo "Manifest validation passed"
Loading