Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 123 additions & 0 deletions ROB-SETUP-GUIDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# Rob's ccstatusline Setup Guide

This guide shows you how to configure ccstatusline to match my exact setup: a single-line status bar showing the model name, context length, block/session usage with cost estimates and reset timers, and weekly usage with reset day.

**What it looks like:**

```
Claude Opus 4.6 | 42.3k | Block: 18.2% (~$14/$89) resets in 3hr 45m | Weekly: 12.0% resets Friday
```

## Prerequisites

This setup uses API usage widgets that require a Claude Pro/Max subscription (they read from the Anthropic usage API using your OAuth credentials).

## Step 1: Install from this fork

```bash
# Clone the fork (has the API usage widgets)
git clone https://github.com/robjampar/ccstatusline.git
cd ccstatusline
bun install
```

## Step 2: Configure Claude Code to use it

Add this to your `~/.claude/settings.json`:

```json
{
"statusLine": "bun run /path/to/ccstatusline/src/ccstatusline.ts"
}
```

Replace `/path/to/ccstatusline` with the actual path where you cloned the repo.

## Step 3: Apply the settings

Copy this JSON to `~/.config/ccstatusline/settings.json`:

```json
{
"version": 3,
"lines": [
[
{
"id": "1",
"type": "model",
"color": "cyan",
"rawValue": true
},
{
"id": "2",
"type": "separator"
},
{
"id": "3",
"type": "context-length",
"color": "brightBlack"
},
{
"id": "4",
"type": "separator"
},
{
"id": "su1",
"type": "session-usage",
"color": "yellow",
"rawValue": true
},
{
"id": "5",
"type": "separator"
},
{
"id": "wu1",
"type": "weekly-usage",
"color": "cyan",
"rawValue": true
}
]
],
"flexMode": "full-minus-40",
"compactThreshold": 60,
"colorLevel": 2,
"inheritSeparatorColors": false,
"globalBold": false,
"powerline": {
"enabled": false,
"separators": [""],
"separatorInvertBackground": [false],
"startCaps": [],
"endCaps": [],
"autoAlign": false
}
}
```

## Widget breakdown

| Widget | Type | Color | Raw Value | What it shows |
|--------|------|-------|-----------|---------------|
| Model | `model` | cyan | yes | Model name without label, e.g. `Claude Opus 4.6` |
| Context Length | `context-length` | brightBlack (gray) | no | Current context token count, e.g. `Ctx: 42.3k` |
| Session Usage | `session-usage` | yellow | yes | Block usage %, estimated cost, and reset countdown, e.g. `Block: 18.2% (~$14/$89) resets in 3hr 45m` |
| Weekly Usage | `weekly-usage` | cyan | yes | Weekly usage % and reset day, e.g. `Weekly: 12.0% resets Friday` |

### Key settings

- **`rawValue: true`** on model removes the "Model:" label prefix
- **`rawValue: true`** on session-usage switches from a progress bar to the compact text format with cost estimates and reset timer
- **`rawValue: true`** on weekly-usage switches from a progress bar to the compact text format with reset day
- **`flexMode: full-minus-40`** reserves space so the auto-compact message doesn't wrap
- **`colorLevel: 2`** enables 256-color mode

## Customizing further

Run the TUI to interactively adjust widgets, colors, and layout:

```bash
bun run /path/to/ccstatusline/src/ccstatusline.ts
```

(Run it without piped input to get the interactive configuration UI.)
13 changes: 11 additions & 2 deletions src/ccstatusline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import chalk from 'chalk';
import { runTUI } from './tui';
import type {
BlockMetrics,
BlockTokenMetrics,
TokenMetrics
} from './types';
import type { RenderContext } from './types/RenderContext';
Expand All @@ -16,6 +17,7 @@ import {
} from './utils/config';
import {
getBlockMetrics,
getBlockTokenMetrics,
getSessionDuration,
getTokenMetrics
} from './utils/jsonl';
Expand Down Expand Up @@ -72,8 +74,9 @@ async function renderMultipleLines(data: StatusJSON) {
// Check if session clock is needed
const hasSessionClock = lines.some(line => line.some(item => item.type === 'session-clock'));

// Check if block timer is needed
const hasBlockTimer = lines.some(line => line.some(item => item.type === 'block-timer'));
// Check if block timer or block usage is needed
const hasBlockTimer = lines.some(line => line.some(item => item.type === 'block-timer' || item.type === 'block-usage'));
const hasBlockUsage = lines.some(line => line.some(item => item.type === 'block-usage'));

let tokenMetrics: TokenMetrics | null = null;
if (hasTokenItems && data.transcript_path) {
Expand All @@ -90,12 +93,18 @@ async function renderMultipleLines(data: StatusJSON) {
blockMetrics = getBlockMetrics();
}

let blockTokenMetrics: BlockTokenMetrics | null = null;
if (hasBlockUsage && blockMetrics) {
blockTokenMetrics = getBlockTokenMetrics(blockMetrics);
}

// Create render context
const context: RenderContext = {
data,
tokenMetrics,
sessionDuration,
blockMetrics,
blockTokenMetrics,
isPreview: false
};

Expand Down
6 changes: 5 additions & 1 deletion src/types/RenderContext.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import type { BlockMetrics } from '../types';

import type { StatusJSON } from './StatusJSON';
import type { TokenMetrics } from './TokenMetrics';
import type {
BlockTokenMetrics,
TokenMetrics
} from './TokenMetrics';

export interface RenderContext {
data?: StatusJSON;
tokenMetrics?: TokenMetrics | null;
sessionDuration?: string | null;
blockMetrics?: BlockMetrics | null;
blockTokenMetrics?: BlockTokenMetrics | null;
terminalWidth?: number | null;
isPreview?: boolean;
lineIndex?: number; // Index of the current line being rendered (for theme cycling)
Expand Down
18 changes: 17 additions & 1 deletion src/types/StatusJSON.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,23 @@ export const StatusJSONSchema = z.looseObject({
total_api_duration_ms: z.number().optional(),
total_lines_added: z.number().optional(),
total_lines_removed: z.number().optional()
}).optional()
}).optional(),
context_window: z.object({
context_window_size: z.number().nullable().optional(),
total_input_tokens: z.number().nullable().optional(),
total_output_tokens: z.number().nullable().optional(),
current_usage: z.union([
z.number(),
z.object({
input_tokens: z.number().optional(),
output_tokens: z.number().optional(),
cache_creation_input_tokens: z.number().optional(),
cache_read_input_tokens: z.number().optional()
})
]).nullable().optional(),
used_percentage: z.number().nullable().optional(),
remaining_percentage: z.number().nullable().optional()
}).nullable().optional()
});

export type StatusJSON = z.infer<typeof StatusJSONSchema>;
16 changes: 15 additions & 1 deletion src/types/TokenMetrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export interface TokenUsage {
}

export interface TranscriptLine {
message?: { usage?: TokenUsage };
message?: { model?: string; usage?: TokenUsage };
isSidechain?: boolean;
timestamp?: string;
isApiErrorMessage?: boolean;
Expand All @@ -18,4 +18,18 @@ export interface TokenMetrics {
cachedTokens: number;
totalTokens: number;
contextLength: number;
}

export interface BlockTokenMetrics {
inputTokens: number;
outputTokens: number;
cacheCreationTokens: number;
cacheReadTokens: number;
totalTokens: number;
readCostUsd: number;
writeCostUsd: number;
estimatedCostUsd: number;
estimatedMaxTokens: number;
estimatedMaxCostUsd: number;
isMaxEstimated: boolean;
}
2 changes: 1 addition & 1 deletion src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export type { PowerlineConfig } from './PowerlineConfig';
export type { ColorLevel, ColorLevelString } from './ColorLevel';
export { getColorLevelString } from './ColorLevel';
export type { StatusJSON } from './StatusJSON';
export type { TokenMetrics, TokenUsage, TranscriptLine } from './TokenMetrics';
export type { BlockTokenMetrics, TokenMetrics, TokenUsage, TranscriptLine } from './TokenMetrics';
export type { RenderContext } from './RenderContext';
export type { PowerlineFontStatus } from './PowerlineFontStatus';
export type { ClaudeSettings } from './ClaudeSettings';
Expand Down
Loading