ai-statusline 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,331 @@
1
+ # ai-statusline
2
+
3
+ A high-performance, customizable status line for [Claude Code](https://claude.ai/code) CLI.
4
+
5
+ Built in Rust. Zero runtime dependencies. Sub-millisecond rendering.
6
+
7
+ ```
8
+ [Opus] ▓▓▓▓░░░░░░ 42% | $0.08 | 5m 23s
9
+ 📁 my-project | 🌿 main +3 ~2 | +156 -23 | v2.1.31
10
+ ```
11
+
12
+ **✨ Features:**
13
+ - 🎨 **Interactive TUI configurator** — Visual configuration with live preview
14
+ - ⚡ **26 customizable widgets** — Model, tokens, cost, git status, and more
15
+ - 🎭 **11 built-in themes** — Dracula, Nord, Tokyo Night, Catppuccin, and more
16
+ - 🚀 **Sub-millisecond rendering** — Zero lag, always fresh
17
+ - 🔧 **Zero dependencies** — Single 1MB binary, no Node.js required
18
+
19
+ ## Why ai-statusline?
20
+
21
+ | | ai-statusline | ccstatusline |
22
+ |---|---|---|
23
+ | **Language** | Rust (compiled binary) | TypeScript (bunx/npx) |
24
+ | **Render time** | <1ms | ~200ms (npx overhead) |
25
+ | **Binary size** | 1.0 MB | N/A (requires Node.js) |
26
+ | **Runtime deps** | None | Node.js or Bun |
27
+ | **Data source** | Native JSON API (stdin) | Transcript file parsing |
28
+ | **Accuracy** | Always correct (official API) | Breaks across models/versions |
29
+ | **Memory** | 1.2 MB | ~50 MB (Node.js runtime) |
30
+ | **Widgets** | 26 | ~15 |
31
+ | **Config format** | TOML (with comments) | JSON |
32
+
33
+ ## Quick Start
34
+
35
+ ### Install
36
+
37
+ ```bash
38
+ # npm (downloads platform binary automatically)
39
+ npm install -g ai-statusline
40
+
41
+ # Or direct binary
42
+ curl -fsSL https://raw.githubusercontent.com/mstuart/ai-statusline/main/scripts/install.sh | sh
43
+
44
+ # Or build from source
45
+ cargo install --path .
46
+ ```
47
+
48
+ ### Configure Claude Code
49
+
50
+ Add to `~/.claude/settings.json`:
51
+
52
+ ```json
53
+ {
54
+ "statusLine": {
55
+ "type": "command",
56
+ "command": "ai-statusline"
57
+ }
58
+ }
59
+ ```
60
+
61
+ Restart Claude Code. Done.
62
+
63
+ ### Configure Your Status Line
64
+
65
+ **🎨 Interactive TUI (Recommended for Beginners)**
66
+
67
+ The easiest way to customize your status line:
68
+
69
+ ```bash
70
+ ai-statusline config
71
+ ```
72
+
73
+ The TUI configurator lets you:
74
+ - **Add/remove widgets** — Choose from 26 available widgets with live preview
75
+ - **Reorder widgets** — Use `j`/`k` to move widgets up/down
76
+ - **Switch themes** — Browse and preview 11 built-in themes instantly
77
+ - **Configure powerline** — Toggle powerline mode, change separators, enable auto-align
78
+ - **Manage layouts** — Add/remove status lines, adjust flex modes
79
+ - **Save instantly** — `Ctrl-S` to save, changes apply immediately to Claude Code
80
+
81
+ **⚡ Quick Presets**
82
+
83
+ Apply pre-built layouts instantly:
84
+
85
+ ```bash
86
+ ai-statusline preset full # Two-line layout with everything
87
+ ai-statusline preset minimal # Just model + context %
88
+ ai-statusline preset powerline # Full layout with powerline arrows
89
+ ai-statusline preset compact # Single line, compact values
90
+ ```
91
+
92
+ ## Widgets
93
+
94
+ 26 built-in widgets, all reading from Claude Code's native JSON API:
95
+
96
+ ### Core Metrics
97
+ | Widget | Type | Description |
98
+ |--------|------|-------------|
99
+ | Model | `model` | Current model name (Opus, Sonnet, etc.) |
100
+ | Context % | `context-percentage` | Context window usage with optional progress bar |
101
+ | Context Length | `context-length` | Absolute token count (e.g., "42K") |
102
+ | Tokens In | `tokens-input` | Input tokens from current usage |
103
+ | Tokens Out | `tokens-output` | Output tokens |
104
+ | Tokens Cached | `tokens-cached` | Cache creation + read tokens |
105
+ | Tokens Total | `tokens-total` | All tokens combined |
106
+ | Session Cost | `session-cost` | Running cost in USD with optional burn rate |
107
+ | Session Duration | `session-duration` | Elapsed time with optional API ratio |
108
+ | Block Timer | `block-timer` | 5-hour usage block tracker with progress bar |
109
+
110
+ ### Git Integration
111
+ | Widget | Type | Description |
112
+ |--------|------|-------------|
113
+ | Branch | `git-branch` | Current branch (with detached HEAD support) |
114
+ | Status | `git-status` | Staged/modified/untracked file counts |
115
+ | Worktree | `git-worktree` | Active worktree name (hidden when not in worktree) |
116
+
117
+ ### Workspace
118
+ | Widget | Type | Description |
119
+ |--------|------|-------------|
120
+ | CWD | `cwd` | Current directory (basename, full, fish-style) |
121
+ | Lines Changed | `lines-changed` | Lines added/removed this session |
122
+ | Version | `version` | Claude Code version |
123
+ | Session ID | `session-id` | Truncated session identifier |
124
+
125
+ ### Advanced
126
+ | Widget | Type | Description |
127
+ |--------|------|-------------|
128
+ | Vim Mode | `vim-mode` | NORMAL/INSERT (hidden when vim mode off) |
129
+ | Agent Name | `agent-name` | Active agent (hidden when not using --agent) |
130
+ | Output Style | `output-style` | Current output style (hidden when "default") |
131
+ | Exceeds 200K | `exceeds-tokens` | Warning when tokens exceed 200K threshold |
132
+ | API Duration | `api-duration` | Ratio of API wait time to total time |
133
+ | Custom Command | `custom-command` | Run any shell command, display output |
134
+ | Custom Text | `custom-text` | Static text with emoji support |
135
+ | Separator | `separator` | Visual divider between widgets |
136
+ | Flex Separator | `flex-separator` | Flexible spacer that pushes widgets apart |
137
+ | Terminal Width | `terminal-width` | Current terminal width in columns |
138
+
139
+ ## Configuration
140
+
141
+ ### TUI Configurator
142
+
143
+ Launch the interactive TUI to configure your status line visually:
144
+
145
+ ```bash
146
+ ai-statusline config
147
+ ```
148
+
149
+ **Navigation:**
150
+ - `Tab` / `Shift-Tab` — Switch between tabs (Widgets, Theme, Powerline, Layout, Preview)
151
+ - `↑` / `↓` — Navigate items
152
+ - `←` / `→` — Switch between status lines (in Widgets tab)
153
+ - `Enter` / `Space` — Select/toggle options
154
+ - `a` — Add widget
155
+ - `d` / `Delete` — Remove widget
156
+ - `j` / `k` — Move widget down/up
157
+ - `Ctrl-S` — Save configuration
158
+ - `q` — Quit
159
+
160
+ **Tabs:**
161
+ - **Widgets** — Add, remove, and reorder widgets on each status line
162
+ - **Theme** — Browse and select from 11 built-in color themes
163
+ - **Powerline** — Toggle powerline mode, cycle separators, enable auto-align
164
+ - **Layout** — Add/remove status lines, change flex mode
165
+ - **Preview** — Live preview of your current configuration
166
+
167
+ ### Manual Configuration
168
+
169
+ Config lives at `~/.config/ai-statusline/config.toml`. Generate a default:
170
+
171
+ ```bash
172
+ ai-statusline init
173
+ ```
174
+
175
+ Or edit the TOML file directly for advanced customization.
176
+
177
+ ### Example config
178
+
179
+ ```toml
180
+ theme = "dracula"
181
+ default_separator = " | "
182
+ default_padding = " "
183
+ flex_mode = "full-minus-40"
184
+ compact_threshold = 60
185
+ global_bold = false
186
+ inherit_separator_colors = false
187
+
188
+ # First status line
189
+ [[lines]]
190
+ type = "model"
191
+ color = "cyan"
192
+ raw_value = true
193
+
194
+ [[lines]]
195
+ type = "context-percentage"
196
+ metadata = { bar = "true" }
197
+
198
+ [[lines]]
199
+ type = "session-cost"
200
+ color = "yellow"
201
+ raw_value = true
202
+
203
+ [[lines]]
204
+ type = "session-duration"
205
+ raw_value = true
206
+
207
+ # Second status line (start a new line group)
208
+ [[lines]]
209
+ type = "cwd"
210
+ metadata = { fish_style = "true" }
211
+
212
+ [[lines]]
213
+ type = "git-branch"
214
+ color = "magenta"
215
+
216
+ [[lines]]
217
+ type = "git-status"
218
+
219
+ [powerline]
220
+ enabled = false
221
+ separator = "\uE0B0"
222
+ auto_align = false
223
+ ```
224
+
225
+ ### Widget options
226
+
227
+ Every widget supports:
228
+
229
+ | Option | Type | Description |
230
+ |--------|------|-------------|
231
+ | `type` | string | Widget type (see table above) |
232
+ | `color` | string | Foreground color (named, hex, or 256-color index) |
233
+ | `background_color` | string | Background color |
234
+ | `bold` | bool | Bold text |
235
+ | `raw_value` | bool | Compact mode without labels |
236
+ | `padding` | string | Override default padding |
237
+ | `merge_next` | bool | Merge with next widget (no separator) |
238
+ | `metadata` | table | Widget-specific options |
239
+
240
+ ### Widget-specific metadata
241
+
242
+ | Widget | Key | Values | Description |
243
+ |--------|-----|--------|-------------|
244
+ | `context-percentage` | `bar` | `"true"` | Show progress bar |
245
+ | `context-percentage` | `inverse` | `"true"` | Show remaining instead of used |
246
+ | `session-cost` | `burn_rate` | `"true"` | Show hourly burn rate |
247
+ | `session-duration` | `api_ratio` | `"true"` | Show API time percentage |
248
+ | `block-timer` | `bar` | `"true"` | Show progress bar |
249
+ | `block-timer` | `bar_width` | `"16"` | Progress bar width |
250
+ | `cwd` | `full` | `"true"` | Show full path |
251
+ | `cwd` | `fish_style` | `"true"` | Fish-style abbreviation |
252
+ | `cwd` | `segments` | `"3"` | Show last N segments |
253
+ | `custom-command` | `command` | shell cmd | Command to execute |
254
+ | `custom-text` | `text` | any string | Static text to display |
255
+ | `separator` | `char` | any char | Separator character |
256
+ | `flex-separator` | `char` | any char | Fill character (default: space) |
257
+
258
+ ## Themes
259
+
260
+ 11 built-in themes optimized for popular terminal color schemes:
261
+
262
+ ```bash
263
+ ai-statusline theme list # List all themes
264
+ ai-statusline theme set nord # Switch theme
265
+ ```
266
+
267
+ Available: `default`, `solarized`, `nord`, `dracula`, `gruvbox`, `monokai`, `light`, `high-contrast`, `one-dark`, `tokyo-night`, `catppuccin`
268
+
269
+ ## Color Support
270
+
271
+ ai-statusline auto-detects terminal color capabilities:
272
+
273
+ - **Truecolor** (24-bit): detected via `COLORTERM=truecolor`
274
+ - **256-color**: detected via `TERM=*256color*`
275
+ - **16-color**: fallback for basic terminals
276
+ - **No color**: respects `NO_COLOR` environment variable
277
+
278
+ Override with `--color-level`:
279
+
280
+ ```bash
281
+ ai-statusline --color-level truecolor
282
+ ai-statusline --color-level none
283
+ ```
284
+
285
+ ## CLI Commands
286
+
287
+ ```bash
288
+ ai-statusline # Render status line (reads JSON from stdin)
289
+ ai-statusline init # Generate default config file
290
+ ai-statusline doctor # Check environment compatibility
291
+ ai-statusline theme list # List available themes
292
+ ai-statusline theme set <name> # Switch theme
293
+ ai-statusline preset <name> # Apply a preset layout
294
+ ai-statusline config # Interactive TUI configurator
295
+ ai-statusline dump-schema # Print expected JSON input schema
296
+ ai-statusline --version # Show version
297
+ ```
298
+
299
+ ## Performance
300
+
301
+ Benchmarked on Apple M1:
302
+
303
+ | Metric | Value |
304
+ |--------|-------|
305
+ | Render time | <1ms |
306
+ | Binary size | 1.0 MB |
307
+ | Memory usage | 1.2 MB |
308
+ | Startup time | <1ms |
309
+
310
+ Claude Code debounces status line updates at 300ms. ai-statusline completes in <1ms, ensuring the status line is always fresh and never causes UI lag.
311
+
312
+ ## How It Works
313
+
314
+ Claude Code pipes JSON session data to your status line script via stdin. ai-statusline reads this JSON, applies your configuration, and prints formatted ANSI text to stdout. No transcript parsing, no file watching, no external dependencies.
315
+
316
+ ```
317
+ Claude Code → JSON stdin → ai-statusline → ANSI stdout → Terminal
318
+ ```
319
+
320
+ ## Building from Source
321
+
322
+ ```bash
323
+ git clone https://github.com/mstuart/ai-statusline
324
+ cd ai-statusline
325
+ cargo build --release
326
+ # Binary at ./target/release/ai-statusline
327
+ ```
328
+
329
+ ## License
330
+
331
+ MIT
@@ -0,0 +1,24 @@
1
+ #!/bin/sh
2
+ # Wrapper script for ai-statusline binary
3
+ # The actual binary is downloaded during npm postinstall
4
+
5
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
6
+ BINARY="$SCRIPT_DIR/ai-statusline-bin"
7
+
8
+ if [ -f "$BINARY" ]; then
9
+ exec "$BINARY" "$@"
10
+ fi
11
+
12
+ # Check for binary with platform-specific name
13
+ case "$(uname -s)" in
14
+ MINGW*|MSYS*|CYGWIN*) BINARY="$SCRIPT_DIR/ai-statusline.exe" ;;
15
+ esac
16
+
17
+ if [ -f "$BINARY" ]; then
18
+ exec "$BINARY" "$@"
19
+ fi
20
+
21
+ echo "Error: ai-statusline binary not found."
22
+ echo "Try reinstalling: npm install -g ai-statusline"
23
+ echo "Or build from source: cargo install --path ."
24
+ exit 1
package/bin/cli.js ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ const { spawnSync } = require('child_process');
3
+ const path = require('path');
4
+
5
+ const ext = process.platform === 'win32' ? '.exe' : '';
6
+ const bin = path.join(__dirname, `claude-status-bin${ext}`);
7
+
8
+ const result = spawnSync(bin, process.argv.slice(2), {
9
+ stdio: 'inherit',
10
+ windowsHide: false,
11
+ });
12
+
13
+ if (result.error) {
14
+ if (result.error.code === 'ENOENT') {
15
+ console.error('claude-status binary not found. Try reinstalling: npm install -g claude-status');
16
+ } else {
17
+ console.error(result.error.message);
18
+ }
19
+ process.exit(1);
20
+ }
21
+
22
+ process.exit(result.status ?? 1);
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "ai-statusline",
3
+ "version": "1.0.0",
4
+ "description": "Customizable status line for AI coding assistants - real-time model, tokens, cost, git status display",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/mstuart/ai-statusline"
9
+ },
10
+ "homepage": "https://github.com/mstuart/ai-statusline#readme",
11
+ "keywords": [
12
+ "ai-coding",
13
+ "statusline",
14
+ "terminal",
15
+ "cli",
16
+ "developer-tools",
17
+ "claude-code",
18
+ "cursor",
19
+ "ai-assistant"
20
+ ],
21
+ "bin": {
22
+ "ai-statusline": "bin/ai-statusline"
23
+ },
24
+ "scripts": {
25
+ "postinstall": "node scripts/install.js"
26
+ },
27
+ "files": [
28
+ "bin/",
29
+ "scripts/",
30
+ "README.md",
31
+ "LICENSE"
32
+ ],
33
+ "engines": {
34
+ "node": ">=16"
35
+ },
36
+ "os": [
37
+ "darwin",
38
+ "linux",
39
+ "win32"
40
+ ],
41
+ "cpu": [
42
+ "x64",
43
+ "arm64"
44
+ ]
45
+ }
@@ -0,0 +1,150 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const https = require("https");
5
+ const http = require("http");
6
+ const fs = require("fs");
7
+ const path = require("path");
8
+ const os = require("os");
9
+ const { execSync } = require("child_process");
10
+
11
+ const PACKAGE = require("../package.json");
12
+ const VERSION = PACKAGE.version;
13
+ const REPO = "mstuart/ai-statusline";
14
+ const BINARY_NAME = "ai-statusline";
15
+
16
+ function getPlatformTarget() {
17
+ const platform = os.platform();
18
+ const arch = os.arch();
19
+
20
+ const targets = {
21
+ "darwin-x64": "x86_64-apple-darwin",
22
+ "darwin-arm64": "aarch64-apple-darwin",
23
+ "linux-x64": "x86_64-unknown-linux-gnu",
24
+ "linux-arm64": "aarch64-unknown-linux-gnu",
25
+ "win32-x64": "x86_64-pc-windows-msvc",
26
+ };
27
+
28
+ const key = `${platform}-${arch}`;
29
+ const target = targets[key];
30
+
31
+ if (!target) {
32
+ console.error(`Unsupported platform: ${key}`);
33
+ console.error(`Supported platforms: ${Object.keys(targets).join(", ")}`);
34
+ process.exit(1);
35
+ }
36
+
37
+ return { target, platform, arch };
38
+ }
39
+
40
+ function getDownloadUrl(target) {
41
+ const ext = target.includes("windows") ? ".exe" : "";
42
+ const archive = target.includes("windows") ? "zip" : "tar.gz";
43
+ return `https://github.com/${REPO}/releases/download/v${VERSION}/${BINARY_NAME}-v${VERSION}-${target}.${archive}`;
44
+ }
45
+
46
+ function download(url) {
47
+ return new Promise((resolve, reject) => {
48
+ const handler = (response) => {
49
+ if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
50
+ const redirectUrl = response.headers.location;
51
+ const client = redirectUrl.startsWith("https") ? https : http;
52
+ client.get(redirectUrl, handler).on("error", reject);
53
+ return;
54
+ }
55
+
56
+ if (response.statusCode !== 200) {
57
+ reject(new Error(`Download failed with status ${response.statusCode}: ${url}`));
58
+ return;
59
+ }
60
+
61
+ const chunks = [];
62
+ response.on("data", (chunk) => chunks.push(chunk));
63
+ response.on("end", () => resolve(Buffer.concat(chunks)));
64
+ response.on("error", reject);
65
+ };
66
+
67
+ https.get(url, handler).on("error", reject);
68
+ });
69
+ }
70
+
71
+ async function extractTarGz(buffer, destDir) {
72
+ const tmpFile = path.join(os.tmpdir(), `ai-statusline-${Date.now()}.tar.gz`);
73
+ fs.writeFileSync(tmpFile, buffer);
74
+
75
+ try {
76
+ execSync(`tar xzf "${tmpFile}" -C "${destDir}"`, { stdio: "pipe" });
77
+ } finally {
78
+ try { fs.unlinkSync(tmpFile); } catch (_) {}
79
+ }
80
+ }
81
+
82
+ async function extractZip(buffer, destDir) {
83
+ const tmpFile = path.join(os.tmpdir(), `ai-statusline-${Date.now()}.zip`);
84
+ fs.writeFileSync(tmpFile, buffer);
85
+
86
+ try {
87
+ execSync(`unzip -o "${tmpFile}" -d "${destDir}"`, { stdio: "pipe" });
88
+ } finally {
89
+ try { fs.unlinkSync(tmpFile); } catch (_) {}
90
+ }
91
+ }
92
+
93
+ async function install() {
94
+ const { target, platform } = getPlatformTarget();
95
+ const binDir = path.join(__dirname, "..", "bin");
96
+ const binPath = path.join(binDir, platform === "win32" ? `${BINARY_NAME}.exe` : BINARY_NAME);
97
+
98
+ // Check if binary already exists
99
+ if (fs.existsSync(binPath)) {
100
+ console.log(`ai-statusline binary already installed at ${binPath}`);
101
+ return;
102
+ }
103
+
104
+ const url = getDownloadUrl(target);
105
+ console.log(`Downloading ai-statusline v${VERSION} for ${target}...`);
106
+ console.log(` URL: ${url}`);
107
+
108
+ try {
109
+ const data = await download(url);
110
+ console.log(` Downloaded ${(data.length / 1024 / 1024).toFixed(1)} MB`);
111
+
112
+ // Extract
113
+ fs.mkdirSync(binDir, { recursive: true });
114
+
115
+ if (target.includes("windows")) {
116
+ await extractZip(data, binDir);
117
+ } else {
118
+ await extractTarGz(data, binDir);
119
+ }
120
+
121
+ // Make executable
122
+ if (platform !== "win32") {
123
+ fs.chmodSync(binPath, 0o755);
124
+ }
125
+
126
+ console.log(` Installed to ${binPath}`);
127
+ } catch (error) {
128
+ console.warn(`\nFailed to download pre-built binary: ${error.message}`);
129
+ console.warn("\nYou can build from source instead:");
130
+ console.warn(" cargo install --path .");
131
+ console.warn("\nOr download manually from:");
132
+ console.warn(` https://github.com/${REPO}/releases`);
133
+
134
+ // Create a stub script that tells the user to install manually
135
+ const stub = platform === "win32"
136
+ ? `@echo off\necho ai-statusline binary not installed. Run: cargo install --path . in the ai-statusline repo\nexit /b 1\n`
137
+ : `#!/bin/sh\necho "ai-statusline binary not installed. Run: cargo install --path . in the ai-statusline repo"\nexit 1\n`;
138
+
139
+ fs.mkdirSync(binDir, { recursive: true });
140
+ fs.writeFileSync(binPath, stub);
141
+ if (platform !== "win32") {
142
+ fs.chmodSync(binPath, 0o755);
143
+ }
144
+ }
145
+ }
146
+
147
+ install().catch((error) => {
148
+ console.error("Installation failed:", error.message);
149
+ process.exit(1);
150
+ });