Agent setup: authorize Sight MCP and ask your agent to call get_tracking_setup. It configures this integration automatically.

Claude Code spend hook

A Claude Code Stop hook that tracks what you spend running Claude Code to build your product, and reports it to Sight — where it shows up next to your revenue (AgentSpendCard on the dashboard, get_agent_spend via MCP). This is the primary, automatic path for Sight's "agent build-spend" feature; logging an entry by hand via the track_agent_spend MCP tool is the fallback for corrections.

Part of Sight, split across repos under the sightapp org — see sightapp/sight-backend's SPEC.md for the full picture.

How this works

Claude Code's Stop hook fires once per turn, but its payload does not include a ready-made cost figure — only (per the current docs) session_id, transcript_path, cwd, and sometimes a coarse tokens.input/tokens.output. There is no claude cost/usage CLI command to shell out to either. So this hook:

  1. Reads the turn's real token usage (input/output/cache) and model from transcript_path (a JSONL file Claude Code writes as the session progresses — falls back to the Stop payload's coarser tokens.input/ tokens.output if the transcript hasn't caught up yet, which can happen since it's written asynchronously).
  2. Computes a cost estimate itself, using a local pricing table (src/pricing.js) — Anthropic doesn't hand us a dollar figure, so we derive one from tokens × known per-model rates. Ships with current rates; override without a code change by dropping a ~/.sight/pricing.json with the same shape.
  3. Accumulates fractional cents across turns rather than rounding each one — a single small turn is often well under a cent, and since Stop fires every turn, rounding-to-zero per turn would silently drop real spend for the common case. The leftover fraction persists in ~/.sight/remainder.json and carries forward until it crosses a whole cent.
  4. Buffers locally, batches the network call — appends each turn's entry to ~/.sight/agent-spend.buffer.jsonl and only POSTs to Sight once the buffer hits flushMaxEntries (default 20) or flushMaxAgeMinutes (default 15), whichever comes first. This keeps a hook that fires every single turn from making a network call every single turn. The flush itself runs inside the Stop hook (10-minute timeout budget) rather than SessionEnd (a much tighter ~1.5s shared budget across all SessionEnd hooks) — Stop is the only place with enough headroom for a reliable POST.
  5. On flush failure (network error, non-2xx), the buffer is left intact and retried on the next Stop — nothing is dropped on a transient failure.

Never blocks or fails the Claude Code session: every code path exits 0, errors go to stderr (never stdout — Claude Code treats non-JSON stdout from a Stop hook as text appended to its context, so this hook stays silent on success).

Install

git clone https://github.com/sightapp/sight-claude-code-hook.git

No npm install needed to run it — the hook script itself has zero dependencies (only Node built-ins), specifically so it works reliably inside whatever environment Claude Code invokes hooks in.

1. Configure

Create ~/.sight/config.json:

{
  "apiKey": "sk_your_project_api_key"
}

(Or set SIGHT_API_KEY env var instead — it takes precedence over the config file.)

2. Register the hook

Add to ~/.claude/settings.json (applies to every project) or a specific project's .claude/settings.json:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "node /absolute/path/to/sight-claude-code-hook/bin/stop-hook.js",
            "timeout": 15
          }
        ]
      }
    ]
  }
}

Verify this against the current Claude Code hooks docs before relying on it — the payload shape and cost-derivation approach above are confirmed against current docs, but the exact settings.json registration schema (matcher requirements, env var interpolation like $CLAUDE_PROJECT_DIR, timeout field support) wasn't independently re-verified in the session this hook was built in. If the schema has moved, this is the one place to double-check.

Configuration reference

~/.sight/config.json:

FieldDefaultMeaning
apiKey(required to flush)Project API key
flushMaxEntries20Flush once the buffer reaches this many entries
flushMaxAgeMinutes15...or once the oldest buffered entry is this old

~/.sight/pricing.json (optional, overrides src/pricing.js's defaults):

{
  "models": {
    "claude-opus-5": { "input": 5, "output": 25 }
  },
  "fallback": { "input": 3, "output": 15 }
}

Local files this hook manages

  • ~/.sight/agent-spend.buffer.jsonl — unflushed entries.
  • ~/.sight/remainder.json — fractional-cent carry-over.
  • ~/.sight/config.json / ~/.sight/pricing.json — you create these.

None of these are Claude Code's own files — safe to delete any of them to reset local state (you'll lose unflushed spend for the current buffering window, not historical data already sent to Sight).

Source repository