paddington 0.6.0

A fast status line and cost tracker for Claude Code and Pi
# paddington

A fast status line and cost tracker for [Claude Code](https://docs.anthropic.com/en/docs/claude-code) and [Pi](https://pi.dev), written in Rust.

Reads JSON status input from stdin and outputs a compact, color-coded multi-line status bar showing:

- **Line 1** — Project path, git repo/branch, worktree, PR status, session name
- **Line 2** — Model name, context window usage
- **Line 3** — Session cost (with monthly total and optional budget tracking), duration, input/output token breakdown (when available), lines changed

Session cost data is persisted to a local SQLite database, enabling monthly analytics with breakdowns by day, model, and project.

## Install

```sh
cargo install paddington
```

## Usage

### With Claude Code

Configure as your Claude Code status line command in `~/.claude/settings.json`:

```json
{
  "statusLine": {
    "type": "command",
    "command": "~/.cargo/bin/paddington",
    "padding": 3
  }
}
```

### With Pi

Paddington integrates natively into [pi](https://pi.dev) as a footer extension, replacing pi's default footer with paddington's status bar. Other extensions' `ctx.ui.setStatus()` text is preserved as an appended line, so paddington's footer doesn't silently hide it.

1. Ensure paddington is installed via cargo or available in your `$PATH`.
2. Copy the extension to your global pi extensions directory:

   ```sh
   mkdir -p ~/.pi/agent/extensions
   cp contrib/pi/paddington.ts ~/.pi/agent/extensions/
   ```

The extension collects session data from pi's APIs (model, tokens, cost, cache stats, context usage, git info) and pipes it to the paddington binary. It updates on model changes, turn completions, thinking level changes, and session events.

**Pi-specific features:**

- Provider name and reasoning/thinking level
- Cache token stats (read, write, hit rate)
- Session duration computed from message timestamps
- Lines added/removed parsed from edit tool patches
- Git branch from pi's footer data (no extra shell calls)

If you are developing paddington, opening pi in this repository will automatically use the local debug/release build.

## Custom Templates

Paddington's output is fully customizable via a [MiniJinja](https://github.com/mitsuhiko/minijinja) template in `$XDG_CONFIG_HOME/paddington/config.toml` (defaults to `~/.config/paddington/config.toml`):

```toml
[format]
template = """
{{ blue }}{{ path_raw }}{{ reset }} {{ yellow }}{{ branch_raw }}{{ reset }} {{ git_dirty }}
{{ model }} {{ gray }}[${{ cost_raw }}]{{ reset }}"""
```

Every variable has two forms:

- **Auto-colored** (`{{ model }}`) — wraps the value in its default ANSI color
- **Raw** (`{{ model_raw }}`) — plain text, use with explicit color tags for custom coloring

### Available Variables

| Variable | Description | Default Color |
| --- | --- | --- |
| `path` | cwd relative to project root | blue |
| `repo_owner`, `repo_name` | GitHub owner/repo | yellow |
| `branch` | git branch or short SHA | yellow |
| `worktree` | worktree name | blue |
| `pr_number`, `pr_state` | PR number and review symbol (✓/✗/~) | magenta |
| `session_name` | session name | magenta |
| `model` | model display name | green |
| `model_id` | raw model identifier | gray |
| `context_used`, `context_total`, `context_pct` | context window (in k / %) | gray |
| `cost`, `monthly_cost` | session / monthly cost (USD, 2 decimal) | yellow |
| `duration` | session duration | gray |
| `lines_added`, `lines_removed` | lines changed | green / red |
| `project_name` | project directory basename | blue |
| `session_id` | session identifier | gray |
| `hostname`, `username` | machine / OS user | gray |
| `datetime` | current time (HH:MM) | gray |
| `paddington_version` | version string | gray |
| `git_dirty` | "dirty" if uncommitted changes | yellow |
| `budget_limit` | configured monthly budget limit | gray |
| `budget_remaining` | dollars remaining in budget (floors at 0) | green/yellow/red |
| `budget_pct` | percentage of budget used (uncapped) | green/yellow/red |

#### Pi-Specific Variables

These are populated when running under pi (via the extension or when `agent` is set to `"pi"`):

| Variable | Description | Default Color |
| --- | --- | --- |
| `provider` | model provider (e.g. `anthropic`, `openai`) | gray |
| `reasoning_level` | thinking level (e.g. `off`, `high`, `max`) | magenta |
| `agent` | calling agent (`pi` or `claude-code`) | gray |
| `is_pi` | boolean, `true` when agent is pi ||
| `cache_read` | cache read tokens (e.g. `145k`) | gray |
| `cache_write` | cache write tokens (e.g. `23k`) | gray |
| `cache_hit_rate` | cache hit rate percentage | gray |
| `tokens_input` | cumulative input tokens (e.g. `230k`) | gray |
| `tokens_output` | cumulative output tokens (e.g. `26k`) | gray |

Color tags: `{{ blue }}`, `{{ yellow }}`, `{{ green }}`, `{{ red }}`, `{{ magenta }}`, `{{ gray }}`, `{{ reset }}`

#### Example: Pi-Optimized Template

```toml
[format]
template = """{{ blue }}{{ path_raw }}{{ reset }}\
{% if repo_owner_raw and repo_name_raw %} {{ yellow }}{{ repo_owner_raw }}/{{ repo_name_raw }}\
{% if branch_raw %}:{{ branch_raw }}{% endif %}{{ reset }}{% elif branch_raw %} {{ yellow }}{{ branch_raw }}{{ reset }}{% endif %}\
{% if session_name_raw %} {{ gray }}|{{ reset }} {{ magenta }}{{ session_name_raw }}{{ reset }}{% endif %}
{{ green }}{{ model_raw }}{{ reset }}\
{% if provider_raw %} {{ gray }}({{ provider_raw }}){{ reset }}{% endif %}\
{% if reasoning_level_raw %} {{ magenta }}[{{ reasoning_level_raw }}]{{ reset }}{% endif %}\
{% if context_total_raw and context_pct_raw %} {{ gray }}[{{ context_used_raw }}k/{{ context_total_raw }}k ({{ context_pct_raw }}% used)]{{ reset }}{% endif %}
{% if cost_raw %}{{ yellow }}${{ cost_raw }}{{ reset }}{% if budget_limit_raw %} {{ gray }}(${{ monthly_cost_raw }}/${{ budget_limit_raw }}){{ reset }}{% elif monthly_cost_raw %} {{ gray }}(${{ monthly_cost_raw }}/mo){{ reset }}{% endif %}{% endif %}\
{% if duration_raw %}{% if cost_raw %} {{ gray }}·{{ reset }}{% endif %} {{ gray }}{{ duration_raw }}{{ reset }}{% endif %}\
{% if tokens_input_raw %} {{ gray }}· ↑{{ tokens_input_raw }} ↓{{ tokens_output_raw }}{{ reset }}{% endif %}\
{% if cache_read_raw %} {{ gray }}R{{ cache_read_raw }} W{{ cache_write_raw }} CH{{ cache_hit_rate_raw }}%{{ reset }}{% endif %}\
{% if lines_added_raw or lines_removed_raw %}{% if cost_raw or duration_raw %} {{ gray }}·{{ reset }}{% endif %} {{ green }}+{{ lines_added_raw }}{{ reset }}{{ gray }}/{{ reset }}{{ red }}-{{ lines_removed_raw }}{{ reset }} {{ gray }}lines{{ reset }}{% endif %}"""
```

### Budget Tracking

Set a monthly spending limit in your config file to see budget status in the status line and `stats` output:

```toml
[budget]
monthly_limit = 500.00
```

The status line shows spend vs. limit: `($42.00/$500.00)`. Colors shift from green to yellow (80% used) to red (95% used).

### Validate & Preview

```sh
paddington check
```

Validates your config file and renders a preview with mock data — no live session needed.

If no config file exists, paddington uses a built-in default template that matches the standard output. If the config has errors, a warning is shown in the status line and the default template is used as a fallback.

## Analytics

### Monthly Stats

View cost breakdowns by day, model, and project:

```sh
paddington stats                              # current month
paddington stats --month 2026-07              # specific month
paddington stats --compare 2026-06            # compare current vs another month
paddington stats --month 2026-07 --compare 2026-05  # compare two specific months
```

### Session History

Browse recent sessions:

```sh
paddington history            # last 20 sessions
paddington history -n 50      # last 50
paddington history -p myproj  # filter by project
```

Data is stored at `$XDG_DATA_HOME/paddington/sessions.db` (defaults to `~/.local/share/paddington/sessions.db`).

## Build

```sh
cargo build --release
```

The release profile is configured with LTO and symbol stripping for a small binary.

## Credits

Heavily inspired by [Starship](https://starship.rs/)