Skip to content

Commit cf74e87

Browse files
committed
feat: add worktree segment for --worktree sessions
Display worktree info when Claude Code sends worktree data in JSON input. Shows worktree name in curly braces {name} by default. Configurable via worktree:show=name|branch|path|origin or comma-separated combo. Path and origin apply ~ home shortening. New theme color text_worktree (Nord15 purple dark, muted purple light).
1 parent cfd1694 commit cf74e87

2 files changed

Lines changed: 34 additions & 4 deletions

File tree

CLAUDE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ Key sections in `claude-code-status-line.py` (~2,200 lines):
4848
- **Lines ~872**: `format_usage_indicators()` — returns dict with per-window usage strings
4949
- **Lines ~1183**: `_format_duration()` / `_format_duration_compact()` — round durations for burndown; default uses rounded single-unit (`3 d`, `8 h`), compact uses compound no-space form (`5d2h30m`)
5050
- **Lines ~1228**: `_format_burndown()` — three-mode burndown message with `verbosity` param (`default`/`short`): Soon (<1h to depletion), Pace (>=48h, shows pace warning), Countdown (<48h, shows absolute time left)
51-
- **Lines ~1125-1220**: Segment renderers — `_render_model`, `_render_progress_bar`, `_render_percentage`, `_render_tokens`, `_render_directory`, `_render_added_dirs`, `_render_git_branch`, `_render_git_status`, `_render_usage_5hour`, `_render_usage_weekly`, `_render_usage_burndown`, `_render_update`, `_render_new_line` + `SEGMENT_RENDERERS` dict
51+
- **Lines ~1125-1220**: Segment renderers — `_render_model`, `_render_progress_bar`, `_render_percentage`, `_render_tokens`, `_render_directory`, `_render_added_dirs`, `_render_worktree`, `_render_git_branch`, `_render_git_status`, `_render_usage_5hour`, `_render_usage_weekly`, `_render_usage_burndown`, `_render_update`, `_render_new_line` + `SEGMENT_RENDERERS` dict
5252
- **Lines ~1530**: `_join_parts()` — joins segment parts with newline-aware flush-left behavior for multi-line layouts
5353
- **Lines ~1547**: `build_progress_bar()` — builds ctx dict, iterates SEGMENTS calling renderers, uses `_join_parts`
5454
- **Lines ~2086**: `main()` — entry point, handles demo modes and normal stdin flow
@@ -63,7 +63,7 @@ Key sections in `claude-code-status-line.py` (~2,200 lines):
6363
- **Bayesian shrinkage**: the observed burn rate is blended toward the on-track rate using a hyperbolic trust curve `f = elapsed / (halftrust + elapsed)`, so early-window noise is dampened. Config: `usage_burndown:halftrust=16` (default 16h — at 16h elapsed, 50/50 blend).
6464
- **Relevance filter**: burndown is suppressed when the predicted "sooner" gap is too small relative to remaining time, using a power curve `days_remaining^coeff` (in hours). This avoids noisy predictions early in the window. Config: `usage_burndown:coeff=1.4` (default). At 6.5 days left requires ~13h sooner, at 1 day ~1h, at 0.5 days ~24min.
6565
- `model` segment supports `effort` option: `model:effort=short` shows single letter (`H`/`M`/`L`), `model:effort=full` shows full word (`high`/`medium`/`low`), `model:effort=` disables effort display. Default: `full`. Effort read from `CLAUDE_CODE_EFFORT_LEVEL` env var → `~/.claude/settings.json` `effortLevel``"high"`.
66-
- Config: `usage_burndown:verbosity=short:coeff=1.4:halftrust=16`. Short uses compact compound durations (`5d2h30m`), `~` instead of `about`, `->` instead of `then`, drops `usage`, `out ~` instead of `may run out about`; `git_status` shows working directory state using symbols: `+` staged, `!` modified, `x` deleted, `r` renamed, `?` untracked, `=` conflicted, `$` stashed, `>` ahead, `<` behind, `<>` diverged; `added_dirs` shows directories added via `/add-dir` (from `workspace.added_dirs` JSON field), sorted alphabetically, joined by separator. Config: `added_dirs:basename_only=1:separator=,` (defaults: full path with `~` home shortening, `` separator). Theme color: `text_added_dirs` (dark: Nord3 muted gray `#4C566A`, light: medium gray `#7B8394`).
66+
- Config: `usage_burndown:verbosity=short:coeff=1.4:halftrust=16`. Short uses compact compound durations (`5d2h30m`), `~` instead of `about`, `->` instead of `then`, drops `usage`, `out ~` instead of `may run out about`; `git_status` shows working directory state using symbols: `+` staged, `!` modified, `x` deleted, `r` renamed, `?` untracked, `=` conflicted, `$` stashed, `>` ahead, `<` behind, `<>` diverged; `added_dirs` shows directories added via `/add-dir` (from `workspace.added_dirs` JSON field), sorted alphabetically, joined by separator. Config: `added_dirs:basename_only=1:separator=,` (defaults: full path with `~` home shortening, ` • ` separator). Theme color: `text_added_dirs` (dark: Nord3 muted gray `#4C566A`, light: medium gray `#7B8394`); `worktree` shows worktree info when `worktree` field is present in JSON input (from `--worktree` sessions). Displays in curly braces `{name}`. Config: `worktree:show=name` (default), supports `name`, `branch`, `path`, `origin` or comma-separated combo like `show=name,branch`. `path` and `origin` apply `~` home shortening. `origin` maps to JSON `original` field. Theme color: `text_worktree` (dark: Nord15 purple `#B48EAD`, light: muted purple `#8B6B85`).
6767
- **Configuration**: global settings via `SL_THEME`, `SL_USAGE_CACHE_DURATION`, `SL_UPDATE_CACHE_DURATION`, `SL_UPDATE_RETRY_DURATION`, `SL_UPDATE_CUSTOM_RETRY_DURATION`, `SL_UPDATE_VERSION_CMD`, `SL_UPDATE_VERSION_SOURCE`, `SL_SHOW_STATUSLINE_UPDATE`, `SL_THEME_FILE`. All per-segment config (bar width, gauge style, hide default branch, basename-only directory) via colon-separated options in `SL_SEGMENTS`.
6868
- **Update checker**: fetches latest version from npm registry (`@anthropic-ai/claude-code`) or custom command (`SL_UPDATE_VERSION_CMD`), compares with `claude --version` output. Custom command retries 3 times with 1s delay, then falls back to npm. Returns `(version, source)` tuple where source is `npm`, `custom`, or `npm_fallback`. Cached to `~/.claude/.update_cache.json` — success cached for `UPDATE_CACHE_DURATION` (1h), custom source failures retry after `UPDATE_CUSTOM_RETRY_DURATION` (2min), total failures retry after `UPDATE_RETRY_DURATION` (10min). Falls back to stale cache when offline.
6969
- **Self-update**: `--self-update` flag downloads latest version from GitHub and replaces the script atomically. Status line update notifications appear on a separate line below the main output with the update command. Controlled by `SL_SHOW_STATUSLINE_UPDATE` (default on).

claude-code-status-line.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def _env_int(key, default):
7070
# --- Segment system ---
7171

7272
DEFAULT_SEGMENTS = (
73-
"update model progress_bar percentage tokens directory added_dirs git_branch git_status usage_5hour usage_weekly"
73+
"update model progress_bar percentage tokens directory worktree added_dirs git_branch git_status usage_5hour usage_weekly"
7474
)
7575
VALID_SEGMENTS = frozenset(DEFAULT_SEGMENTS.split() + ["new_line", "usage_burndown"])
7676

@@ -82,6 +82,7 @@ def _env_int(key, default):
8282
"git_branch": {"hide_default": "1"},
8383
"usage_5hour": {"gauge": "blocks", "width": "4"},
8484
"usage_weekly": {"gauge": "blocks", "width": "4"},
85+
"worktree": {"show": "name"},
8586
"usage_burndown": {"coeff": "1.4"},
8687
}
8788

@@ -194,6 +195,7 @@ def hex_to_256(hex_color):
194195
"text_git": (("#B48EAD", None), 139), # nord15 purple
195196
"text_na": (("#D08770", None), 173), # nord12 orange
196197
"text_added_dirs": (("#4C566A", None), 60), # nord3 muted gray
198+
"text_worktree": (("#B48EAD", None), 139), # nord15 purple
197199
# Usage indicator colors (ratio-based)
198200
"usage_light": ("#88C0D0", 110), # nord8 frost - well ahead
199201
"usage_green": ("#A3BE8C", 108), # nord14 - on track
@@ -232,6 +234,7 @@ def hex_to_256(hex_color):
232234
"text_git": (("#508C50", None), 65), # muted green
233235
"text_na": (("#D08770", None), 173), # nord12 orange
234236
"text_added_dirs": (("#7B8394", None), 103), # medium gray
237+
"text_worktree": (("#8B6B85", None), 132), # muted purple
235238
# Usage indicator colors (ratio-based) - darker for light bg
236239
"usage_light": ("#2B7A78", 30), # dark teal - well ahead
237240
"usage_green": ("#4A7C4A", 65), # dark green - on track
@@ -332,7 +335,7 @@ def _is_hex(v):
332335
overrides[key] = (h, hex_to_256(h))
333336

334337
# Text colors: "hex" → (("hex", None), 256)
335-
for key in ("text_percent", "text_numbers", "text_cwd", "text_git", "text_na", "text_added_dirs"):
338+
for key in ("text_percent", "text_numbers", "text_cwd", "text_git", "text_na", "text_added_dirs", "text_worktree"):
336339
if key in ns:
337340
h = ns[key]
338341
if not _is_hex(h):
@@ -1466,6 +1469,28 @@ def _render_added_dirs(ctx, opts):
14661469
return f" {text_color('added_dirs')}{joined}"
14671470

14681471

1472+
def _render_worktree(ctx, opts):
1473+
worktree = ctx.get("worktree")
1474+
if not worktree:
1475+
return ""
1476+
show = opts.get("show", "name")
1477+
fields = [f.strip() for f in show.split(",")]
1478+
parts = []
1479+
field_map = {"origin": "original"}
1480+
for field in fields:
1481+
val = worktree.get(field_map.get(field, field), "")
1482+
if field in ("path", "origin"):
1483+
home = os.path.expanduser("~")
1484+
if val.startswith(home):
1485+
val = "~" + val[len(home):]
1486+
if val:
1487+
parts.append(val)
1488+
if not parts:
1489+
return ""
1490+
display = " ".join(parts)
1491+
return f" {BOLD}{text_color('worktree')}{{{display}}}"
1492+
1493+
14691494
def _render_git_branch(ctx, opts):
14701495
cwd = ctx.get("cwd")
14711496
if not cwd:
@@ -1569,6 +1594,7 @@ def _render_new_line(ctx, opts):
15691594
"tokens": _render_tokens,
15701595
"directory": _render_directory,
15711596
"added_dirs": _render_added_dirs,
1597+
"worktree": _render_worktree,
15721598
"git_branch": _render_git_branch,
15731599
"git_status": _render_git_status,
15741600
"usage_5hour": _render_usage_5hour,
@@ -1613,6 +1639,7 @@ def build_progress_bar(
16131639
usage_weekly_burndown_color="",
16141640
update_info=None,
16151641
added_dirs=None,
1642+
worktree=None,
16161643
):
16171644
"""Build the full status line string"""
16181645
bar_width = max(1, min(128, int(_segment_opts("progress_bar").get("width", "12"))))
@@ -1670,6 +1697,7 @@ def build_progress_bar(
16701697
"usage_weekly_burndown_color": usage_weekly_burndown_color,
16711698
"update_info": update_info,
16721699
"added_dirs": added_dirs or [],
1700+
"worktree": worktree,
16731701
}
16741702

16751703
parts = []
@@ -2123,6 +2151,7 @@ def main():
21232151
model = data.get("model", {}).get("display_name", "Claude")
21242152
cwd = data.get("cwd", "")
21252153
added_dirs = data.get("workspace", {}).get("added_dirs", [])
2154+
worktree = data.get("worktree")
21262155

21272156
# Get context window info
21282157
context_window = data.get("context_window", {})
@@ -2166,6 +2195,7 @@ def main():
21662195
usage_weekly_burndown_color=usage_parts.get("weekly_burndown_color", ""),
21672196
update_info=update_info,
21682197
added_dirs=added_dirs,
2198+
worktree=worktree,
21692199
)
21702200
)
21712201

0 commit comments

Comments
 (0)