Skip to content

Commit 4bd91c1

Browse files
lroolleclaude
andcommitted
polish: CI, contributing guide, README glow-up
- GitHub Actions workflow for bats tests (ubuntu-latest) - CONTRIBUTING.md: quick start, guidelines, test commands, bug report template - README restructured for strong first impression: - Badges (tests, version, license) - Feature comparison table vs built-in /statusline - Collapsible sections for themes/styles and OAuth details - Project structure section - Acknowledgments linking official statusline docs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 9f973e0 commit 4bd91c1

3 files changed

Lines changed: 199 additions & 98 deletions

File tree

.github/workflows/test.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
name: tests
2+
on:
3+
push:
4+
branches: [main]
5+
pull_request:
6+
branches: [main]
7+
8+
jobs:
9+
test:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v4
13+
- name: Install dependencies
14+
run: sudo apt-get update && sudo apt-get install -y jq
15+
- name: Install bats
16+
run: npm install -g bats
17+
- name: Run tests
18+
run: bats t/

CONTRIBUTING.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Contributing
2+
3+
## Quick Start
4+
5+
```bash
6+
git clone https://github.com/thevibeworks/claude-code-statusline.git
7+
cd claude-code-statusline
8+
npm install -g bats # or: brew install bats-core
9+
bats t/ # run tests
10+
```
11+
12+
## Guidelines
13+
14+
- One file per script. No external dependencies beyond `jq` and `curl`.
15+
- Test what you change. Add to `t/statusline.bats` or `t/install.bats`.
16+
- Match existing code style. Read the code before proposing rewrites.
17+
- Keep the statusline fast. It runs on every prompt render.
18+
19+
## Tests
20+
21+
```bash
22+
bats t/ # all tests
23+
bats t/statusline.bats # statusline only
24+
bats t/install.bats # install only
25+
```
26+
27+
Tests source functions from the real `statusline.sh` via `t/helpers.bash`
28+
no copies, no drift. Install tests use mock `curl` and temp `$HOME` isolation.
29+
30+
## Pull Requests
31+
32+
- One feature per PR. Small diffs review faster.
33+
- Include test coverage for new functions.
34+
- Update CHANGELOG.md if user-facing behavior changes.
35+
- Run `bats t/` before pushing. CI runs it too.
36+
37+
## Bug Reports
38+
39+
Open an issue with:
40+
1. Your terminal + shell (e.g., alacritty + zsh, iTerm2 + bash)
41+
2. Claude Code version (`claude --version`)
42+
3. Debug log: `echo '...' | bash statusline.sh --test --debug && cat /tmp/claude-code-statusline.log`

README.md

Lines changed: 139 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,44 @@
11
# Claude Code Statusline
22

3-
A batteries-included statusline for [Claude Code](https://code.claude.com).
4-
Shows what the built-in `/statusline` can't: live quota with reset countdowns,
5-
adaptive polling, model abbreviation, subscription tier, themes, and bar styles.
3+
[![tests](https://github.com/thevibeworks/claude-code-statusline/actions/workflows/test.yml/badge.svg)](https://github.com/thevibeworks/claude-code-statusline/actions/workflows/test.yml)
4+
[![version](https://img.shields.io/github/v/tag/thevibeworks/claude-code-statusline?label=version&sort=semver)](https://github.com/thevibeworks/claude-code-statusline/releases)
5+
[![license](https://img.shields.io/github/license/thevibeworks/claude-code-statusline)](LICENSE)
6+
7+
A batteries-included status bar for [Claude Code](https://code.claude.com).
8+
Live quota with reset countdowns, adaptive polling, model abbreviation,
9+
subscription tier, 5 themes, 9 bar styles.
610

711
```
8-
myproject (main*) +84/-14 8m $6.72 opus4.6[1m][███░░░26%] [MAX|feast.t.] 5h[24%] 7d[100%~12h6m]
12+
myproject (main*) +84/-14 8m $6.72 opus4.6[1m][█░░░░░26%] [MAX|feast.t.] 5h[24%] 7d[100%~12h6m]
913
^ ^ ^ ^ ^ ^ ^ ^ ^ ^
1014
project branch edits time cost model context user 5h quota 7d quota
1115
```
1216

1317
## Install
1418

15-
**One-liner** (recommended -- needs `jq` and `curl`):
16-
1719
```bash
1820
curl -fsSL https://raw.githubusercontent.com/thevibeworks/claude-code-statusline/main/install.sh | bash
1921
```
2022

21-
Downloads the script, merges into your `settings.json`, done.
23+
Downloads the script to `~/.claude/`, configures `settings.json`, done. Needs `jq` and `curl`.
24+
25+
<details>
26+
<summary>Other install methods</summary>
2227

23-
**Ask Claude Code** -- paste this into any session:
28+
**Ask Claude Code** -- paste into any session:
2429

2530
```
2631
Install claude-code-statusline: curl -fsSL https://raw.githubusercontent.com/thevibeworks/claude-code-statusline/main/install.sh | bash
2732
```
2833

29-
**Manual** -- if piping curl to bash makes you twitch:
34+
**Manual:**
3035

3136
```bash
3237
curl -fsSL https://raw.githubusercontent.com/thevibeworks/claude-code-statusline/main/statusline.sh -o ~/.claude/statusline.sh
3338
chmod +x ~/.claude/statusline.sh
3439
```
3540

36-
Then add to `~/.claude/settings.json`:
41+
Add to `~/.claude/settings.json`:
3742

3843
```json
3944
{
@@ -45,61 +50,57 @@ Then add to `~/.claude/settings.json`:
4550
}
4651
```
4752

48-
## What's New in v0.3.0
49-
50-
**Quota reset countdowns.** Percentages don't answer your actual question:
51-
52-
- 5h >= 80%: wall-clock -- `5h[87%@14:30]` -- "can I resume after lunch?"
53-
- 7d >= 70%: countdown -- `7d[75%~2d5h]` -- "how long until capacity?"
54-
- Below threshold: just the percentage. No noise until you need it.
53+
</details>
5554

56-
**Model abbreviation.** `claude-opus-4-6` becomes `opus4.6`. Short aliases
57-
stay unchanged. Version only appears when you've pinned a specific model.
55+
## Why This Instead of `/statusline`?
5856

59-
## Components
60-
61-
Default order: `activity,time,cost,model,context,user,quota`
57+
Claude Code's built-in [`/statusline`](https://code.claude.com/docs/en/statusline)
58+
generates basic one-off scripts. This does more:
6259

63-
| Component | Shows | Example |
64-
|------------|-----------------------------|-----------------------|
65-
| `activity` | Lines added/removed | `+84/-14` |
66-
| `time` | API duration | `8m` |
67-
| `cost` | Session cost | `$6.72` |
68-
| `model` | Active model (abbreviated) | `opus4.6[1m]` |
69-
| `context` | Context window usage bar | `[███░░░26%]` |
70-
| `user` | Tier + display name | `[MAX\|feast.t.]` |
71-
| `quota` | 5h / 7d quota utilization | `5h[24%] 7d[100%~12h6m]` |
60+
| Feature | `/statusline` | This |
61+
|---------|:---:|:---:|
62+
| Context bar | * | * |
63+
| Cost / duration | * | * |
64+
| Git branch | * | * |
65+
| Live quota (5h/7d) | | * |
66+
| Quota reset countdown | | * |
67+
| Adaptive API polling | | * |
68+
| Subscription tier | | * |
69+
| Model abbreviation | | * |
70+
| Themes / bar styles | | * |
71+
| OAuth credential resolution | | * |
72+
| macOS Keychain support | | * |
7273

73-
Custom order: `--order "model,context,quota"`
74+
## Features
7475

75-
## Context Window
76+
### Quota Reset Countdowns
7677

77-
Matches the CLI's `/context` formula:
78+
When utilization enters the warning zone, reset time appears automatically:
7879

7980
```
80-
percentage = totalTokens / contextWindow * 100
81+
Low usage: 5h[24%] 7d[10%]
82+
Warning: 5h[87%@14:30] 7d[75%~2d5h]
83+
Critical: 5h[95%@14:30] 7d[88%~2d5h]
8184
```
8285

83-
Where `totalTokens = cache_read_input_tokens + input_tokens` from the latest
84-
assistant message.
85-
86-
**1M auto-detection:** If `model.id` contains `[1m]`, the window is 1,000,000
87-
tokens. Otherwise 200,000. Override with `CLAUDE_CONTEXT_LIMIT`.
86+
- **5h**: wall-clock time (`@14:30`) -- you're planning your day
87+
- **7d**: relative countdown (`~2d5h`) -- you need the duration, not a date
8888

89-
Color: green < 85%, yellow >= 85%, red >= 100%.
89+
Thresholds: 5h at 80% (yellow), 7d at 70% (yellow).
9090

91-
## Quota
91+
### Model Abbreviation
9292

93-
Format: `5h[24%] 7d[100%~12h6m] op[45%]`
93+
Full model IDs get shortened. Aliases stay unchanged.
9494

95-
- **5h** -- 5-hour rolling window. Yellow at 80%, red at 90%.
96-
- **7d** -- 7-day aggregate. Yellow at 70%, red at 85%.
97-
- **op** -- Opus-specific 7d (MAX users only).
98-
- **sn** -- Sonnet-specific 7d (PRO / TEAM / ENT users).
95+
```
96+
claude-opus-4-6[1m] --> opus4.6[1m] (pinned version)
97+
opus[1m] --> opus[1m] (alias, unchanged)
98+
claude-haiku-4-5-* --> haiku4.5
99+
```
99100

100101
### Adaptive Polling
101102

102-
Doesn't hammer the API. TTL scales with 5h utilization:
103+
Quota polling scales with usage. Doesn't waste API calls when you're idle.
103104

104105
| 5h Utilization | Poll Interval |
105106
|----------------|---------------|
@@ -108,91 +109,131 @@ Doesn't hammer the API. TTL scales with 5h utilization:
108109
| 50-79% | 1 min |
109110
| >= 80% | 30 sec |
110111

111-
Error backoff: 2 min.
112+
Error backoff: 2 min. Atomic cache writes via tmp+mv.
112113

113-
### OAuth Gate
114+
### Context Window
114115

115-
Quota and user components are **skipped** when `ANTHROPIC_API_KEY`,
116-
`ANTHROPIC_AUTH_TOKEN`, or `ANTHROPIC_BASE_URL` is set. The usage endpoint
117-
only works with OAuth tokens at `api.anthropic.com`.
116+
Matches the CLI's `/context` formula exactly:
118117

119-
### Credentials
118+
```
119+
percentage = totalTokens / contextWindow * 100
120+
```
120121

121-
1. **Linux:** `~/.claude/.credentials.json` (`claudeAiOauth.accessToken`)
122-
2. **macOS:** Keychain first, plaintext fallback
123-
3. **OrbStack:** resolves real `$HOME` via `getent passwd`
122+
1M auto-detected from `model.id` `[1m]` suffix. Override: `CLAUDE_CONTEXT_LIMIT`.
124123

125-
## Themes (`--theme`)
124+
## Components
126125

127-
| Theme | What you get |
126+
Default order: `activity,time,cost,model,context,user,quota`
127+
128+
| Component | Shows | Example |
129+
|------------|-----------------------------|----------------------- |
130+
| `activity` | Lines added/removed | `+84/-14` |
131+
| `time` | API duration | `8m` |
132+
| `cost` | Session cost | `$6.72` |
133+
| `model` | Active model (abbreviated) | `opus4.6[1m]` |
134+
| `context` | Context window usage bar | `[█░░░░░26%]` |
135+
| `user` | Tier + display name | `[MAX\|feast.t.]` |
136+
| `quota` | 5h / 7d utilization | `5h[24%] 7d[100%~12h6m]` |
137+
138+
Reorder: `--order "model,context,quota"`
139+
140+
## Themes and Styles
141+
142+
<details>
143+
<summary>5 themes, 9 bar styles</summary>
144+
145+
### Themes (`--theme`)
146+
147+
| Theme | Layout |
128148
|-------------|--------------------------------------|
129149
| `minimal` | Model + context + user only |
130150
| `compact` | Everything, unicode bars (default) |
131151
| `detailed` | Bracketed bars, working path |
132152
| `developer` | Dot bars, right-aligned, full path |
133153
| `manager` | Percent only, cost prominent |
134154

135-
## Bar Styles (`--style`)
155+
### Bar Styles (`--style`)
156+
157+
| Style | Output |
158+
|--------------------|-------------------------|
159+
| `unicode-blocks` | `[███░░░42%]` (default) |
160+
| `single-block` | `▓ 42%` |
161+
| `bracketed-bars` | `[████░░░░] 42%` |
162+
| `filled-dots` | `●●●○○○ 42%` |
163+
| `square-blocks` | `▰▰▰▱▱▱ 42%` |
164+
| `line-segments` | `━━━┅┅┅ 42%` |
165+
| `ascii-bars` | `\|\|\|░░░ 42%` |
166+
| `percent-only` | `42%` |
167+
| `fraction-display` | `3/8` |
136168

137-
| Style | Output |
138-
|--------------------|------------------------|
139-
| `unicode-blocks` | `[███░░░42%]` (default)|
140-
| `single-block` | `▓ 42%` |
141-
| `bracketed-bars` | `[████░░░░] 42%` |
142-
| `filled-dots` | `●●●○○○ 42%` |
143-
| `square-blocks` | `▰▰▰▱▱▱ 42%` |
144-
| `line-segments` | `━━━┅┅┅ 42%` |
145-
| `ascii-bars` | `\|\|\|░░░ 42%` |
146-
| `percent-only` | `42%` |
147-
| `fraction-display` | `3/8` |
169+
</details>
148170

149-
## Options
171+
## Configuration
150172

151173
```
152174
--style <style> Progress bar style
153-
--theme <theme> Preset theme (overrides style/order/path/alignment)
154-
--order <csv> Component order: activity,time,cost,model,context,user,quota
175+
--theme <theme> Preset (overrides style/order/path/alignment)
176+
--order <csv> Component order
155177
--path-display <type> project | cwd | full | relative
156178
--alignment <type> left-right | right-left | center
157179
--debug Log to /tmp/claude-code-statusline.log
158-
--test [json] Test mode (pipe JSON or pass as argument)
180+
--test [json] Test mode
159181
```
160182

161-
## Environment Variables
183+
| Variable | Default | Purpose |
184+
|----------------------------------|------------------|-------------------------------|
185+
| `CLAUDE_CONTEXT_LIMIT` | auto | Context token limit override |
186+
| `CLAUDE_DATA_DIR` | script directory | Usage log location |
187+
| `CLAUDE_CACHE_DIR` | `$DATA_DIR/sessions` | Quota/profile cache |
188+
| `CLAUDE_CODE_MAX_OUTPUT_TOKENS` | `32000` | Max output tokens |
189+
| `CLAUDE_CONFIG_DIR` | `~/.claude` | Claude config directory |
190+
191+
<details>
192+
<summary>OAuth gate and credentials</summary>
193+
194+
Quota/user components are **skipped** when `ANTHROPIC_API_KEY`,
195+
`ANTHROPIC_AUTH_TOKEN`, or `ANTHROPIC_BASE_URL` is set.
162196

163-
| Variable | Default | Purpose |
164-
|----------------------------------|------------------|---------------------------------------|
165-
| `CLAUDE_CONTEXT_LIMIT` | auto | Override context token limit |
166-
| `CLAUDE_DATA_DIR` | script directory | Where `usage.jsonl` lives |
167-
| `CLAUDE_CACHE_DIR` | `$CLAUDE_DATA_DIR/sessions` | Quota/profile cache |
168-
| `CLAUDE_CODE_MAX_OUTPUT_TOKENS` | `32000` | Max output tokens |
169-
| `CLAUDE_CONFIG_DIR` | `~/.claude` | Claude config directory |
197+
Credential resolution:
198+
1. **Linux:** `~/.claude/.credentials.json`
199+
2. **macOS:** Keychain first, plaintext fallback
200+
3. **OrbStack:** resolves real `$HOME` via `getent passwd`
201+
202+
</details>
170203

171204
## Testing
172205

173-
Pipe JSON to verify output:
206+
```bash
207+
bats t/ # 50 tests (needs: npm install -g bats)
208+
```
209+
210+
Or pipe mock JSON directly:
174211

175212
```bash
176-
echo '{"model":{"id":"claude-opus-4-6-20251101[1m]","display_name":"Opus"},"cwd":"/home/dev/project","cost":{"total_cost_usd":6.72,"total_lines_added":84,"total_lines_removed":14,"total_api_duration_ms":480000}}' \
177-
| bash ~/.claude/statusline.sh --test
213+
echo '{"model":{"id":"claude-opus-4-6[1m]","display_name":"Opus"},"cwd":"/tmp/project","cost":{"total_cost_usd":6.72,"total_lines_added":84,"total_lines_removed":14,"total_api_duration_ms":480000},"version":"2.1.139"}' \
214+
| bash statusline.sh --test
178215
```
179216

180-
Add `--debug` to dump diagnostics to `/tmp/claude-code-statusline.log`.
217+
## Project Structure
218+
219+
```
220+
statusline.sh Main script (single file, no dependencies beyond jq/curl)
221+
install.sh One-liner installer (downloads + configures settings.json)
222+
t/
223+
statusline.bats 38 unit + integration tests
224+
install.bats 12 install tests (mock curl, isolated $HOME)
225+
helpers.bash Test helper (sources functions from real statusline.sh)
226+
```
181227

182-
## Official Docs
228+
## Contributing
183229

184-
The [Claude Code statusline documentation](https://code.claude.com/docs/en/statusline)
185-
covers the built-in JSON fields (`context_window.used_percentage`,
186-
`rate_limits.five_hour.resets_at`, etc.) and basic configuration.
230+
See [CONTRIBUTING.md](CONTRIBUTING.md). Short version: run `bats t/` before pushing.
187231

188-
This script uses all of those fields and adds quota tracking with reset
189-
countdowns, adaptive polling, subscription tier display, and model abbreviation
190-
-- things the official examples don't cover.
232+
## Acknowledgments
191233

192-
Worth knowing from the official docs: settings live in `~/.claude/settings.json`;
193-
`refreshInterval` enables periodic updates; script fires after each assistant
194-
message (debounced 300ms); `disableAllHooks: true` kills the statusline too.
234+
Built on the [Claude Code statusline API](https://code.claude.com/docs/en/statusline).
235+
API contract verified against Claude Code CLI v2.1.76 and v2.1.139 binaries.
195236

196237
## License
197238

198-
MIT
239+
[MIT](LICENSE)

0 commit comments

Comments
 (0)