feat: update defaults, schema, and installer for new capabilities

Configuration and deployment updates to match the new feature set.

defaults.json:
  - Dark theme palette switched from named ANSI to Dracula-inspired hex:
    success=#50fa7b, warning=#f1fa8c, danger=#ff5555, accent=#8be9fd,
    info=#bd93f9. Light theme unchanged (named ANSI with bold).
  - Glyph characters normalized to Unicode escapes (clean/ahead/behind).
  - Verbose layout (3 lines) reorganized:
    Line 1: model, provider, spacer, lines_changed, project, vcs, beads
    Line 2: context_bar, context_usage, cache_efficiency, spacer, cost,
             cost_velocity, cost_trend, duration
    Line 3: context_trend, tokens_raw, spacer, tools, turns, load,
             cloud_profile, k8s_context, python_env, toolchain
  - context_usage, cost_trend, context_trend now enabled by default.
  - Trend widths increased from 8 to 12 characters.
  - Context bar: bar_style=block, gradient=true.
  - Tools: show_breakdown=true, top_n=7.
  - New sections enabled: cloud_profile (P2), k8s_context (P3, ttl=30),
    python_env (P3), toolchain (P3).

schema.json:
  - Added $schema self-reference field for editor autocomplete.
  - Expanded colorName from fixed enum to freeform string with docs
    covering hex, bg:, modifiers, and palette refs.
  - New section schemas: trendSection, contextTrendSection with width,
    gradient, and threshold properties.
  - context_bar: added bar_style, gradient, fill_char, empty_char.
  - tools: added show_breakdown, top_n, palette array.
  - All section types: added background and placeholder properties.
  - Width description updated to clarify it's a max cap + fallback,
    not an override.
  - colorMatch additionalProperties relaxed from enum to string.

install.sh:
  Complete rewrite for Rust binary workflow:
  - Checks cargo and jq prerequisites.
  - Builds release binary via cargo build --release.
  - Installs to ~/.local/bin/claude-statusline with PATH check.
  - Creates/updates ~/.claude/settings.json with statusLine command
    (type: "command", --color=always, padding: 0).
  - Symlinks statusline.json config if present in project.
  - Removed all bash-script-era logic (symlinks, bash version checks).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Taylor Eernisse
2026-02-09 23:42:58 -05:00
parent c03b0b1bd7
commit 8853afffa1
3 changed files with 399 additions and 98 deletions

View File

@@ -2,6 +2,7 @@
"version": 1,
"global": {
"separator": " | ",
"separator_style": "text",
"justify": "space-between",
"vcs": "auto",
"cache_dir": "/tmp/claude-sl-{session_id}-{cache_version}-{config_hash}",
@@ -14,14 +15,14 @@
},
"colors": {
"dark": {
"success": "green",
"warning": "yellow",
"danger": "red",
"critical": "red bold",
"success": "#50fa7b",
"warning": "#f1fa8c",
"danger": "#ff5555",
"critical": "#ff5555 bold",
"muted": "dim",
"accent": "cyan",
"accent": "#8be9fd",
"highlight": "bold",
"info": "blue"
"info": "#bd93f9"
},
"light": {
"success": "green bold",
@@ -41,9 +42,9 @@
"separator_alt": "",
"branch": "",
"dirty": "*",
"clean": "",
"ahead": "",
"behind": "",
"clean": "\u2713",
"ahead": "\u2191",
"behind": "\u2193",
"folder": "",
"clock": "",
"dollar": ""
@@ -93,24 +94,33 @@
[
"model",
"provider",
"spacer",
"lines_changed",
"project",
"vcs",
"beads"
],
[
"context_bar",
"tokens_raw",
"context_usage",
"cache_efficiency",
"spacer",
"cost",
"cost_velocity"
"cost_velocity",
"cost_trend",
"duration"
],
[
"lines_changed",
"duration",
"context_trend",
"tokens_raw",
"spacer",
"tools",
"turns",
"load",
"version"
"cloud_profile",
"k8s_context",
"python_env",
"toolchain"
]
]
},
@@ -167,6 +177,8 @@
"flex": true,
"min_width": 15,
"bar_width": 10,
"bar_style": "block",
"gradient": true,
"thresholds": {
"warn": 50,
"danger": 70,
@@ -174,7 +186,7 @@
}
},
"context_usage": {
"enabled": false,
"enabled": true,
"priority": 2,
"capacity": 200000,
"thresholds": {
@@ -210,14 +222,16 @@
"priority": 3
},
"cost_trend": {
"enabled": false,
"enabled": true,
"priority": 3,
"width": 8
"width": 12,
"gradient": true
},
"context_trend": {
"enabled": false,
"enabled": true,
"priority": 3,
"width": 8,
"width": 12,
"gradient": true,
"thresholds": {
"warn": 50,
"danger": 70,
@@ -237,6 +251,8 @@
"priority": 2,
"min_width": 6,
"show_last_name": true,
"show_breakdown": true,
"top_n": 7,
"ttl": 2
},
"turns": {
@@ -265,6 +281,23 @@
"hostname": {
"enabled": true,
"priority": 3
},
"cloud_profile": {
"enabled": true,
"priority": 2
},
"k8s_context": {
"enabled": true,
"priority": 3,
"ttl": 30
},
"python_env": {
"enabled": true,
"priority": 3
},
"toolchain": {
"enabled": true,
"priority": 3
}
},
"custom": []

View File

@@ -1,75 +1,109 @@
#!/usr/bin/env bash
# install.sh — Set up claude-statusline symlinks
# install.sh — Build and install claude-statusline (Rust binary)
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
INSTALL_DIR="$HOME/.local/bin"
BINARY_NAME="claude-statusline"
CLAUDE_DIR="$HOME/.claude"
SETTINGS="$CLAUDE_DIR/settings.json"
echo "claude-statusline installer"
echo "==========================="
echo ""
# Check dependencies
# ── Check toolchain ──────────────────────────────────────────────────
if ! command -v cargo &>/dev/null; then
echo "ERROR: cargo (Rust toolchain) is required but not installed."
echo " Install via: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh"
exit 1
fi
echo "[ok] cargo found ($(cargo --version))"
if ! command -v jq &>/dev/null; then
echo "ERROR: jq is required but not installed."
echo "ERROR: jq is required for settings.json updates."
echo " macOS: brew install jq"
echo " Ubuntu: sudo apt install jq"
echo " Fedora: sudo dnf install jq"
exit 1
fi
echo "[ok] jq found"
# Check bash version
if (( BASH_VERSINFO[0] < 4 )); then
echo "WARNING: bash 4+ recommended (you have ${BASH_VERSION})"
echo " macOS ships bash 3. Install bash 4+:"
echo " brew install bash"
# ── Build release binary ─────────────────────────────────────────────
echo ""
echo "Building release binary..."
(cd "$SCRIPT_DIR" && cargo build --release --quiet)
echo "[ok] Built: $(ls -lh "$SCRIPT_DIR/target/release/$BINARY_NAME" | awk '{print $5}')"
# ── Install binary ───────────────────────────────────────────────────
mkdir -p "$INSTALL_DIR"
cp "$SCRIPT_DIR/target/release/$BINARY_NAME" "$INSTALL_DIR/$BINARY_NAME"
chmod +x "$INSTALL_DIR/$BINARY_NAME"
echo "[ok] Installed to $INSTALL_DIR/$BINARY_NAME"
# Verify it's on PATH
if ! command -v "$BINARY_NAME" &>/dev/null; then
echo "[warn] $INSTALL_DIR is not on your PATH"
echo " Add to your shell config: export PATH=\"$INSTALL_DIR:\$PATH\""
fi
# Ensure ~/.claude exists
# ── Configure Claude Code settings.json ──────────────────────────────
echo ""
mkdir -p "$CLAUDE_DIR"
# Create symlinks
create_link() {
local src="$1" dst="$2" name="$3"
if [[ -L "$dst" ]]; then
local existing
existing="$(readlink "$dst")"
if [[ "$existing" == "$src" ]]; then
echo "[ok] $name already linked"
return
fi
echo "[update] $name: updating symlink"
ln -sf "$src" "$dst"
elif [[ -f "$dst" ]]; then
echo "[skip] $name: $dst exists as a regular file"
echo " To use the symlink, rename or remove the existing file first."
return
else
ln -s "$src" "$dst"
echo "[ok] $name linked"
BINARY_PATH="$INSTALL_DIR/$BINARY_NAME"
# The binary runs in a non-TTY context, so force color on.
STATUSLINE_CMD="$BINARY_PATH --color=always"
if [[ -f "$SETTINGS" ]]; then
# Update existing settings.json
CURRENT_CMD=$(jq -r '.statusLine.command // empty' "$SETTINGS" 2>/dev/null || true)
if [[ -n "$CURRENT_CMD" ]]; then
echo "[info] Current statusLine command: $CURRENT_CMD"
fi
}
create_link "$SCRIPT_DIR/statusline.sh" "$CLAUDE_DIR/statusline.sh" "statusline.sh"
# Optionally link user config if they want to start from an example
if [[ ! -f "$CLAUDE_DIR/statusline.json" ]]; then
echo "[info] No statusline.json found. You can copy an example from:"
echo " $SCRIPT_DIR/examples/"
# Write updated settings
TMP="$SETTINGS.tmp.$$"
jq --arg cmd "$STATUSLINE_CMD" '.statusLine = {"type": "command", "command": $cmd, "padding": 0}' "$SETTINGS" > "$TMP"
mv "$TMP" "$SETTINGS"
echo "[ok] Updated statusLine in $SETTINGS"
else
# Create minimal settings.json
jq -n --arg cmd "$STATUSLINE_CMD" '{"statusLine": {"type": "command", "command": $cmd, "padding": 0}}' > "$SETTINGS"
echo "[ok] Created $SETTINGS"
fi
# ── Symlink config ───────────────────────────────────────────────────
CONFIG_SRC="$SCRIPT_DIR/statusline.json"
CONFIG_DST="$CLAUDE_DIR/statusline.json"
if [[ -f "$CONFIG_SRC" ]]; then
if [[ -L "$CONFIG_DST" ]]; then
EXISTING="$(readlink "$CONFIG_DST")"
if [[ "$EXISTING" == "$CONFIG_SRC" ]]; then
echo "[ok] Config already linked"
else
ln -sf "$CONFIG_SRC" "$CONFIG_DST"
echo "[ok] Config symlink updated (was: $EXISTING)"
fi
elif [[ -f "$CONFIG_DST" ]]; then
echo "[skip] $CONFIG_DST exists as a regular file"
echo " To use the symlink, remove it first: rm $CONFIG_DST"
else
ln -s "$CONFIG_SRC" "$CONFIG_DST"
echo "[ok] Config linked: $CONFIG_DST -> $CONFIG_SRC"
fi
else
echo "[info] No statusline.json in project. To customize, create:"
echo " $CONFIG_SRC"
echo ""
echo " Print defaults: $BINARY_NAME --print-defaults"
echo " Print schema: $BINARY_NAME --config-schema"
fi
# ── Done ─────────────────────────────────────────────────────────────
echo ""
echo "Symlinks created. Now add the status line to your Claude Code settings."
echo "Done. Restart Claude Code to see the status line."
echo ""
echo "Add this to ~/.claude/settings.json:"
echo ""
echo ' "statusLine": "'$CLAUDE_DIR'/statusline.sh"'
echo ""
echo "If ~/.claude/settings.json doesn't exist yet, create it:"
echo ""
echo ' {'
echo ' "statusLine": "'$CLAUDE_DIR'/statusline.sh"'
echo ' }'
echo ""
echo "Then restart Claude Code to see the status line."
echo "Quick test: $BINARY_NAME --test --color=always"
echo "Debug: $BINARY_NAME --test --dump-state=json"

View File

@@ -4,11 +4,12 @@
"title": "Claude Code Status Line Configuration",
"description": "Configuration for the claude-statusline status line script",
"type": "object",
"required": [
"version"
],
"additionalProperties": false,
"properties": {
"$schema": {
"type": "string",
"description": "JSON Schema reference for editor support"
},
"version": {
"type": "integer",
"const": 1,
@@ -77,6 +78,15 @@
"token_velocity": {
"$ref": "#/$defs/basicSection"
},
"cost_trend": {
"$ref": "#/$defs/trendSection"
},
"context_trend": {
"$ref": "#/$defs/contextTrendSection"
},
"context_remaining": {
"$ref": "#/$defs/basicSection"
},
"lines_changed": {
"$ref": "#/$defs/basicSection"
},
@@ -154,9 +164,15 @@
"properties": {
"separator": {
"type": "string",
"description": "Separator between sections on a line. When justify is 'left', this is used as-is. When justify is 'spread' or 'space-between', the non-space characters (e.g. '|') are kept as a visual anchor and extra space is added around them.",
"description": "Separator text between sections (used when separator_style is 'text'). When justify is 'spread' or 'space-between', the non-space characters are kept as visual anchors.",
"default": " | "
},
"separator_style": {
"type": "string",
"enum": ["text", "powerline", "arrow", "none"],
"description": "Separator style between sections. 'text': use the separator string. 'powerline': Nerd Font triangle (auto-falls-back to arrow if glyphs disabled). 'arrow': Unicode heavy angle quotation mark. 'none': spaces only.",
"default": "text"
},
"justify": {
"type": "string",
"enum": [
@@ -181,7 +197,7 @@
"width": {
"type": "integer",
"minimum": 40,
"description": "Explicit terminal width override. If omitted, auto-detection walks the process tree to find an ancestor with a real TTY, falling back to stty via /dev/tty, COLUMNS, tput cols, or 120."
"description": "Maximum terminal width cap and fallback. Dynamic sources (ioctl, process tree, stty) take priority when available."
},
"width_margin": {
"type": "integer",
@@ -227,22 +243,11 @@
},
"colorName": {
"type": "string",
"enum": [
"red",
"green",
"yellow",
"blue",
"magenta",
"cyan",
"white",
"dim",
"bold"
],
"description": "Named ANSI color"
"description": "Color specifier. Supports: named colors (red, green, yellow, blue, magenta, cyan, white), modifiers (dim, bold, italic, underline, strikethrough), hex (#FF6B35, #F00), backgrounds (bg:red, bg:#FF6B35), palette refs (p:success). Multiple can be space-separated (e.g., '#FF6B35 bold')."
},
"colorPalette": {
"type": "object",
"description": "Semantic color palette for a theme",
"description": "Semantic color palette for a theme. Values support all colorName formats.",
"properties": {
"success": { "type": "string" },
"warning": { "type": "string" },
@@ -343,6 +348,14 @@
},
"color": {
"$ref": "#/$defs/colorName"
},
"background": {
"$ref": "#/$defs/colorName",
"description": "Background color for the section. Accepts named colors (green, red), hex (#50fa7b), or palette refs (p:success). Applied as a background wrap around the section output, preserving foreground colors."
},
"placeholder": {
"type": ["string", "null"],
"description": "Text shown when the section has no data (e.g. after /clear). Set to empty string or null to hide the section instead. Default: '--'."
}
},
"additionalProperties": false
@@ -415,6 +428,14 @@
},
"color": {
"$ref": "#/$defs/colorName"
},
"background": {
"$ref": "#/$defs/colorName",
"description": "Background color for the section. Accepts named colors (green, red), hex (#50fa7b), or palette refs (p:success). Applied as a background wrap around the section output, preserving foreground colors."
},
"placeholder": {
"type": ["string", "null"],
"description": "Text shown when the section has no data (e.g. after /clear). Set to empty string or null to hide the section instead. Default: '--'."
}
},
"additionalProperties": false
@@ -475,6 +496,14 @@
},
"color": {
"$ref": "#/$defs/colorName"
},
"background": {
"$ref": "#/$defs/colorName",
"description": "Background color for the section. Accepts named colors (green, red), hex (#50fa7b), or palette refs (p:success). Applied as a background wrap around the section output, preserving foreground colors."
},
"placeholder": {
"type": ["string", "null"],
"description": "Text shown when the section has no data (e.g. after /clear). Set to empty string or null to hide the section instead. Default: '--'."
}
},
"additionalProperties": false
@@ -503,6 +532,25 @@
"minimum": 3,
"default": 10
},
"bar_style": {
"type": "string",
"enum": ["classic", "block"],
"default": "block",
"description": "Bar rendering style. 'classic': = and - characters. 'block': Unicode block characters with optional gradient."
},
"gradient": {
"type": "boolean",
"default": true,
"description": "Enable per-character gradient coloring (green to yellow to red). Only applies when bar_style is 'block'."
},
"fill_char": {
"type": "string",
"description": "Override character for filled portion. Default: '=' (classic) or full block (block)."
},
"empty_char": {
"type": "string",
"description": "Override character for empty portion. Default: '-' (classic) or light shade (block)."
},
"thresholds": {
"$ref": "#/$defs/thresholds"
},
@@ -523,6 +571,14 @@
},
"color": {
"$ref": "#/$defs/colorName"
},
"background": {
"$ref": "#/$defs/colorName",
"description": "Background color for the section. Accepts named colors (green, red), hex (#50fa7b), or palette refs (p:success). Applied as a background wrap around the section output, preserving foreground colors."
},
"placeholder": {
"type": ["string", "null"],
"description": "Text shown when the section has no data (e.g. after /clear). Set to empty string or null to hide the section instead. Default: '--'."
}
},
"additionalProperties": false
@@ -563,6 +619,14 @@
},
"color": {
"$ref": "#/$defs/colorName"
},
"background": {
"$ref": "#/$defs/colorName",
"description": "Background color for the section. Accepts named colors (green, red), hex (#50fa7b), or palette refs (p:success). Applied as a background wrap around the section output, preserving foreground colors."
},
"placeholder": {
"type": ["string", "null"],
"description": "Text shown when the section has no data (e.g. after /clear). Set to empty string or null to hide the section instead. Default: '--'."
}
},
"additionalProperties": false
@@ -599,6 +663,14 @@
},
"color": {
"$ref": "#/$defs/colorName"
},
"background": {
"$ref": "#/$defs/colorName",
"description": "Background color for the section. Accepts named colors (green, red), hex (#50fa7b), or palette refs (p:success). Applied as a background wrap around the section output, preserving foreground colors."
},
"placeholder": {
"type": ["string", "null"],
"description": "Text shown when the section has no data (e.g. after /clear). Set to empty string or null to hide the section instead. Default: '--'."
}
},
"additionalProperties": false
@@ -633,6 +705,135 @@
},
"color": {
"$ref": "#/$defs/colorName"
},
"background": {
"$ref": "#/$defs/colorName",
"description": "Background color for the section. Accepts named colors (green, red), hex (#50fa7b), or palette refs (p:success). Applied as a background wrap around the section output, preserving foreground colors."
},
"placeholder": {
"type": ["string", "null"],
"description": "Text shown when the section has no data (e.g. after /clear). Set to empty string or null to hide the section instead. Default: '--'."
}
},
"additionalProperties": false
},
"trendSection": {
"type": "object",
"description": "Sparkline trend visualization",
"properties": {
"enabled": {
"type": "boolean",
"default": true
},
"priority": {
"$ref": "#/$defs/priority"
},
"width": {
"type": "integer",
"minimum": 3,
"default": 8,
"description": "Number of sparkline characters"
},
"gradient": {
"type": "boolean",
"default": true,
"description": "Enable per-character gradient coloring based on value"
},
"flex": {
"type": "boolean",
"default": false
},
"min_width": {
"type": "integer",
"minimum": 0
},
"prefix": {
"type": "string"
},
"suffix": {
"type": "string"
},
"pad": {
"type": "integer",
"minimum": 1
},
"align": {
"type": "string",
"enum": ["left", "right", "center"],
"default": "left"
},
"color": {
"$ref": "#/$defs/colorName"
},
"background": {
"$ref": "#/$defs/colorName",
"description": "Background color for the section. Accepts named colors (green, red), hex (#50fa7b), or palette refs (p:success). Applied as a background wrap around the section output, preserving foreground colors."
},
"placeholder": {
"type": ["string", "null"],
"description": "Text shown when the section has no data (e.g. after /clear). Set to empty string or null to hide the section instead. Default: '--'."
}
},
"additionalProperties": false
},
"contextTrendSection": {
"type": "object",
"description": "Context usage sparkline trend with threshold coloring",
"properties": {
"enabled": {
"type": "boolean",
"default": true
},
"priority": {
"$ref": "#/$defs/priority"
},
"width": {
"type": "integer",
"minimum": 3,
"default": 8,
"description": "Number of sparkline characters"
},
"gradient": {
"type": "boolean",
"default": true,
"description": "Enable per-character gradient coloring based on value"
},
"thresholds": {
"$ref": "#/$defs/thresholds"
},
"flex": {
"type": "boolean",
"default": false
},
"min_width": {
"type": "integer",
"minimum": 0
},
"prefix": {
"type": "string"
},
"suffix": {
"type": "string"
},
"pad": {
"type": "integer",
"minimum": 1
},
"align": {
"type": "string",
"enum": ["left", "right", "center"],
"default": "left"
},
"color": {
"$ref": "#/$defs/colorName"
},
"background": {
"$ref": "#/$defs/colorName",
"description": "Background color for the section. Accepts named colors (green, red), hex (#50fa7b), or palette refs (p:success). Applied as a background wrap around the section output, preserving foreground colors."
},
"placeholder": {
"type": ["string", "null"],
"description": "Text shown when the section has no data (e.g. after /clear). Set to empty string or null to hide the section instead. Default: '--'."
}
},
"additionalProperties": false
@@ -656,6 +857,26 @@
"type": "boolean",
"default": true
},
"show_breakdown": {
"type": "boolean",
"description": "Show per-tool breakdown in wide terminals (e.g. Bash: 84/Read: 35/Edit: 34)",
"default": true
},
"top_n": {
"type": "integer",
"minimum": 0,
"description": "Max number of tools to show in breakdown (0 = all). Adaptively reduced to fit terminal width.",
"default": 7
},
"palette": {
"type": "array",
"items": {
"type": "string",
"description": "Hex color string (#RRGGBB or #RGB)"
},
"description": "Rotating color palette for tool names. Override to customize; leave empty (default) to auto-detect from terminal config (WezTerm/Kitty/Alacritty) or fall back to built-in Dracula palette.",
"default": []
},
"ttl": {
"type": "number",
"default": 2
@@ -677,6 +898,14 @@
},
"color": {
"$ref": "#/$defs/colorName"
},
"background": {
"$ref": "#/$defs/colorName",
"description": "Background color for the section. Accepts named colors (green, red), hex (#50fa7b), or palette refs (p:success). Applied as a background wrap around the section output, preserving foreground colors."
},
"placeholder": {
"type": ["string", "null"],
"description": "Text shown when the section has no data (e.g. after /clear). Set to empty string or null to hide the section instead. Default: '--'."
}
},
"additionalProperties": false
@@ -711,6 +940,14 @@
},
"color": {
"$ref": "#/$defs/colorName"
},
"background": {
"$ref": "#/$defs/colorName",
"description": "Background color for the section. Accepts named colors (green, red), hex (#50fa7b), or palette refs (p:success). Applied as a background wrap around the section output, preserving foreground colors."
},
"placeholder": {
"type": ["string", "null"],
"description": "Text shown when the section has no data (e.g. after /clear). Set to empty string or null to hide the section instead. Default: '--'."
}
},
"additionalProperties": false
@@ -747,26 +984,23 @@
},
"color": {
"$ref": "#/$defs/colorName"
},
"background": {
"$ref": "#/$defs/colorName",
"description": "Background color for the section. Accepts named colors (green, red), hex (#50fa7b), or palette refs (p:success). Applied as a background wrap around the section output, preserving foreground colors."
},
"placeholder": {
"type": ["string", "null"],
"description": "Text shown when the section has no data (e.g. after /clear). Set to empty string or null to hide the section instead. Default: '--'."
}
},
"additionalProperties": false
},
"colorMatch": {
"type": "object",
"description": "Map of output value to color name",
"description": "Map of output value to color specifier",
"additionalProperties": {
"type": "string",
"enum": [
"red",
"green",
"yellow",
"blue",
"magenta",
"cyan",
"white",
"dim",
"bold"
]
"type": "string"
}
},
"customCommand": {