Skip to content

Commit a884733

Browse files
Add a cost / rate-limit / branch status line, installed by setup.sh
Brings the repo's monitoring wedge to life: a status line showing model, context usage, 5-hour + weekly plan limits (bars + time-to-reset), git branch/changes, session time, and free disk. - assets/statusline/: ccstatusline config (the rich 3-line display), a no-Node statusline.sh alternative (bash + jq + git), and a README - setup.sh: install ccstatusline + jq, write the ccstatusline config and the statusline.sh alternative, and add `statusLine` to the generated settings.json (all idempotent and tolerant; tests still 32/32 green) - docs: flesh out the status-line section on the monitoring page; add it to the bootstrap "what it does / writes" and the home "one command does" table Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent b0c0542 commit a884733

8 files changed

Lines changed: 225 additions & 9 deletions

File tree

assets/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ safe by default — no destructive actions are ever auto-approved.
1010
| [`hooks/`](hooks/) | Hook snippets (format-on-edit, protect-paths, notify-on-stop) | your `settings.json` `hooks` block |
1111
| [`settings/`](settings/) | `settings.json` examples (minimal, team) | `~/.claude/settings.json` or `.claude/settings.json` |
1212
| [`skills/`](skills/) | Full shareable skills (e.g. project-onboard) | `~/.claude/skills/<name>/` |
13+
| [`statusline/`](statusline/) | A cost / rate-limit / branch status line | `~/.claude` + `~/.config/ccstatusline/` |
1314

1415
Each folder has its own `README.md` with install instructions.
1516

assets/statusline/README.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Status line
2+
3+
A status line at the bottom of Claude Code that shows, at a glance: the **model**,
4+
**context usage**, your **5-hour and weekly plan limits** (with bars + time-to-reset),
5+
the **git branch + changes**, **session time**, and **free disk**.
6+
7+
This is the repo's monitoring wedge made real — you always see how much of your plan
8+
you've burned and when it resets, without running `/usage`.
9+
10+
```text
11+
🤖 Opus 4.8 🧠 [███░░░░] 48%
12+
🟢 5h [███░░░░] 32% ⟳ 3h 3m 📅 7d [███░░░░] 31% ⟳ 4d 10h
13+
🌿 main +19 -8 ⏱ 4h 27m 💾 19.9G/48.0G
14+
```
15+
16+
## Two ways to get it
17+
18+
### Option A — `ccstatusline` (recommended, what `setup.sh` installs)
19+
20+
[`ccstatusline`](https://github.com/sirmalloc/ccstatusline) is a configurable,
21+
multi-line status line for Claude Code (the 3-line display above). `setup.sh` installs
22+
it and writes this config for you. To do it by hand:
23+
24+
```bash
25+
npm install -g ccstatusline
26+
mkdir -p ~/.config/ccstatusline
27+
cp ccstatusline-settings.json ~/.config/ccstatusline/settings.json
28+
```
29+
30+
Then point Claude Code at it in `~/.claude/settings.json`:
31+
32+
```json
33+
{ "statusLine": { "type": "command", "command": "ccstatusline", "padding": 0 } }
34+
```
35+
36+
Tweak the widgets interactively by running `ccstatusline` in a terminal.
37+
38+
### Option B — self-contained `statusline.sh` (no Node, just bash + jq + git)
39+
40+
A single script with no dependencies beyond `jq` and `git`. Lighter, fully yours, but
41+
a single line and fewer widgets.
42+
43+
```bash
44+
cp statusline.sh ~/.claude/statusline.sh && chmod +x ~/.claude/statusline.sh
45+
```
46+
47+
```json
48+
{ "statusLine": { "type": "command", "command": "~/.claude/statusline.sh" } }
49+
```
50+
51+
## Notes
52+
53+
- The **5-hour / weekly** segments rely on your Claude Code version exposing
54+
`rate_limits` in the status payload. If yours doesn't, those segments are omitted
55+
(the model + context + branch still show).
56+
- See [Monitor cost & rate limits](../../docs/environment/monitoring-cost-ratelimits.md)
57+
for the full picture, including `/usage` and `/context`.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
{
2+
"version": 3,
3+
"lines": [
4+
[
5+
{ "id": "l1a", "type": "custom-text", "customText": "🤖 ", "color": "cyan" },
6+
{ "id": "l1b", "type": "model", "rawValue": true, "color": "cyan", "bold": true },
7+
{ "id": "l1c", "type": "custom-text", "customText": " 🧠 ", "color": "white" },
8+
{ "id": "l1d", "type": "context-bar", "rawValue": true, "color": "white" }
9+
],
10+
[
11+
{ "id": "l2a", "type": "custom-text", "customText": "🟢 5h ", "color": "green" },
12+
{ "id": "l2b", "type": "session-usage", "rawValue": true, "color": "green", "metadata": { "display": "progress-short" } },
13+
{ "id": "l2c", "type": "custom-text", "customText": "", "color": "brightBlack" },
14+
{ "id": "l2d", "type": "reset-timer", "rawValue": true, "color": "brightBlack" },
15+
{ "id": "l2e", "type": "custom-text", "customText": " 📅 7d ", "color": "blue" },
16+
{ "id": "l2f", "type": "weekly-usage", "rawValue": true, "color": "blue", "metadata": { "display": "progress-short" } },
17+
{ "id": "l2g", "type": "custom-text", "customText": "", "color": "brightBlack" },
18+
{ "id": "l2h", "type": "weekly-reset-timer", "rawValue": true, "color": "brightBlack" }
19+
],
20+
[
21+
{ "id": "l3a", "type": "custom-text", "customText": "🌿 ", "color": "magenta" },
22+
{ "id": "l3b", "type": "git-branch", "rawValue": true, "color": "magenta" },
23+
{ "id": "l3c", "type": "custom-text", "customText": " ", "color": "yellow" },
24+
{ "id": "l3d", "type": "git-changes", "color": "yellow" },
25+
{ "id": "l3e", "type": "custom-text", "customText": "", "color": "brightBlack" },
26+
{ "id": "l3f", "type": "session-clock", "rawValue": true, "color": "brightBlack" },
27+
{ "id": "l3g", "type": "custom-text", "customText": " 💾 ", "color": "brightBlack" },
28+
{ "id": "l3h", "type": "free-memory", "rawValue": true, "color": "brightBlack" }
29+
]
30+
],
31+
"flexMode": "full-minus-40",
32+
"compactThreshold": 60,
33+
"colorLevel": 2,
34+
"inheritSeparatorColors": false,
35+
"globalBold": false,
36+
"gitCacheTtlSeconds": 5,
37+
"minimalistMode": false,
38+
"powerline": {
39+
"enabled": false,
40+
"separators": [""],
41+
"separatorInvertBackground": [false],
42+
"startCaps": [],
43+
"endCaps": [],
44+
"autoAlign": false,
45+
"continueThemeAcrossLines": false
46+
}
47+
}

assets/statusline/statusline.sh

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#!/usr/bin/env bash
2+
# Self-contained Claude Code status line — model, context %, your Max-plan limits
3+
# (5-hour window + weekly) with time-to-reset, and the git branch.
4+
#
5+
# Needs: bash + jq + git. Lighter than ccstatusline (no Node). The rate-limit
6+
# fields depend on your Claude Code version exposing them in the status payload;
7+
# if it doesn't, those segments are simply omitted.
8+
#
9+
# Enable: copy to ~/.claude/statusline.sh, then set in ~/.claude/settings.json:
10+
# "statusLine": { "type": "command", "command": "~/.claude/statusline.sh" }
11+
12+
input=$(cat)
13+
field() { printf '%s' "$input" | jq -r "$1" 2>/dev/null; }
14+
15+
model=$(field '.model.display_name // "?"')
16+
ctx=$(field '.context_window.used_percentage // 0' | cut -d. -f1)
17+
h5_pct=$(field '.rate_limits.five_hour.used_percentage // empty')
18+
h5_reset=$(field '.rate_limits.five_hour.resets_at // empty')
19+
w_pct=$(field '.rate_limits.seven_day.used_percentage // empty')
20+
w_reset=$(field '.rate_limits.seven_day.resets_at // empty')
21+
cwd=$(field '.workspace.current_dir // .cwd // empty')
22+
23+
now=$(date +%s)
24+
25+
fmt_left() { # seconds -> "2h 13m" / "3d 4h"
26+
local secs=$1
27+
[ -z "$secs" ] && { printf '?'; return; }
28+
(( secs < 0 )) && secs=0
29+
local d=$(( secs / 86400 )) h=$(( (secs % 86400) / 3600 )) m=$(( (secs % 3600) / 60 ))
30+
if (( d > 0 )); then printf '%dd %dh' "$d" "$h"
31+
elif (( h > 0 )); then printf '%dh %dm' "$h" "$m"
32+
else printf '%dm' "$m"; fi
33+
}
34+
35+
bar() { # 0-100% -> a 10-segment bar
36+
local pct=${1%.*}; [ -z "$pct" ] && pct=0
37+
local filled=$(( pct / 10 )); (( filled > 10 )) && filled=10
38+
local i out=""
39+
for ((i=0;i<10;i++)); do (( i < filled )) && out+="" || out+=""; done
40+
printf '%s' "$out"
41+
}
42+
43+
seg="🤖 $model 🧠 ${ctx}% ctx"
44+
45+
branch=$(git -C "${cwd:-.}" rev-parse --abbrev-ref HEAD 2>/dev/null)
46+
[ -n "$branch" ] && seg+=" 🌿 $branch"
47+
48+
if [ -n "$h5_pct" ]; then
49+
p=${h5_pct%.*}; seg+=" ⏱️ 5h $(bar "$p") ${p}% (reset $(fmt_left $(( h5_reset - now ))))"
50+
fi
51+
if [ -n "$w_pct" ]; then
52+
p=${w_pct%.*}; seg+=" 📅 7d $(bar "$p") ${p}% (reset $(fmt_left $(( w_reset - now ))))"
53+
fi
54+
55+
printf '%s' "$seg"

docs/environment/bootstrap-setup.md

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@ The script runs everything from zero, in order:
1616
4. **JetBrains Mono**`brew install --cask font-jetbrains-mono`.
1717
5. **Node.js** — the runtime for Claude Code and many projects.
1818
6. **GitHub CLI (`gh`)** — for pull requests and the GitHub MCP.
19-
7. **Claude Code** — via the official native installer (`curl -fsSL https://claude.ai/install.sh | bash`), with an npm fallback. Persists `~/.local/bin` on your PATH.
20-
8. **Ghostty config** — writes `~/.config/ghostty/config` (Catppuccin auto dark/light, JetBrains Mono).
21-
9. **Global Claude config** — writes `~/.claude/settings.json` and `~/.claude/CLAUDE.md`, but only if they don't already exist.
22-
10. **Shell aliases** — adds `cc`, `ccc`, `ccp` to `~/.zshrc`.
23-
11. **Validation** — runs `validate()` to report what's in place and what's missing.
19+
7. **jq** — used by the example hooks and the status line.
20+
8. **Claude Code** — via the official native installer (`curl -fsSL https://claude.ai/install.sh | bash`), with an npm fallback. Persists `~/.local/bin` on your PATH.
21+
9. **Ghostty config** — writes `~/.config/ghostty/config` (Catppuccin auto dark/light, JetBrains Mono).
22+
10. **Global Claude config** — writes `~/.claude/settings.json` and `~/.claude/CLAUDE.md`, but only if they don't already exist.
23+
11. **Status line** — installs [ccstatusline](https://github.com/sirmalloc/ccstatusline) and writes its config (model · context · 5h/weekly limits · branch · session · disk), plus a no-Node `statusline.sh` alternative. The settings.json points at it.
24+
12. **Shell aliases** — adds `cc`, `ccc`, `ccp` to `~/.zshrc`.
25+
13. **Validation** — runs `validate()` to report what's in place and what's missing.
2426

2527
## Flags
2628

@@ -76,8 +78,10 @@ Then:
7678
| Path | Contents |
7779
| --- | --- |
7880
| `~/.config/ghostty/config` | Ghostty theme, font, padding, cursor settings. |
79-
| `~/.claude/settings.json` | Global settings with a small safe permission allowlist (only if absent). |
81+
| `~/.claude/settings.json` | Global settings: permission allowlist + the `statusLine` (only if absent). |
8082
| `~/.claude/CLAUDE.md` | Global preferences (only if absent). |
83+
| `~/.config/ccstatusline/settings.json` | Status-line layout (only if absent). |
84+
| `~/.claude/statusline.sh` | No-Node status-line alternative (only if absent). |
8185
| `~/.zshrc` | `brew shellenv`, `~/.local/bin` on PATH, and the `cc` / `ccc` / `ccp` aliases. |
8286

8387
The aliases: `cc` = `claude`, `ccc` = `claude --continue` (resume the last session), `ccp` = `claude --permission-mode plan`.

docs/environment/monitoring-cost-ratelimits.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,18 @@ Run `/context` to see how full the current conversation's **context window** is.
3030
3131
## A live status line
3232

33-
You can surface rate-limit and context info **continuously** with a status line, configured via `statusLine` in `~/.claude/settings.json`. Instead of running `/usage` repeatedly, you get a live readout at the bottom of the prompt.
33+
Instead of running `/usage` repeatedly, keep a **status line** pinned to the bottom of the prompt that always shows your model, context usage, your 5-hour and weekly limits (with bars and time-to-reset), the git branch, session time, and free disk:
34+
35+
```text
36+
🤖 Opus 4.8 🧠 [███░░░░] 48%
37+
🟢 5h [███░░░░] 32% ⟳ 3h 3m 📅 7d [███░░░░] 31% ⟳ 4d 10h
38+
🌿 main +19 -8 ⏱ 4h 27m 💾 19.9G/48.0G
39+
```
40+
41+
The [one-command setup](./bootstrap-setup.md) installs this for you — via [ccstatusline](https://github.com/sirmalloc/ccstatusline) — and wires it into `~/.claude/settings.json`. To set it up by hand, or to use the no-Node `statusline.sh` alternative (just bash + jq + git), see the [status-line assets](https://github.com/bogdanmatasaru/claude-code-guide/tree/main/assets/statusline).
3442

3543
> [!TIP]
36-
> A status line that shows remaining context and time-to-reset is the cheapest insurance against an unexpected limit mid-task.
44+
> A status line that shows remaining context and time-to-reset is the cheapest insurance against an unexpected limit mid-task. It's the whole point of this guide's setup — you never have to wonder how much plan you've got left.
3745
3846
## What to do when you hit a limit
3947

docs/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ It runs an idempotent, re-runnable bootstrap that installs and configures everyt
6767
| 4 | **Node.js** + GitHub CLI |
6868
| 5 | **Claude Code** — the `claude` CLI |
6969
| 6 | **Configs** — Ghostty theme, `~/.claude` settings, shell PATH & aliases |
70+
| 7 | **Live status line** — model · context · 5h/weekly limits · branch · session · disk |
7071

7172
> [!TIP]
7273
> Want to preview it first, changing nothing? Add `--dry-run`:

setup.sh

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,9 @@ brew_formula node "Node.js"
230230
step "GitHub CLI (gh) — for PRs / the GitHub MCP"
231231
brew_formula gh "gh"
232232

233+
step "jq — used by hooks and the status line"
234+
brew_formula jq "jq"
235+
233236
# ─────────────────────────────────────────────────────────────────────────────
234237
# 4. Claude Code (official native installer, npm fallback) + persist PATH
235238
# ─────────────────────────────────────────────────────────────────────────────
@@ -286,6 +289,7 @@ read -r -d '' CLAUDE_SETTINGS <<'EOF' || true
286289
{
287290
"$schema": "https://json.schemastore.org/claude-code-settings.json",
288291
"includeCoAuthoredBy": true,
292+
"statusLine": { "type": "command", "command": "ccstatusline", "padding": 0 },
289293
"permissions": {
290294
"allow": [
291295
"Bash(git status:*)",
@@ -320,7 +324,46 @@ else
320324
fi
321325

322326
# ─────────────────────────────────────────────────────────────────────────────
323-
# 7. Shell aliases (optional)
327+
# 7. Status line — model, context, 5h/weekly limits, branch, session, disk
328+
# ─────────────────────────────────────────────────────────────────────────────
329+
step "Status line (cost / rate-limits / branch)"
330+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
331+
SL_ASSETS="$SCRIPT_DIR/assets/statusline"
332+
if [ -d "$SL_ASSETS" ]; then
333+
# ccstatusline (the rich multi-line display the settings.json points to)
334+
if command -v ccstatusline >/dev/null 2>&1; then
335+
ok "ccstatusline already installed"
336+
else
337+
run "npm install -g ccstatusline" \
338+
|| warn "ccstatusline not installed (needs npm) — the status line stays blank until it is"
339+
fi
340+
# its config, only if you don't already have one
341+
if [ -f "$HOME/.config/ccstatusline/settings.json" ]; then
342+
skip "ccstatusline config exists — not overwriting"
343+
elif $DRY_RUN; then
344+
skip "[dry-run] write ~/.config/ccstatusline/settings.json"
345+
else
346+
mkdir -p "$HOME/.config/ccstatusline"
347+
cp "$SL_ASSETS/ccstatusline-settings.json" "$HOME/.config/ccstatusline/settings.json"
348+
ok "wrote ~/.config/ccstatusline/settings.json"
349+
fi
350+
# also drop the no-Node alternative (handy if you'd rather not use ccstatusline)
351+
if [ -f "$HOME/.claude/statusline.sh" ]; then
352+
# shellcheck disable=SC2088 # tilde is displayed text, not a path to expand
353+
skip "~/.claude/statusline.sh exists — not overwriting"
354+
elif $DRY_RUN; then
355+
skip "[dry-run] write ~/.claude/statusline.sh"
356+
else
357+
cp "$SL_ASSETS/statusline.sh" "$HOME/.claude/statusline.sh"
358+
chmod +x "$HOME/.claude/statusline.sh"
359+
ok "wrote ~/.claude/statusline.sh (alternative)"
360+
fi
361+
else
362+
skip "status-line assets not found (run setup.sh from the repo) — skipping"
363+
fi
364+
365+
# ─────────────────────────────────────────────────────────────────────────────
366+
# 8. Shell aliases (optional)
324367
# ─────────────────────────────────────────────────────────────────────────────
325368
if $ADD_SHELL_ALIASES; then
326369
step "zsh aliases (~/.zshrc)"

0 commit comments

Comments
 (0)