statusline-config — guided layout setup
You configure what the statusline shows by editing statusline_config.json. You set
layout only: which lines exist, which element blocks appear, the usage display style,
the context-gauge form, weather, and emoji_width.
You do NOT touch colors. The colors block is owned entirely by /statusline-theme.
Leave colors exactly as you found it (preserve it byte-for-byte). If the user asks about
colors/themes/palettes, tell them to run /statusline-theme and do not edit colors here.
Never present {peak_label} as an option — Anthropic removed peak-hour limits; it exists
only for back-compat. Only emit placeholders from the list below. English only.
Step 1 — Locate the ACTIVE statusline and read its config
You edit the ACTIVE statusline — the one Claude Code is really running. That is usually
the DEPLOYED copy (e.g. ~/.claude/statusline.py and ~/.claude/statusline_config.json),
NOT the repo copy. Do not assume the repo. Resolve the active target with the shared
helper that lives beside this file, statusline_io.py:
python statusline_io.py locate
(Run it with the absolute path to statusline_io.py in this skill's own directory; fall
back to python3 if python is unavailable.) It prints a single JSON object:
{"statusline_py": <abs or null>, "config_path": <abs or null>,
"settings_json": <path or null>, "launcher": <str or null>,
"source": "pointer"|"settings"|"none",
"pointer_path": <abs path where the pointer would live>,
"notes": <short human string>}
Use config_path as the file you will write back to, and statusline_py as the renderer.
Handle source as follows:
pointer— a saved pointer resolved the target. Use it as-is.settings— resolved by parsing the activesettings.json(statusLine.command). Use it, then OFFER to save a pointer so future runs are instant:python statusline_io.py save-pointer --statusline-py <statusline_py> --config <config_path> [--settings <settings_json>] [--launcher <launcher>]none— nothing resolvable. ASK the user where their statusline is installed (the path to the activestatusline.py); the config isstatusline_config.jsonbeside it unless they setSTATUSLINE_CONFIG. Then OFFER tosave-pointerwith those values.
Always make clear to the user that you are editing the ACTIVE (often deployed ~/.claude)
config, not the repo copy. Never write to a directory the renderer will not read from.
Now read the resolved config_path. If it is missing or invalid JSON, note that you'll
start from built-in defaults (the bar always renders even with no config). Then summarize
what is currently active:
- Which
templateskeys start withline(those are ON, in order) vs_disabled_*/_*(those are OFF). - Which element blocks each active line contains (session header / weather / git / usage / status incident indicator).
- Which usage style is in use, if any (
{usage_bars}+{usage_resets}/{usage_micro}/{compact_usage_micro}/ none). - Which context-gauge form is in use (
{ctx_micro}inline / bare{ctx_bar}/{ctx_bar:N}/ just{ctx}/{ctx_percent}). emoji_width, and weathername+show_*flags.- Whether a
statusblock is present and whether any active line uses a status placeholder ({status},{status:N},{status_icon},{status_header}).
Step 2 — Gather choices (use the AskUserQuestion tool)
Fast path first
Offer the user a choice up front:
- Start from a preset (recommended) — pick one of the four presets below, then optionally tweak.
- Customize (ask me everything) — go through the custom rounds.
Presets (mirror the repo's examples/):
| Preset | Lines | What it shows |
|---|---|---|
| Minimal (1 line) | line1 | model + context label/percent + inline ctx micro. No weather, no usage, no git. |
| Two-line (weather + compact usage) | line1, line2 | sun + time + model + effort + ctx percent on line1; weather + {compact_usage_micro} on line2. |
| Three-line (context bar + git) | line1, line2, line3 | header on line1; weather + big auto-aligned {ctx_bar} on line2; path/git/changes on line3. |
| Four-line (full usage) | line1..line4 | header on line1; path/git on line2; {usage_bars} on line3; {usage_resets} on line4. |
The exact preset templates are in the Preset templates section near the end of this file — copy them verbatim when a preset is chosen.
Custom rounds (only if the user picked "customize")
Ask in clear, separate rounds:
- Element blocks (
multiSelect):session header,weather,git,usage,status(incident indicator — zero-cost when nothing is active; the whole line is suppressed automatically). - Usage style (single select — pick at most ONE, never mix):
- Full bars (2 lines) —
{usage_bars}on one line,{usage_resets}on the next. Most detail; costs two lines. - Micro (1 line) —
{usage_micro}: one line, full-word labels ("session 41%…"). - Compact micro (1 line) —
{compact_usage_micro}: one line, terse ("s 41%…w 63%…"). - None — no usage segment.
- Full bars (2 lines) —
- Context gauge (single select):
- Inline micro — append
{ctx_micro}to line1 (tiny▕<cell>▏). - Big auto-aligned bar — bare
{ctx_bar}on its own line (see composition rule). - Fixed N-cell —
{ctx_bar:N}(ask N, e.g. 28); sits anywhere. - Percent text only — just
{ctx}and/or{ctx_percent}, no gauge.
- Inline micro — append
- Weather (only if
weatherchosen): ask the city (free text), then which parts to show (multiSelectovershow_name,show_icon,show_temp,show_humidity,show_wind), then sun glyph on/off ({sun}). - Emoji width (single select):
2(default; most terminals) or1(tip: choose 1 if emoji render single-width and the bar looks shifted).
Step 3 — Geocode the city (only if weather is ON)
Resolve the city name to coordinates via the Open-Meteo geocoding endpoint (this is
separate from the forecast call statusline.py makes):
GET https://geocoding-api.open-meteo.com/v1/search?name=<CITY>&count=1&language=en&format=json
Read results[0]: use .latitude, .longitude, and .name (and .country to confirm).
Write name/latitude/longitude into the weather block and confirm the resolved
city + country with the user before composing. If geocoding returns no results, ask the
user to refine the city name.
Do NOT ship a config that uses {weather}/{sun} without a geocoded weather block — if
latitude/longitude are missing the renderer falls back to the default placeholder
location (Newyork / 56.25 / -5.2833) and shows bogus weather.
Step 4 — Compose the templates
Build the active line1..lineN from the chosen modules using the Fragment recipes
below, then enforce the composition rules.
Placeholders (the COMPLETE list — emit nothing else)
Color/format: {c.NAME} {r}
Session: {time} {version} {model} {effort}
Context: {ctx} {ctx_percent} {ctx_bar} {ctx_bar:N} {ctx_micro} {align_pad}
Tokens: {total} {input} {output} {cached}
Usage gauges: {usage_bars} {usage_resets} {usage_micro} {compact_usage_micro}
Path/git: {path} {branch} {added} {removed}
Weather: {weather} {sun}
Status: {status} {status:N} {status_icon} {status_header}
{effort} renders like (high) or empty when unsupported. Do not use {peak_label}.
{status:N} caps the label+title at N characters (0 = unlimited), exactly like {ctx_bar:N}.
All four status placeholders render empty (and suppress their whole line) when no incident
is active — the RSS feed is fetched only when a rendered line actually uses one of them.
Fragment recipes (module → fragment)
| Module | Fragment |
|---|---|
| session header | {sun} {c.time}[{time}]{r} {c.version}{version}{r} {c.model}{model}{r}{c.effort}{effort}{r} Ctx:{ctx} {c.ctx_percent}({ctx_percent}%){r} |
| inline ctx micro (append to header) | {ctx_micro} |
| weather line | {c.weather}{weather}{r} |
| git line | {c.path}{path}{r} {c.git_icon}(git)/{r}{c.branch}{branch}{r} ({c.output}+{added}{r},{c.input}-{removed}{r}) |
| usage full (two lines) | line A = {usage_bars} ; line B = {usage_resets} |
| usage micro line | {usage_micro} |
| usage compact line | {compact_usage_micro} |
| weather + compact usage on one line | {c.weather}{weather}{r} {compact_usage_micro} |
| status indicator | {status} (or {status:N} to cap width) |
| status icon only | {status_icon} |
| status header banner | {status_header} — on its OWN line, ABOVE the line containing {status}; appears only when an incident is active |
If the user chose the inline-micro gauge and no {sun} glyph, drop the leading {sun}
from the header. Include {sun} only when the user opted into it.
Usage display styles (pick exactly ONE — never mix)
- (a) FULL BARS → two lines:
{usage_bars}then{usage_resets}. - (b) MICRO → one line:
{usage_micro}. - (c) COMPACT → one line:
{compact_usage_micro}. - or none.
A config must use at most one of these. Never combine (a)+(b), (b)+(c), etc.
Context-gauge forms (3) + composition rules
- INLINE MICRO — append
{ctx_micro}to line1. - BIG AUTO-ALIGNED
{ctx_bar}— on its OWN line. It auto-sizes to align under the line1 segment that starts at{effort}. THEREFORE line1 MUST contain{effort}, and the bar line MUST include{align_pad}before the opening bracket:- without weather:
{align_pad}{c.ctx_bracket}▐{r}{ctx_bar}{c.ctx_bracket}▌{r} - with weather:
{c.weather}{weather}{r}{align_pad}{c.ctx_bracket}▐{r}{ctx_bar}{c.ctx_bracket}▌{r}
- without weather:
- FIXED
{ctx_bar:N}— fixed N cells, sits anywhere, no{align_pad}, e.g.{c.ctx_bracket}▐{r}{ctx_bar:28}{c.ctx_bracket}▌{r} - Just text —
{ctx}and/or{ctx_percent}.
Line ordering and disabling
Order lines sensibly: header → weather/usage → git (gauge line usually right after the
header). Assign active lines as line1, line2, … in display order.
Status incident lines follow a special rule: {status_header} MUST appear on its OWN
line placed ABOVE the line that contains {status}. Both lines vanish together when no
incident is active. The shipped config already contains a ready-to-enable pair — enable both
by renaming them from _disabled_* to line* keys:
"_disabled_status_header": "{status_header}",
"_disabled_status_line": "{status}"
Rename _disabled_status_header to line_status_header (or similar) and
_disabled_status_line to line_status — or insert them as line2/line3 etc. in the
display order. Never place {status_header} on the same line as {status}.
To turn OFF an existing line without deleting it, rename its key to _disabled_<name>
(keys not starting with line are ignored). Preserve every existing _comment* key.
Do NOT alter the colors block.
Step 5 — Show a unified DIFF
Present a unified diff of the old statusline_config.json vs the new one so the user sees
exactly what changes (templates, weather, emoji_width). Confirm colors is unchanged.
Step 6 — Validate
Before rendering, check ALL of:
- JSON parses.
- Every
{placeholder}used appears in the placeholder list above (no{peak_label}). emoji_widthis1or2.- At least one active line (a key starting with
line). - Usage style is not mixed (at most one of full/micro/compact).
- Big-bar rule: if any line uses bare
{ctx_bar}, then line1 contains{effort}and that bar line contains{align_pad}. - If any active line uses
{weather}or{sun}, theweatherblock has a geocodedname/latitude/longitude(not the default placeholder). - If any active line uses
{status},{status:N},{status_icon}, or{status_header}: no special config is required (thestatusblock has sensible defaults and is optional); the RSS fetch happens only when these placeholders appear in a rendered line. If{status_header}is used, confirm it is on a separate line placed ABOVE the line containing{status}. colorsblock is byte-for-byte unchanged;_comment*keys preserved.
Fix any failure before proceeding.
Step 7 — Test-render with the sample input
Render the new config against assets/sample_input.json (bundled with this skill).
Point STATUSLINE_CONFIG at a temp copy of the new config so the user's live file is
untouched until they confirm — the live config is never written until Step 8's commit.
Use the statusline_py path you resolved in Step 1 (from the helper's locate JSON)
wherever <statusline.py> appears below — the absolute path to the active renderer (the
deployed copy, e.g. ~/.claude/statusline.py, or wherever locate reported). Do not
hardcode any particular machine's path. Write the candidate config to a temp path (e.g. the
OS temp dir), then:
PowerShell:
$env:STATUSLINE_CONFIG="<temp-config>"; Get-Content <skill>/assets/sample_input.json | uv run <statusline.py>
(fallback: replace uv run with python)
bash:
STATUSLINE_CONFIG=<temp-config> uv run <statusline.py> < <skill>/assets/sample_input.json
(fallback: python3 instead of uv run)
A non-empty colored line = success. Show the rendered bar to the user.
Step 8 — Confirm, then commit (history-aware)
Only on explicit user confirmation (after the Step 5 diff, Step 6 validate, and Step 7 test-render), install the new config. Otherwise iterate from Step 2. Never write the live config without confirmation.
Never write the live config directly. Instead, write the fully-built new config object
(comments / _disabled_* / colors preserved exactly as required above) to a temp file,
then hand that file to the shared helper, which snapshots the about-to-be-replaced config and
records a human changelog entry next to the active config:
python statusline_io.py commit --config <config_path> --new <temp-new-config> \
--skill statusline-config \
--summary "<what changed, e.g. switched to three-line preset; added weather + big ctx bar>" \
--why "<the user's intent in their words>" \
--diff "<the key changes, e.g. the unified diff from Step 5>"
<config_path>is the ACTIVE config resolved in Step 1.<temp-new-config>is the temp file holding your fully-formed new config (the same content you test-rendered in Step 7 — reuse that temp file).- The helper validates the temp file parses as JSON before touching anything, snapshots the
current config into
.statusline-config-history/YYYYMMDD-hhmm.jsonnext to the active config, writes the new bytes to the active config, and appends to.statusline-config-history/YYYYMMDD.md.
It prints {"snapshot": <path or null>, "changelog": <path>, "config_path": <abs>}. Report
the snapshot and changelog paths to the user so they know where the previous version and
the change note were saved.
Preset templates (copy verbatim when a preset is chosen)
Set the listed templates, weather, and emoji_width fields; keep the existing
colors block and all _comment* keys untouched. (The presets below show
emoji_width: 1 to match examples/; if the user's emoji render 2-wide, use 2.)
For any preset that uses {weather}/{sun} you MUST first run Step 3 geocoding and
fill the weather block's name/latitude/longitude with the resolved values before
writing — do not ship the placeholders shown below.
Minimal (1 line): (no weather)
"emoji_width": 1,
"templates": {
"line1": "{c.model}{model}{r} {c.ctx_label}Ctx{r} {c.ctx_value}{ctx}{r} {c.ctx_percent}({ctx_percent}%){r} {ctx_micro}"
}
Two-line (weather + compact usage): (replace the weather placeholders with the
Step 3 geocoded city)
"emoji_width": 1,
"weather": {
"name": "<geocoded name>", "latitude": <lat>, "longitude": <lon>,
"show_name": true, "show_icon": true, "show_temp": true,
"show_humidity": true, "show_wind": true
},
"templates": {
"line1": "{sun} {c.time}[{time}]{r} {c.model}{model}{r}{c.effort}{effort}{r} {c.ctx_label}Ctx{r} {c.ctx_percent}({ctx_percent}%){r}",
"line2": "{c.weather}{weather}{r} {compact_usage_micro}"
}
Three-line (context bar + git): (replace the weather placeholders with the Step 3
geocoded city)
"emoji_width": 1,
"weather": {
"name": "<geocoded name>", "latitude": <lat>, "longitude": <lon>,
"show_name": false, "show_icon": true, "show_temp": true,
"show_humidity": false, "show_wind": false
},
"templates": {
"line1": "{c.time}[{time}]{r} {c.version}{version}{r} {c.model}{model}{r}{c.effort}{effort}{r} Ctx:{ctx} {c.ctx_percent}({ctx_percent}%){r}",
"line2": "{c.weather}{weather}{r}{align_pad}{c.ctx_bracket}▐{r}{ctx_bar}{c.ctx_bracket}▌{r}",
"line3": "{c.path}{path}{r} {c.git_icon}(git)/{r}{c.branch}{branch}{r} ({c.output}+{added}{r},{c.input}-{removed}{r})"
}
Four-line (full usage): (no weather)
"emoji_width": 1,
"templates": {
"line1": "{c.time}[{time}]{r} {c.model}{model}{r}{c.effort}{effort}{r} Ctx:{ctx} {c.ctx_percent}({ctx_percent}%){r}",
"line2": "{c.path}{path}{r} {c.git_icon}(git)/{r}{c.branch}{branch}{r} ({c.output}+{added}{r},{c.input}-{removed}{r})",
"line3": "{usage_bars}",
"line4": "{usage_resets}"
}
Status config block
The status block is optional — if absent, all status placeholders use these defaults.
Add or edit the block to override specific keys; the rest remain at their defaults.
"status": {
"show_icon": true, // show the incident emoji in {status}
"show_label": true, // show the status label (Investigating / Identified / Monitoring …)
"show_title": true, // show the incident title text
"show_count": true, // show "(+N)" hint when multiple incidents are active
"show_header": true, // allow {status_header} to render
"include_maintenance": false, // also surface scheduled / in-progress maintenance events
"max_len": 48, // global char cap for {status} (0 = unlimited)
"max_age_hours": 48, // only incidents updated within this many hours count (0 = no limit)
"title": "⚠️ Claude have some issues" // banner text shown by {status_header}
}
Key notes:
max_lenis the global cap for{status};{status:N}overrides it per-line (0 = unlimited for that line).include_maintenance: false(default) hides scheduled/in-progress maintenance events; set totrueto surface them alongside incidents.max_age_hours: 0disables the age filter — every parsed incident counts regardless of when it was last updated.- A leading emoji in
titleis auto-spaced from the words; no manual spacing needed. - The
statusblock does not live insidecolors— it is a sibling oftemplates,weather, andemoji_width. - Color keys for the status indicator (
status_investigating,status_identified,status_monitoring,status_maintenance,status_default,status_title,status_count,status_header) live in thecolorsblock and are owned by/statusline-theme; do not touch them here.