radar-cc
v0.1.5
Published
Non-blocking intent alignment checker for Claude Code, powered by OpenTelemetry
Maintainers
Readme
radar-cc
Intent alignment checker for Claude Code. Radar watches Claude Code's OpenTelemetry stream and tells you — in a second terminal pane — whether your prompt was clear before Claude starts, and whether Claude stayed on target after it finishes.
Requirements
- Node.js >= 22
- An Anthropic API key
Install
npm install -g radar-cc
radar setupradar setup writes the required OTel environment variables to ~/.claude/settings.json and walks you through storing your Anthropic API key — either on local disk or in 1Password. Restart Claude Code after running it.
You can also pass the key directly to skip the prompt:
radar setup --api-key <your-key>Usage
Open a second terminal pane alongside Claude Code and run:
radar watchSend prompts in your Claude Code pane as normal. Radar listens passively on localhost:4820.
Output
── Radar v0.1.0 ─────────────────────────────────────
Listening on localhost:4820
Waiting for Claude Code telemetry...
─────────────────────────────────────────────────────
── PRE ── 14:23:07 ── score: 0.34 ── ✓ Clear ────────
── PRE ── 14:25:12 ── score: 0.78 ───────────────────
⚠ "clean up this module" is ambiguous.
Claude will likely restructure imports and rename functions.
Did you mean: remove the 3 commented-out functions?
→ Try: "delete the dead code in auth.ts — the 3 commented
functions at the bottom"
─────────────────────────────────────────────────────
── POST ── 14:25:38 ──────────────────────────────────
✓ Response aligned with intent.
Tools: Edit (2 files) · 847 tokens · $0.003
─────────────────────────────────────────────────────
── POST ── 14:31:02 ──────────────────────────────────
✗ Scope exceeded likely intent.
Claude ran Edit on 5 files, Bash (3 commands), 12k tokens, $0.08.
Developer likely wanted: coverage for 2 new edge cases only.
→ "undo all changes. add test cases for the null input
and timeout edge cases in processOrder — nothing else"
─────────────────────────────────────────────────────PRE advisories fire within ~2 seconds of your prompt. POST advisories fire after Claude's turn ends, based on tool activity, cost, and a Haiku-generated summary of Claude's actual response text.
How it works
Setup: radar watch starts an HTTP server on port 4820. Claude Code streams OpenTelemetry events to it.
When you submit a prompt:
- Claude Code emits a
user_promptOTel event → Radar opens a new turn context - Radar immediately calls Haiku to score ambiguity (0–1). Result cached on the turn context
- Score < 0.6 → suppress. Score ≥ 0.6 → call Haiku again to generate a warning, print yellow pre-advisory box
- Meanwhile, Claude Code is running — tool results and API costs stream in and accumulate on the turn context
When Claude finishes:
- The Stop hook fires, reads Claude Code's JSONL transcript (
~/.claude/projects/**/<session_id>.jsonl) to extract the last assistant response, POSTs{ sessionId, lastAssistantMessage }to Radar - Radar waits 3.5s for straggling OTel events, then closes the turn using the cached ambiguity score from step 2
- Score < 0.6 → skip post-advisory entirely (no further Haiku calls)
- Score ≥ 0.6 → call Haiku to summarise the assistant response, then call Haiku to judge whether it aligned with the original intent
- Aligned → dim one-liner (suppressed in alert-only mode). Misaligned → red box with a re-prompt suggestion
- Full turn written to
~/.config/radar/history/YYYY-MM-DD.jsonlfor history and future review
Next prompt: the classifier receives the last 3 turns as context, so references like "do the same for the tests" score correctly rather than looking vague in isolation.
Claude is never blocked or interrupted.
OTel variables (set by radar setup)
| Variable | Value |
|---|---|
| CLAUDE_CODE_ENABLE_TELEMETRY | 1 |
| OTEL_LOGS_EXPORTER | otlp |
| OTEL_EXPORTER_OTLP_PROTOCOL | http/json |
| OTEL_EXPORTER_OTLP_LOGS_ENDPOINT | http://localhost:4820/v1/logs |
| OTEL_LOG_USER_PROMPTS | 1 |
| OTEL_LOG_TOOL_DETAILS | 1 |
| OTEL_LOGS_EXPORT_INTERVAL | 2000 |
OTEL_LOG_USER_PROMPTS=1 is required for prompt content analysis. Without it, Radar can detect turn boundaries but cannot analyze intent.
These are written to the env block in ~/.claude/settings.json. If Radar is not running, Claude Code silently ignores them.
Options
radar setup [options]
-k, --api-key <key> API key to store (skips the interactive prompt)
radar watch [options]
-p, --port <number> OTLP listener port (default: 4820)
-t, --timeout <ms> Turn boundary silence window (default: 5000)
-s, --threshold <score> Ambiguity score threshold 0.0–1.0 (default: 0.6)
-k, --api-key <key> Anthropic API key (overrides all stored sources)Developing locally
Switch from the global install to a local build
- Uninstall the global package:
npm uninstall -g radar-cc - Install dependencies and build:
npm install npm run build - Link the local build as the global
radarbinary:
From now on,npm linkradarpoints todist/cli/index.jsin this repo. Re-runnpm run buildafter any code change — no re-linking needed.
Run setup and tests
- Run setup to (re-)install the hook scripts:
Confirm you see both of these lines in the output:radar setup✓ Hook script written to ~/.radar/hooks/stop.sh ✓ Extract script written to ~/.radar/hooks/extract-response.py - Restart Claude Code so the new Stop hook takes effect.
- Run the test suite:
npm test
Verify the pipeline end-to-end
- Check the installed hook version:
head -2 ~/.radar/hooks/stop.sh # should print: # radar-hook-v2 - Confirm the extract script is present:
ls ~/.radar/hooks/extract-response.py - Manually test the extract script against a real transcript:
# Find a session transcript (created after Claude Code runs with OTel enabled) ls ~/.claude/projects/ python3 ~/.radar/hooks/extract-response.py \ ~/.claude/projects/<folder>/<session-id>.jsonl # Should print a JSON-encoded string of the last assistant response - With
radar watchrunning, manually POST a stop payload to confirm the endpoint accepts the new field:curl -s -X POST http://localhost:4820/v1/hook/stop \ -H "Content-Type: application/json" \ -d '{"sessionId":"test-123","lastAssistantMessage":"I refactored the auth module."}' | cat # Should return: {"ok":true} - Send a prompt in Claude Code and watch the
radar watchpane. The POST advisory should now reflect what Claude actually said, not just which tools it used.
License
MIT
