AlexandrAI AGENTS.md
agents-md

Claude Usage Cost Extension Agent Guide

Project-specific AGENTS.md guidance for the claude-usage-cost VS Code extension, covering architecture boundaries, local-only privacy rules, parser invariants, pricing updates, and verification commands.

AGENTS.md

Guidance for coding agents working on claude-usage-cost. Humans can start with README.md; this guide captures the build, validation, architecture, and safety rules agents need before changing the VS Code extension.

Project Overview

claude-usage-cost is a VS Code extension that estimates Claude Code token usage and API-equivalent USD cost from local Claude Code transcript JSONL files. It is local-only: it parses existing transcript logs, computes per-model and per-token-category aggregates, and renders a status bar plus dashboard. It should not call external APIs or require API keys.

The extension contributes two commands:

  • Claude Usage: Show Cost Dashboard via claudeUsageCost.showDashboard
  • Claude Usage: Refresh (rescan transcripts) via claudeUsageCost.refresh

The extension activates on onStartupFinished, scans the configured transcript directory, caches aggregate state under VS Code global storage, and updates through file watching plus a polling fallback.

Repository Layout

PathPurpose
src/extension.tsVS Code host integration: activation, status bar, settings, webview, scanning lifecycle, state save/restore.
src/parser.tsPlain Node transcript scanner and aggregator. No vscode import; keep it independently testable.
src/pricing.tsPlain Node pricing resolver and cost calculator. Built-in rates plus user overrides.
src/types.tsShared TypeScript interfaces for prices, token counts, buckets, dashboard payloads, and session/cap views.
src/selftest.tsStandalone verification entry that runs the same parser/pricing path against a transcript directory and prints JSON.
media/dashboard.jsBrowser-side webview rendering for cards, charts, cap gauge, sessions, model table, and warnings.
media/dashboard.cssWebview styling.
esbuild.jsBundles src/extension.ts to out/extension.js; keeps vscode external.
out/Generated build output. Do not edit by hand.

Setup Commands

Install dependencies:

bashnpm install

Build the extension bundle:

bashnpm run compile

Run the TypeScript typecheck:

bashnpm run check-types

Create a VSIX package:

bashnpm run package

Start an esbuild watch loop while editing:

bashnpm run watch

Verification

There is no dedicated npm test script in package.json. Before publishing or claiming a change is ready, run at least:

bashnpm run check-types
npm run compile

For parser/pricing changes, also bundle and run the standalone selftest:

bashnpx esbuild src/selftest.ts --bundle --platform=node --format=cjs --outfile=out/selftest.js
node out/selftest.js <transcript-dir>

Use a redacted or disposable transcript fixture when sharing output. Never paste real transcript content, local usernames, private project names, or exact local transcript paths into public issues, docs, screenshots, or archive items.

Architecture Rules

Keep the host/webview/parser boundaries intact:

  • src/extension.ts may import vscode, manage the status bar, create the webview, read VS Code settings, and schedule scans.
  • src/parser.ts, src/pricing.ts, and src/types.ts must stay free of vscode imports so they can run under plain Node and be exercised by src/selftest.ts.
  • media/dashboard.js receives already-aggregated data from the extension webview bridge. Do not move transcript parsing or filesystem access into the webview.
  • esbuild.js must keep vscode in external; the VS Code host provides it at runtime.

The scanning path is designed for large transcript trees. UsageStore reads JSONL files in chunks, tracks offsets for incremental scans, stores partial-line leftovers, and yields during full scans so the extension host stays responsive. Preserve this streaming design when changing parsing behavior.

Usage Accounting Rules

The core billing invariant is: count each billable Anthropic message.id once when dedupe is enabled.

Important parser behavior:

  • Only lines containing "usage" are parsed as JSON.
  • Zero-token stream-start stubs are ignored and must not claim the message id.
  • If the same message id appears more than once, keep the larger token snapshot and remove the smaller contribution from its bucket.
  • Buckets are keyed by model and hour.
  • rawTokens counts every copy before dedupe; billed totals come from deduped buckets.
  • STATE_VERSION controls restore compatibility for serialized parser state. Bump it when the serialized shape changes.

Do not replace this with a naive file-total sum. The README documents that duplicate transcript copies can materially overcount usage.

Pricing Rules

Pricing lives in src/pricing.ts and is expressed as USD per one million tokens. Keep category splits explicit:

  • input
  • output
  • 5-minute cache write
  • 1-hour cache write
  • cache read

User overrides come from claudeUsageCost.pricingOverride; longest model-id substring matches should keep winning. If built-in rates change, update:

  • PRICING_AS_OF
  • BUILTIN_PRICES
  • the README pricing table and accuracy notes

For unrecognized model ids, preserve the current estimated-family behavior and warning surface. Unknown pricing should be visible to the dashboard rather than silently treated as authoritative.

Dashboard And Webview Rules

extension.ts creates a webview with a strict Content Security Policy. Keep the nonce-based script policy and avoid external scripts, fonts, images, or network resources.

Dashboard rendering should continue to be data-driven from DashboardData:

  • summary cards
  • monthly soft-cap gauge
  • cost and token category splits
  • daily trend
  • reconstructed five-hour sessions
  • per-model table
  • warnings for missing transcripts, estimated pricing, or unpriced models

When adding fields, update src/types.ts, UsageStore.aggregate(), and media/dashboard.js together. Keep browser-side string escaping for dynamic text.

Configuration Rules

Settings are declared in package.json under contributes.configuration. If a setting changes, update README and ensure extension.ts reacts to configuration changes when needed.

Current settings include:

  • claudeUsageCost.transcriptDir
  • claudeUsageCost.autoRefresh
  • claudeUsageCost.dedupe
  • claudeUsageCost.statusBarMetric
  • claudeUsageCost.pricingOverride
  • claudeUsageCost.includedMonthlyAllowanceUsd
  • claudeUsageCost.softCapLinesUsd

The empty transcript directory setting resolves to the default Claude Code transcript location through os.homedir() at runtime. In public docs or reports, describe that as the default Claude transcript directory or <claude-projects-dir> instead of publishing any real local path.

Security And Privacy Rules

This extension's privacy promise is local computation. Preserve it.

  • Do not add telemetry, analytics, API calls, external pricing fetches, or network-backed dashboards.
  • Do not log raw transcript lines or prompt/response content.
  • Do not show raw transcript content in the webview.
  • Do not expose real transcript paths in public documentation beyond repo-relative code references and neutral placeholders.
  • Treat transcript JSONL as sensitive local data even though this extension reads only usage fields.
  • Keep webview content self-contained and CSP-restricted.

When debugging, prefer aggregate counts and synthetic transcript fixtures. If a real user transcript is needed, keep it local and redact every path, username, project name, and message body before sharing.

Code Style

The project uses TypeScript with strict: true, CommonJS modules, ES2022 target, and Node-style imports. Follow the existing direct style:

  • Keep parser/pricing functions small and explicit.
  • Prefer plain objects and typed interfaces from src/types.ts.
  • Avoid unnecessary dependencies; the extension already bundles with esbuild.
  • Keep generated output in out/ out of manual edits.
  • Keep comments short and reserved for non-obvious parser or host-boundary behavior.

Common Change Patterns

When adding a new pricing tier:

  1. Add or adjust entries in BUILTIN_PRICES.
  2. Update PRICING_AS_OF.
  3. Update README pricing documentation.
  4. Run npm run check-types and npm run compile.
  5. Run selftest against a small fixture or local redacted transcript directory.

When changing parser logic:

  1. Preserve chunked reading and incremental offsets.
  2. Preserve dedupe upgrade behavior for repeated message ids.
  3. Validate both dedupe-on and dedupe-off paths if relevant.
  4. Check dashboard warnings still make sense.
  5. Run npm run check-types, npm run compile, and selftest.

When changing dashboard UI:

  1. Keep DashboardData as the single webview input.
  2. Escape dynamic strings in media/dashboard.js.
  3. Keep the CSP compatible with local extension resources only.
  4. Run npm run compile and manually inspect the webview in VS Code when possible.

Packaging Notes

npm run package invokes @vscode/vsce with --allow-missing-repository --skip-license. The README says the project is MIT licensed, but packaging currently skips a license check. If distribution requirements change, add or update the license file and remove the skip flag only after confirming the package still builds.

The extension version is currently 0.1.4 in package.json. Keep README install examples, package output names, and package metadata aligned when bumping versions.

Commit And PR Rules

Before opening a PR or publishing a VSIX:

bashnpm run check-types
npm run compile

Also run the selftest for parser or pricing changes. In the PR summary, call out:

  • whether pricing tables changed
  • whether parser/dedupe behavior changed
  • whether dashboard payload shape changed
  • whether any privacy boundary changed

Do not include real transcript snippets, local paths, screenshots with private project names, or exact usage totals from a private workspace in PR descriptions.