claude-code-status-line 1.2.9

A configurable status line for Claude Code with powerline arrows, context tracking, and quota monitoring
Documentation
use std::fs;
use std::path::PathBuf;

use claude_code_status_line::app::{parse_claude_input, render_statusline_with_width};
use claude_code_status_line::config::Config;

fn fixture_path(name: &str) -> PathBuf {
    PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("tests")
        .join("fixtures")
        .join("statusline")
        .join(name)
}

fn load_fixture(name: &str) -> String {
    fs::read_to_string(fixture_path(name)).expect("fixture should be readable")
}

fn test_config() -> Config {
    let mut config = Config::default();
    config.sections.git.enabled = false;
    config.sections.quota.enabled = false;
    config.sections.cost.enabled = false;
    config.sections.model.show_thinking_mode = false;
    config.display.show_background = false;
    config.display.multiline = false;
    config
}

fn strip_ansi(input: &str) -> String {
    let mut result = String::new();
    let mut chars = input.chars().peekable();

    while let Some(ch) = chars.next() {
        if ch == '\x1b' {
            if chars.peek() == Some(&'[') {
                chars.next();
                for next in chars.by_ref() {
                    if ('@'..='~').contains(&next) {
                        break;
                    }
                }
            }
        } else {
            result.push(ch);
        }
    }

    result
}

fn render_fixture(name: &str, config: &Config) -> String {
    let input = parse_claude_input(&load_fixture(name)).expect("fixture should parse");
    let lines = render_statusline_with_width(&input, config, 200);
    strip_ansi(&lines.join("\n"))
}

#[test]
fn renders_legacy_workspace_only_fixture() {
    let output = render_fixture("legacy-workspace-only.json", &test_config());

    assert!(output.contains("legacy-repo"));
    assert!(output.contains("Sonnet 4.5"));
    assert!(output.contains("ctx: ~7%"));
    assert!(output.contains("200k"));
}

#[test]
fn renders_direct_percentages_without_estimate_marker() {
    let output = render_fixture("direct-percentages.json", &test_config());

    assert!(output.contains("ctx: 8%"));
    assert!(!output.contains("ctx: ~8%"));
}

#[test]
fn renders_workspace_added_dirs_as_compact_scope_detail() {
    let output = render_fixture("workspace-added-dirs.json", &test_config());

    assert!(output.contains("project"));
    assert!(output.contains("+2 dirs"));
}

#[test]
fn renders_worktree_detail_when_git_section_is_unavailable() {
    let output = render_fixture("worktree-session.json", &test_config());

    assert!(output.contains("wt: feature-a"));
}

#[test]
fn renders_one_million_context_totals_without_fallback_to_200k() {
    let output = render_fixture("context-1m.json", &test_config());

    assert!(output.contains("ctx: 12.5%") || output.contains("ctx: 12%"));
    assert!(output.contains("1000k"));
    assert!(!output.contains("200k"));
}

#[test]
fn renders_direct_percentage_without_fake_totals_when_size_missing() {
    let output = render_fixture("context-missing-size.json", &test_config());

    assert!(output.contains("ctx: 12%"));
    assert!(!output.contains("k/"));
}

#[test]
fn renders_model_without_empty_output_style_details() {
    let mut config = test_config();
    config.sections.model.show_output_style = true;

    let output = render_fixture("model-no-output-style.json", &config);

    assert!(output.contains("Opus 4.6"));
    assert!(!output.contains("()"));
}