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 +331 -0
- package/bin/ai-statusline +24 -0
- package/bin/cli.js +22 -0
- package/package.json +45 -0
- package/scripts/install.js +150 -0
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
|
+
});
|