agentsclimarketplace

Statusline creator

Skill lirantal/statusline-creator-skill/skills/statusline-creator

Builds a Claude Code statusline from scratch or extends an existing one. A statusline is a live status bar that runs a shell script after every Claude response and displays data — security scans, git status, API quotas, build state — directly in the session. Use this skill when the user says "create a statusline", "make a statusline for X", "add X to my statusline", "show Y in the status bar", "build a statusline plugin", or "I want live data in my Claude session". Trigger even if the user just describes wanting live feedback during a Claude session without naming "statusline" explicitly. The skill drives from idea to a working, installed, tested statusline without requiring the user to direct every step.From its SKILL.md

Install
npx -y skills add lirantal/statusline-creator-skill --skill statusline-creator

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its file declares

Copied from the file, not written here

The file declares its own license as Apache-2.0. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

18.3 KB, ~4.4k tokens by cl100k_base, as published. Nobody here has run it

Statusline Creator

Instructions

A Claude Code statusline is a bash script that Claude runs after every response. It reads session metadata on stdin and prints a colored line to stdout — giving you live, contextual data without leaving the session.

The key design challenge: the statusline must render instantly, every time. There are two valid execution models — choose based on measured speed:

  • Inline execution: run the command directly in the script. Only viable if the operation reliably completes in under ~300ms (fast HTTP endpoints, local file reads, cheap shell commands). Must be benchmarked first — never assume an operation is fast.
  • Background + cache: the script always renders from cache; a background process refreshes it on a TTL. Required for any operation that is slow, variable, or potentially blocking (CLI scanners, external APIs, network calls to remote services).

Before choosing a model, benchmark the operation — see Step 1.

Read references/protocol.md at the start of every session — it covers the stdin JSON schema, the settings.json format, and ANSI output rules. The authoritative reference for the protocol is the official Claude Code statusline documentation at https://code.claude.com/docs/en/statusline.md — check it for new fields or behaviors not yet reflected in the reference files. Read references/implementation-patterns.md when writing or debugging the script — it covers the caching pattern, exit code handling, state machine, and jq recipes.


Step 1: Understand what the user wants to show

Ask the user what data source they want in their statusline. Common categories:

  • Security toolssnyk test, trivy, semgrep, SAST/SCA/IaC scanners
  • Git/project state — branch, dirty files, CI status, PR state
  • Resource usage — API quotas, context window, cost, rate limits
  • Build/test state — last test run result, coverage, lint status
  • Cloud/infra — Kubernetes pod state, deploy status, environment

For each data source, establish:

  1. What CLI command or HTTP request produces the data?
  2. Benchmark its actual runtime — run it 3 times and record the wall-clock time:
    time <command>   # run 3× and note the range
    
    • Under ~300ms consistently → inline execution is viable
    • Over ~300ms, variable, or potentially blocking → use background + cache
  3. What output format does it produce? (JSON, plain text, exit code only?)
  4. What states should the statusline show? (clean, issues found, scanning, unavailable, auth error)

Done when: You have a concrete data source, its command, its output format, and a list of states.


Step 2: Design the output line

Plan the display format before writing any code. A good statusline line has:

  • A label identifying the tool (e.g. 🔒 snyk, 🌿 git, ☁ k8s)
  • Segments separated by — one per logical data group
  • Color-coded counts or status — use severity/urgency to drive color choice
  • A scan age indicator when results come from a cache (· 5m ago)
  • A spinner when a background refresh is running

Design the full set of states the script will display:

label │ <primary result> │ <secondary result> │ <project> · <age> ⟳
label │ ✔ clean │ project · 2m ago
label │ scanning...
label │ no supported files
label │ ⚠ auth required  run: <cmd>

Use ANSI RGB colors. Standard palette for security tools (see references/implementation-patterns.md):

  • Critical → Red \033[38;2;255;85;85m
  • High → Orange \033[38;2;255;165;0m
  • Medium → Yellow \033[38;2;255;215;0m
  • Clean/OK → Green \033[38;2;80;250;123m
  • Label/dim → \033[38;2;100;170;255m / \033[38;2;128;128;128m

Done when: Every state the script can be in has a corresponding output line designed.


Step 3: Implement the script

Create statusline.sh in the project root. The structure depends on the execution model chosen in Step 1 (based on the benchmark results).

If using inline execution (operation benchmarked at <300ms):

  1. Configuration block — env vars with defaults (TOOL_BIN, display toggles)
  2. Color constants — define all ANSI codes as variables; never inline raw escapes
  3. Read stdinSESSION=$(cat) — captures Claude's JSON; parse fields as needed
  4. Run the operation directly — execute the command and capture its output/exit code
  5. Build segment functions — one per data source; handle unavailable/auth error states
  6. Compose final outputprintf the assembled line with separators

If using background + cache (operation benchmarked at >300ms or variable):

  1. Configuration block — env vars with defaults (TOOL_BIN, CACHE_TTL, display toggles)
  2. Color constants — define all ANSI codes as variables; never inline raw escapes
  3. Read stdinSESSION=$(cat) — captures Claude's JSON; parse fields as needed
  4. Determine project rootgit rev-parse --show-toplevel 2>/dev/null || pwd
  5. Cache paths — hash the project path with cksum for stable, per-project cache keys
  6. file_age() helper — returns age in seconds of any file; 999999 if missing
  7. trigger_scan_bg() function — atomic mkdir-based lock, runs scan in subshell, captures exit code explicitly, writes noscan sentinel on exit 3
  8. Trigger stale scans — compare age to TTL; call trigger functions
  9. Build segment functions — one per data source; check cache/noscan/lock states and return the formatted string for that segment
  10. Compose final outputprintf the assembled line with separators

See references/implementation-patterns.md for full patterns for both models.

Critical rules:

  • Use || exit_code=$? to capture exit codes — never || true, which loses them
  • Some CLI tools use exit code 3 to mean "no supported project/files" (distinct from a scan error) — detect this and write a .noscan sentinel rather than retrying indefinitely
  • Use mkdir for locking (POSIX-atomic), not flock or file touches
  • Make a single jq call per cache file — multiple calls on the same file are slow
  • The script runs in Claude's cwd — use pwd / git rev-parse to find the project

Make the script executable: chmod +x statusline.sh

Configuration env vars — naming convention: Every user-tunable knob should be an env var with a sane default. Use a consistent naming prefix so vars are discoverable:

# In statusline.sh — configuration block at the top
TOOL_BIN="${TOOL_BIN:-tool}"                   # path to the CLI binary
TOOL_STATUSLINE_TTL="${TOOL_STATUSLINE_TTL:-300}"  # cache TTL in seconds
TOOL_SHOW_LOW="${TOOL_SHOW_LOW:-false}"        # boolean display toggle
TOOL_SCAN_ARGS="${TOOL_SCAN_ARGS:-}"           # extra args forwarded to scan command

Users can set these in their shell profile or in ~/.claude/settings.json under "env" — document both paths. The TOOL_BIN var is especially important on macOS where tools installed via nvm, fnm, rbenv, or pyenv are only in PATH in interactive shells; background subshells run by the statusline may not inherit that PATH. Always document the TOOL_BIN override and recommend pinning it if the tool is installed via a version manager:

export TOOL_BIN=$(which tool)   # resolve once in your shell profile

Done when: The script runs without errors from echo '{}' | ./statusline.sh in the project directory.


Step 4: Install into Claude Code

Add the statusline to ~/.claude/settings.json. The value must be an object, not a string — a plain string path will cause a settings error on startup:

{
  "statusLine": {
    "type": "command",
    "command": "/absolute/path/to/statusline.sh"
  }
}

Use jq to update non-destructively (preserves all other settings):

jq --arg p "/absolute/path/to/statusline.sh" \
   '.statusLine = {"type": "command", "command": $p}' \
   ~/.claude/settings.json > ~/.claude/settings.json.tmp \
   && mv ~/.claude/settings.json.tmp ~/.claude/settings.json

Done when: cat ~/.claude/settings.json shows the statusLine object with the correct absolute path.


Step 5: Create install.sh

Always create an install.sh in the project root. This is not optional — every statusline needs one so users can install and uninstall it without manually editing JSON. Use assets/install.sh.template as the starting point and fill in the placeholders.

A correct installer must:

  1. Use colored output — define R/GREEN/YELLOW/RED/BOLD ANSI variables and ok()/info()/die() helpers that use them; use printf not echo; print a bold tool name header at the start
  2. Check that required CLI tools are installed (with install hints if missing)
  3. Run an auth preflight — verify the tool is authenticated before touching settings
  4. Update ~/.claude/settings.json non-destructively using jq
  5. Support --remove to cleanly uninstall
  6. Print the env vars users can set to configure behavior

Auth preflight — important distinction: there are two different commands for every authenticated tool:

  • A check command (e.g. tool whoami, gh auth status) — safe to run anytime; exit 0 = authenticated
  • A do-auth command (e.g. tool auth, gh auth login) — only run this if not authenticated; on some tools re-running when already authed resets or replaces credentials

The installer should run the check command and only instruct the user to run the do-auth command if the check fails. Never silently run the auth command unconditionally.

Make it executable: chmod +x install.sh

Done when: ./install.sh runs cleanly with colored output and ./install.sh --remove cleanly removes the statusLine entry.


Step 6: Test every state

Do not rely on the live statusline alone — seed the cache manually to exercise every state:

CACHE_DIR="$HOME/.cache/<tool>-statusline"
HASH=$(printf '%s' "$(git rev-parse --show-toplevel)" | cksum | cut -d' ' -f1)

# Test: clean/no issues
echo '<valid empty result JSON>' > "$CACHE_DIR/${HASH}.json"
echo '{}' | ./statusline.sh

# Test: issues found
echo '<valid result JSON with findings>' > "$CACHE_DIR/${HASH}.json"
echo '{}' | ./statusline.sh

# Test: no supported project
printf '3' > "$CACHE_DIR/${HASH}.noscan"; rm -f "$CACHE_DIR/${HASH}.json"
echo '{}' | ./statusline.sh

# Test: scanning in progress
mkdir "$CACHE_DIR/${HASH}.lock" 2>/dev/null
rm -f "$CACHE_DIR/${HASH}.json" "$CACHE_DIR/${HASH}.noscan"
echo '{}' | ./statusline.sh
rmdir "$CACHE_DIR/${HASH}.lock"

# Test: auth error
echo 'Authentication failed' > "$CACHE_DIR/${HASH}.err"
echo '{}' | ./statusline.sh

Check the raw output for correct ANSI escape codes:

echo '{}' | ./statusline.sh | cat -v

^[[38;2;...m in the output means ANSI is working correctly.

Repeat testing in the project directory where the data source is valid, and in a directory where no supported files exist, to confirm the noscan path works.

Done when: All designed states produce the expected output line with correct colors and content.


Step 7: Write documentation

Create a README.md covering:

  1. What it shows — annotated output line explaining every token
  2. Why it's useful — the problem it solves for a developer, not a feature list
  3. What scans/data sources run — and why those specifically
  4. Prerequisites — tools required, accounts, authentication
  5. Quick start — clone → auth check → ./install.sh (three steps max)
  6. Manual installation — direct settings.json snippet for users who don't want the installer
  7. Uninstall./install.sh --remove and how to clear the cache
  8. Configuration — table of env vars with defaults, and example of setting them via settings.json "env" block
  9. Troubleshooting — how to read the error log, force a cache refresh, fix auth; include the binary PATH pinning note for tools installed via version managers

See assets/README.md.template for a ready-to-fill skeleton with all sections pre-structured.

Annotate the output line token by token — users shouldn't have to guess what H:4 M:2 (6↑) means:

🔒 snyk │ deps H:4 M:2 (6↑) │ code H:2 M:3 │ test-project · 5m ago ⟳
          ^^^^ ^^^  ^^^  ^^^^   ^^^^ ^^^  ^^^   ^^^^^^^^^^^^^ ^^^^^^^^ ^
          [A]  [B]  [C]  [D]   [E]  [F]  [G]       [H]          [I]   [J]

[A] scan type label, [B][C] severity counts, [D] fixable, [E–G] same for second scan, [H] project name, [I] cache age, [J] background scan active

Done when: A developer unfamiliar with the project can install and use the statusline using the README alone.


Examples

Example 1: Building a security statusline from scratch

User says: "I want to see Snyk vulnerability counts in my Claude statusline."

Actions:

  1. Confirm data source: snyk test --json (SCA) and snyk code test --json (SAST)
  2. Run both commands in the test project to capture real output; note JSON structures, exit codes (0=clean, 1=vulns, 3=no manifest), and scan duration
  3. Design two segments: deps H:N M:N (N↑) and code H:N M:N, plus · Xs ago ⟳
  4. Implement statusline.sh with two background scan functions and separate caches; parse SCA with .vulnerabilities[] and SAST with .runs[0].results[] (SARIF)
  5. Install into ~/.claude/settings.json using the {type, command} object format
  6. Test all states by seeding cache files; verify ANSI codes with cat -v
  7. Write README with annotated output line and troubleshooting section

Result: A single status bar line showing live dep and code security status, refreshing every 5 minutes in the background, with zero impact on Claude's response time.


Example 2: Adding a second data source to an existing statusline

User says: "My statusline shows git branch — can I also add the last CI run status?"

Actions:

  1. Read the existing statusline.sh to understand its cache and output structure
  2. Identify the CI data source (e.g. gh run list --limit 1 --json status,conclusion)
  3. Add new cache variables (CI_CACHE, CI_LOCK, CI_NOSCAN) following the existing pattern
  4. Add a trigger_ci_bg() function using the same atomic-lock pattern
  5. Add a build_ci_segment() function; handle the case where gh is not authenticated
  6. Compose the new segment into the existing printf output line
  7. Test: seed CI cache with a passing result, a failing result, and no GH auth

Result: The statusline now shows both git state and CI status on one line, each refreshing independently, without either scan blocking the other.


Example 3: Debugging a statusline stuck on "scanning..."

User says: "My statusline has been saying 'scanning...' for 20 minutes."

Actions:

  1. Check whether the lock directory is still present: ls ~/.cache/<tool>-statusline/*.lock
  2. If the lock exists but no background process is running, it's a stale lock from a crashed scan — remove it: rmdir ~/.cache/<tool>-statusline/*.lock
  3. Run the scan command manually in the project dir to check for errors: snyk test --json 2>&1 | head -20
  4. Check the error log: cat ~/.cache/<tool>-statusline/*.err
  5. Common root causes: auth expired (re-run snyk auth), no manifest in directory (statusline should show "no manifest" not "scanning"), background process killed
  6. Force a fresh scan by deleting the cache: rm -rf ~/.cache/<tool>-statusline/
  7. Re-run echo '{}' | ./statusline.sh and watch for the "initializing..." → "scanning..." → result progression

Result: Root cause identified and resolved; statusline shows current data.


Troubleshooting

Error: Settings Error: statusLine: Expected object, but received string Cause: settings.json has "statusLine": "/path/to/script" as a plain string. Solution: Change it to the object format: "statusLine": {"type": "command", "command": "/path/to/script"}. Run the jq update command from Step 4.


Error: Statusline stuck on "scanning..." indefinitely. Cause: A stale lock directory left by a crashed background scan process. Solution: Delete the lock: rmdir ~/.cache/<tool>-statusline/*.lock. The next render will trigger a fresh scan.


Error: Statusline shows "initializing..." after every Claude restart. Cause: The cache TTL has expired between sessions, or the cache directory is being cleared on restart. Solution: Increase CACHE_TTL (e.g. export TOOL_STATUSLINE_TTL=3600), or check whether something is clearing ~/.cache on startup.


Error: Colors don't render — raw escape codes like ^[[38;2;... show in the bar. Cause: The terminal or Claude Code version doesn't support RGB ANSI. Less likely: escape codes are being double-escaped. Solution: Verify codes are defined as $'...' literals (e.g. RED=$'\033[38;2;255;85;85m'), not as regular strings with \033. Use cat -v to inspect raw output.


Error: Background scan never completes — cache file never appears. Cause: The scan command is failing silently. The || true pattern suppresses the exit code, so errors go undetected. Solution: Run the scan command manually. Check the .err file in the cache dir. Ensure exit code 3 ("no supported project") writes a .noscan sentinel instead of being treated as a generic failure — otherwise the script retries every render.

What ships with it: 4 files

29.2 KB alongside SKILL.md

Keep looking

Skills are one crate of 326,851. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.