claudeline 1.0.0 → 1.2.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/.claude/skills/configure-statusline/SKILL.md +187 -0
- package/.claude-plugin/plugin.json +8 -0
- package/README.md +60 -9
- package/dist/index.js +388 -36
- package/package.json +7 -4
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: configure-statusline
|
|
3
|
+
description: Configure claudeline status line for Claude Code. Use when user wants to set up, customize, or change their status line display. Helps users choose themes, build custom formats, and install to global or project scope.
|
|
4
|
+
tools: Read, Bash, AskUserQuestion
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Configure Claudeline Status Line
|
|
8
|
+
|
|
9
|
+
Help users configure their Claude Code status line using claudeline.
|
|
10
|
+
|
|
11
|
+
## Workflow
|
|
12
|
+
|
|
13
|
+
### Step 1: Understand User's Needs
|
|
14
|
+
|
|
15
|
+
Ask the user what they want:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
What would you like to do?
|
|
19
|
+
1. **Quick setup** - Install a preset theme
|
|
20
|
+
2. **Custom format** - Build a custom status line
|
|
21
|
+
3. **See current** - Check what's currently configured
|
|
22
|
+
4. **Uninstall** - Remove status line configuration
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Step 2: Determine Scope
|
|
26
|
+
|
|
27
|
+
Ask where to install:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
Where should the status line be configured?
|
|
31
|
+
1. **Global** (~/.claude/settings.json) - Applies to all projects
|
|
32
|
+
2. **Project** (.claude/settings.json) - Only this project
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Step 3A: Quick Setup (Theme Selection)
|
|
36
|
+
|
|
37
|
+
If user wants a theme, show available options:
|
|
38
|
+
|
|
39
|
+
| Theme | Description | Preview |
|
|
40
|
+
|-------|-------------|---------|
|
|
41
|
+
| `minimal` | Just model and directory | `Sonnet 4 myproject` |
|
|
42
|
+
| `default` | Model, directory, git info | `[Sonnet 4] 📁 myproject \| 🌿 main ✓` |
|
|
43
|
+
| `powerline` | Powerline style with colors | `Sonnet 4 myproject main ✓` |
|
|
44
|
+
| `full` | Everything including cost/context | `[Sonnet 4] ~/project → main ✓ \| [████░░] 45% \| $0.42` |
|
|
45
|
+
| `git` | Git-focused with detailed status | `[Sonnet 4] 📁 myproject \| 🌿 main ✓ ↑2` |
|
|
46
|
+
| `tokens` | Token/context focused | `Sonnet 4 \| 🟢 50k/200k \| +133` |
|
|
47
|
+
| `dashboard` | Full dashboard view | `[Sonnet 4] myproject \| main ✓ \| 45% \| $0.42 \| 14:32` |
|
|
48
|
+
| `compact` | Minimal with slashes | `Sonnet 4 / myproject / main` |
|
|
49
|
+
| `colorful` | Vibrant colors | Colored model, dir, branch, status |
|
|
50
|
+
|
|
51
|
+
Install with:
|
|
52
|
+
```bash
|
|
53
|
+
npx claudeline --theme <name> --install [--project]
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Step 3B: Custom Format Builder
|
|
57
|
+
|
|
58
|
+
If user wants custom, guide them through building a format string.
|
|
59
|
+
|
|
60
|
+
#### Available Components
|
|
61
|
+
|
|
62
|
+
**Model/Session:**
|
|
63
|
+
- `claude:model` - Model name (Sonnet 4)
|
|
64
|
+
- `claude:model-letter` - First letter (S)
|
|
65
|
+
|
|
66
|
+
**Directory:**
|
|
67
|
+
- `fs:dir` - Current directory name
|
|
68
|
+
- `fs:home` - Path with ~ for home
|
|
69
|
+
- `fs:project` - Project name
|
|
70
|
+
|
|
71
|
+
**Git:**
|
|
72
|
+
- `git:branch` - Branch name
|
|
73
|
+
- `git:status` - ✓ or *
|
|
74
|
+
- `git:ahead-behind` - ↑2↓1
|
|
75
|
+
- `git:dirty` - Combined status
|
|
76
|
+
|
|
77
|
+
**Context:**
|
|
78
|
+
- `ctx:percent` - Usage % (45%)
|
|
79
|
+
- `ctx:bar` - Progress bar [████░░]
|
|
80
|
+
- `ctx:emoji` - 🟢🟡🟠🔴
|
|
81
|
+
- `ctx:tokens` - 50k/200k
|
|
82
|
+
|
|
83
|
+
**Cost:**
|
|
84
|
+
- `cost:total` - $0.42
|
|
85
|
+
- `cost:duration` - 5m
|
|
86
|
+
- `cost:lines` - +133
|
|
87
|
+
|
|
88
|
+
**Time:**
|
|
89
|
+
- `time:now` - 14:32
|
|
90
|
+
- `time:date` - Feb 1
|
|
91
|
+
|
|
92
|
+
**Separators:** `sep:pipe` (\|), `sep:arrow` (→), `sep:dot` (•), `sep:slash` (/)
|
|
93
|
+
|
|
94
|
+
**Emojis:** `emoji:folder` (📁), `emoji:branch` (🌿), `emoji:rocket` (🚀)
|
|
95
|
+
|
|
96
|
+
**Styling:** Prefix with `green:`, `bold:`, `cyan:`, etc.
|
|
97
|
+
|
|
98
|
+
**Grouping:** `[...]` adds brackets, `if:git(...)` for conditionals
|
|
99
|
+
|
|
100
|
+
#### Example Formats
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
# Simple
|
|
104
|
+
"claude:model fs:dir git:branch"
|
|
105
|
+
|
|
106
|
+
# With separators
|
|
107
|
+
"claude:model sep:pipe fs:dir sep:arrow git:branch"
|
|
108
|
+
|
|
109
|
+
# With colors
|
|
110
|
+
"bold:cyan:claude:model fs:dir green:git:branch"
|
|
111
|
+
|
|
112
|
+
# With conditionals (only show git info in git repos)
|
|
113
|
+
"claude:model fs:dir if:git(sep:pipe git:branch git:status)"
|
|
114
|
+
|
|
115
|
+
# Full custom
|
|
116
|
+
"[bold:cyan:claude:model] emoji:folder fs:dir sep:arrow green:git:branch git:status sep:pipe ctx:percent"
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Install custom format:
|
|
120
|
+
```bash
|
|
121
|
+
npx claudeline "<format>" --install [--project]
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Step 4: Verify Installation
|
|
125
|
+
|
|
126
|
+
After installing, show the user:
|
|
127
|
+
|
|
128
|
+
1. What was written to settings.json
|
|
129
|
+
2. Remind them to restart Claude Code
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# Check current configuration
|
|
133
|
+
cat ~/.claude/settings.json | grep -A5 statusLine
|
|
134
|
+
|
|
135
|
+
# Or for project
|
|
136
|
+
cat .claude/settings.json | grep -A5 statusLine
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Step 5: Test Before Installing (Optional)
|
|
140
|
+
|
|
141
|
+
User can test their format:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
# See sample data
|
|
145
|
+
npx claudeline --preview
|
|
146
|
+
|
|
147
|
+
# Test with sample data
|
|
148
|
+
echo '{"model":{"display_name":"Sonnet 4"},"workspace":{"current_dir":"/test/project"}}' | npx claudeline run "claude:model fs:dir"
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Commands Reference
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
# Install theme globally
|
|
155
|
+
npx claudeline --theme minimal --install
|
|
156
|
+
|
|
157
|
+
# Install theme to project only
|
|
158
|
+
npx claudeline --theme powerline --install --project
|
|
159
|
+
|
|
160
|
+
# Install custom format
|
|
161
|
+
npx claudeline "claude:model fs:dir git:branch" --install
|
|
162
|
+
|
|
163
|
+
# List all themes
|
|
164
|
+
npx claudeline --themes
|
|
165
|
+
|
|
166
|
+
# List all components
|
|
167
|
+
npx claudeline --list
|
|
168
|
+
|
|
169
|
+
# Uninstall
|
|
170
|
+
npx claudeline --uninstall [--project]
|
|
171
|
+
|
|
172
|
+
# Preview sample data
|
|
173
|
+
npx claudeline --preview
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Troubleshooting
|
|
177
|
+
|
|
178
|
+
**Status line not showing?**
|
|
179
|
+
- Restart Claude Code after installing
|
|
180
|
+
- Check settings.json has the statusLine config
|
|
181
|
+
|
|
182
|
+
**Command not found?**
|
|
183
|
+
- Ensure Node.js/npm is installed
|
|
184
|
+
- Try `npx claudeline --help`
|
|
185
|
+
|
|
186
|
+
**Want to use bun instead of npx?**
|
|
187
|
+
- Use `--use-bunx` flag when installing
|
package/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# claudeline
|
|
2
2
|
|
|
3
|
-
Customizable status line for [Claude Code](https://claude.ai/code). Display model info, git status, context usage, costs, and more in your terminal.
|
|
3
|
+
Customizable status line for [Claude Code](https://claude.ai/code). Display model info, git status, context usage, costs, API usage limits, and more in your terminal.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
# Install with a theme
|
|
9
|
-
npx claudeline --theme
|
|
9
|
+
npx claudeline --theme luca --install
|
|
10
10
|
|
|
11
11
|
# Or with a custom format
|
|
12
12
|
npx claudeline "claude:model fs:dir git:branch" --install
|
|
@@ -39,17 +39,21 @@ npx claudeline --uninstall
|
|
|
39
39
|
|-------|--------|
|
|
40
40
|
| `minimal` | `claude:model fs:dir` |
|
|
41
41
|
| `default` | `[claude:model] emoji:folder fs:dir if:git(sep:pipe emoji:branch git:branch git:status)` |
|
|
42
|
-
| `powerline` | `bold:cyan:claude:model sep:powerline fs:dir if:git(
|
|
42
|
+
| `powerline` | `bold:cyan:claude:model sep:powerline fs:dir if:git(...)` |
|
|
43
43
|
| `full` | `[bold:cyan:claude:model] fs:home sep:arrow green:git:branch git:status sep:pipe ctx:bar ctx:percent sep:pipe cost:total` |
|
|
44
|
-
| `git` | `[claude:model] emoji:folder fs:dir sep:pipe emoji:branch git:branch git:status git:ahead-behind if:dirty(
|
|
44
|
+
| `git` | `[claude:model] emoji:folder fs:dir sep:pipe emoji:branch git:branch git:status git:ahead-behind if:dirty(...)` |
|
|
45
45
|
| `tokens` | `claude:model sep:pipe ctx:emoji ctx:tokens sep:pipe cost:lines` |
|
|
46
46
|
| `dev` | `[fs:dir] git:branch sep:pipe env:node sep:pipe time:now` |
|
|
47
47
|
| `dashboard` | `[bold:claude:model] fs:dir sep:pipe git:branch git:status sep:pipe ctx:percent sep:pipe cost:total sep:pipe time:now` |
|
|
48
48
|
| `context-focus` | `claude:model sep:pipe ctx:bar sep:pipe ctx:tokens sep:pipe ctx:emoji` |
|
|
49
49
|
| `cost` | `claude:model sep:pipe cost:total sep:pipe cost:duration sep:pipe cost:lines-both` |
|
|
50
50
|
| `simple` | `claude:model fs:dir git:branch` |
|
|
51
|
+
| `verbose` | `[claude:model] [claude:version] sep:pipe fs:home sep:pipe git:branch git:status git:ahead-behind sep:pipe ctx:bar ctx:percent sep:pipe cost:total cost:duration sep:pipe time:now` |
|
|
51
52
|
| `compact` | `claude:model sep:slash fs:dir sep:slash git:branch` |
|
|
52
53
|
| `colorful` | `bold:magenta:claude:model sep:arrow cyan:fs:dir sep:arrow green:git:branch yellow:git:status sep:arrow blue:ctx:percent` |
|
|
54
|
+
| `luca` | Nerd font icons, colored repo:branch, dirty state, cost, 5h/weekly usage bars |
|
|
55
|
+
|
|
56
|
+
When you install with `--theme`, claudeline stores a `theme:name` reference so your status line automatically picks up theme updates when you upgrade.
|
|
53
57
|
|
|
54
58
|
## Format Syntax
|
|
55
59
|
|
|
@@ -87,6 +91,7 @@ Show components only when conditions are met:
|
|
|
87
91
|
```
|
|
88
92
|
if:git(git:branch git:status) # only in git repos
|
|
89
93
|
if:dirty(text:UNCOMMITTED) # only when working tree is dirty
|
|
94
|
+
if:subdir(fs:relative) # only when in a subdirectory (not git root)
|
|
90
95
|
if:node(env:node) # only in Node.js projects
|
|
91
96
|
if:python(env:python) # only in Python projects
|
|
92
97
|
if:rust(emoji:rust) # only in Rust projects
|
|
@@ -120,7 +125,7 @@ Available separators:
|
|
|
120
125
|
- `powerline` → ``
|
|
121
126
|
- `powerline-left` → ``
|
|
122
127
|
- `space` → ` `
|
|
123
|
-
- `none` → (empty)
|
|
128
|
+
- `none` → (empty, useful for glueing components together)
|
|
124
129
|
|
|
125
130
|
## Components Reference
|
|
126
131
|
|
|
@@ -151,7 +156,7 @@ Available separators:
|
|
|
151
156
|
| `ctx:emoji` | Status emoji (🟢🟡🟠🔴) |
|
|
152
157
|
| `ctx:used-tokens` | Total used tokens |
|
|
153
158
|
|
|
154
|
-
### Cost
|
|
159
|
+
### Cost
|
|
155
160
|
|
|
156
161
|
| Component | Description |
|
|
157
162
|
|-----------|-------------|
|
|
@@ -164,6 +169,26 @@ Available separators:
|
|
|
164
169
|
| `cost:removed` | Lines removed |
|
|
165
170
|
| `cost:lines-both` | Lines added and removed |
|
|
166
171
|
|
|
172
|
+
### Usage/Limits
|
|
173
|
+
|
|
174
|
+
Fetches your 5-hour and 7-day API utilization from Claude's OAuth API. Data is cached for 5 minutes in `$TMPDIR/claudeline-usage-cache.json`, shared across all sessions system-wide. The OAuth token is read automatically from macOS Keychain.
|
|
175
|
+
|
|
176
|
+
| Component | Description | Example |
|
|
177
|
+
|-----------|-------------|---------|
|
|
178
|
+
| `usage:5h` | 5-hour utilization % | `12%` |
|
|
179
|
+
| `usage:week` | 7-day utilization % | `58%` |
|
|
180
|
+
| `usage:7d` | Alias for `usage:week` | `58%` |
|
|
181
|
+
| `usage:5h-reset` | Time until 5h reset | `3h 32m` |
|
|
182
|
+
| `usage:week-reset` | Time until weekly reset | `2d 5h` |
|
|
183
|
+
| `usage:7d-reset` | Alias for `usage:week-reset` | `2d 5h` |
|
|
184
|
+
| `usage:5h-bar` | 5h progress bar with H label | `H▰▰▱▱▱▱▱▱` |
|
|
185
|
+
| `usage:5h-bar:N` | 5h bar with width N | `H▰▱▱▱▱` |
|
|
186
|
+
| `usage:week-bar` | Weekly progress bar with W label | `W▰▰▰▰▰▱▱▱` |
|
|
187
|
+
| `usage:week-bar:N` | Weekly bar with width N | `W▰▰▰▱▱` |
|
|
188
|
+
| `usage:5h-emoji` | 5h status emoji (🟢🟡🟠🔴) | `🟢` |
|
|
189
|
+
| `usage:week-emoji` | Weekly status emoji | `🟡` |
|
|
190
|
+
| `usage:7d-emoji` | Alias for `usage:week-emoji` | `🟡` |
|
|
191
|
+
|
|
167
192
|
### File System
|
|
168
193
|
|
|
169
194
|
| Component | Description |
|
|
@@ -191,7 +216,8 @@ Available separators:
|
|
|
191
216
|
| `git:staged` | Staged files (●N) |
|
|
192
217
|
| `git:modified` | Modified files (+N) |
|
|
193
218
|
| `git:untracked` | Untracked files (?N) |
|
|
194
|
-
| `git:dirty` | Combined dirty status |
|
|
219
|
+
| `git:dirty` | Combined dirty status with per-type colors (green staged, red untracked, yellow modified) |
|
|
220
|
+
| `git:repo-branch` | Condensed `repo:branch` format |
|
|
195
221
|
| `git:commit` | Short commit hash |
|
|
196
222
|
| `git:commit-long` | Full commit hash |
|
|
197
223
|
| `git:tag` | Current tag |
|
|
@@ -249,9 +275,31 @@ Available separators:
|
|
|
249
275
|
| `time:minute` | Minute |
|
|
250
276
|
| `time:elapsed` | Session elapsed time |
|
|
251
277
|
|
|
278
|
+
### Nerd Font Icons
|
|
279
|
+
|
|
280
|
+
Use `nerd:name` for Nerd Font glyphs. Requires a [Nerd Font](https://www.nerdfonts.com/) in your terminal.
|
|
281
|
+
|
|
282
|
+
**Files:** `folder`, `folder-open`, `file`, `file-code`
|
|
283
|
+
|
|
284
|
+
**Git:** `branch`, `repo`, `commit`, `merge`, `tag`, `stash`, `pr`, `diff`, `compare`
|
|
285
|
+
|
|
286
|
+
**Status:** `check`, `x`, `warn`, `error`, `info`, `question`, `bell`, `pin`
|
|
287
|
+
|
|
288
|
+
**Decorative:** `star`, `fire`, `rocket`, `sparkle`, `lightning`, `heart`, `diamond`, `circle`, `square`, `triangle`
|
|
289
|
+
|
|
290
|
+
**Tech:** `node`, `python`, `rust`, `go`, `ruby`, `java`, `docker`, `terminal`, `code`, `database`, `cloud`, `server`, `package`, `gear`, `lock`, `unlock`, `key`, `shield`
|
|
291
|
+
|
|
292
|
+
**Arrows:** `up`, `down`, `left`, `right`, `arrow-right`, `arrow-left`
|
|
293
|
+
|
|
294
|
+
**Time:** `clock`, `calendar`, `history`
|
|
295
|
+
|
|
296
|
+
**OS:** `apple`, `linux`, `windows`
|
|
297
|
+
|
|
298
|
+
**Other:** `search`, `eye`, `bug`, `wrench`, `plug`, `wifi`, `bluetooth`, `cpu`, `memory`, `home`, `user`
|
|
299
|
+
|
|
252
300
|
### Emojis
|
|
253
301
|
|
|
254
|
-
Use `emoji:name` for
|
|
302
|
+
Use `emoji:name` for Unicode emojis (no Nerd Font required):
|
|
255
303
|
|
|
256
304
|
**Files:** `folder` 📁, `file` 📄, `home` 🏠
|
|
257
305
|
|
|
@@ -319,7 +367,7 @@ When you run `--install`, claudeline updates your `~/.claude/settings.json`:
|
|
|
319
367
|
{
|
|
320
368
|
"statusLine": {
|
|
321
369
|
"type": "command",
|
|
322
|
-
"command": "npx claudeline run
|
|
370
|
+
"command": "npx claudeline run theme:luca",
|
|
323
371
|
"padding": 0
|
|
324
372
|
}
|
|
325
373
|
}
|
|
@@ -337,6 +385,9 @@ npx claudeline --preview
|
|
|
337
385
|
|
|
338
386
|
# Test with sample data
|
|
339
387
|
echo '{"model":{"display_name":"Sonnet 4"}}' | npx claudeline run "claude:model fs:dir"
|
|
388
|
+
|
|
389
|
+
# Test a theme
|
|
390
|
+
echo '{"model":{"display_name":"Opus"}}' | npx claudeline run theme:dashboard
|
|
340
391
|
```
|
|
341
392
|
|
|
342
393
|
## Package Managers
|
package/dist/index.js
CHANGED
|
@@ -148,6 +148,9 @@ function parseComponent(token) {
|
|
|
148
148
|
if (parts[0] === "emoji") {
|
|
149
149
|
return { type: "emoji", key: parts[1] || "", styles };
|
|
150
150
|
}
|
|
151
|
+
if (parts[0] === "nerd") {
|
|
152
|
+
return { type: "nerd", key: parts[1] || "", styles };
|
|
153
|
+
}
|
|
151
154
|
if (parts[0] === "sep") {
|
|
152
155
|
return { type: "sep", key: parts[1] || "pipe", styles };
|
|
153
156
|
}
|
|
@@ -200,6 +203,7 @@ function listComponents() {
|
|
|
200
203
|
"tag",
|
|
201
204
|
"remote",
|
|
202
205
|
"repo",
|
|
206
|
+
"repo-branch",
|
|
203
207
|
"user",
|
|
204
208
|
"email",
|
|
205
209
|
"remote-url"
|
|
@@ -255,6 +259,24 @@ function listComponents() {
|
|
|
255
259
|
"elapsed"
|
|
256
260
|
]
|
|
257
261
|
},
|
|
262
|
+
{
|
|
263
|
+
name: "Usage/Limits",
|
|
264
|
+
prefix: "usage",
|
|
265
|
+
items: [
|
|
266
|
+
"5h",
|
|
267
|
+
"week",
|
|
268
|
+
"7d",
|
|
269
|
+
"5h-reset",
|
|
270
|
+
"week-reset",
|
|
271
|
+
"7d-reset",
|
|
272
|
+
"5h-bar",
|
|
273
|
+
"5h-bar:N",
|
|
274
|
+
"week-bar",
|
|
275
|
+
"week-bar:N",
|
|
276
|
+
"5h-emoji",
|
|
277
|
+
"week-emoji"
|
|
278
|
+
]
|
|
279
|
+
},
|
|
258
280
|
{
|
|
259
281
|
name: "Separators",
|
|
260
282
|
prefix: "sep",
|
|
@@ -327,6 +349,65 @@ function listComponents() {
|
|
|
327
349
|
"money"
|
|
328
350
|
]
|
|
329
351
|
},
|
|
352
|
+
{
|
|
353
|
+
name: "Nerd Font Icons",
|
|
354
|
+
prefix: "nerd",
|
|
355
|
+
items: [
|
|
356
|
+
"folder",
|
|
357
|
+
"folder-open",
|
|
358
|
+
"file",
|
|
359
|
+
"file-code",
|
|
360
|
+
"branch",
|
|
361
|
+
"repo",
|
|
362
|
+
"commit",
|
|
363
|
+
"merge",
|
|
364
|
+
"tag",
|
|
365
|
+
"stash",
|
|
366
|
+
"pr",
|
|
367
|
+
"diff",
|
|
368
|
+
"check",
|
|
369
|
+
"x",
|
|
370
|
+
"warn",
|
|
371
|
+
"error",
|
|
372
|
+
"info",
|
|
373
|
+
"star",
|
|
374
|
+
"fire",
|
|
375
|
+
"rocket",
|
|
376
|
+
"sparkle",
|
|
377
|
+
"lightning",
|
|
378
|
+
"heart",
|
|
379
|
+
"node",
|
|
380
|
+
"python",
|
|
381
|
+
"rust",
|
|
382
|
+
"go",
|
|
383
|
+
"ruby",
|
|
384
|
+
"java",
|
|
385
|
+
"docker",
|
|
386
|
+
"terminal",
|
|
387
|
+
"code",
|
|
388
|
+
"database",
|
|
389
|
+
"cloud",
|
|
390
|
+
"server",
|
|
391
|
+
"package",
|
|
392
|
+
"gear",
|
|
393
|
+
"lock",
|
|
394
|
+
"key",
|
|
395
|
+
"shield",
|
|
396
|
+
"clock",
|
|
397
|
+
"calendar",
|
|
398
|
+
"apple",
|
|
399
|
+
"linux",
|
|
400
|
+
"windows",
|
|
401
|
+
"search",
|
|
402
|
+
"eye",
|
|
403
|
+
"bug",
|
|
404
|
+
"wrench",
|
|
405
|
+
"cpu",
|
|
406
|
+
"memory",
|
|
407
|
+
"home",
|
|
408
|
+
"user"
|
|
409
|
+
]
|
|
410
|
+
},
|
|
330
411
|
{
|
|
331
412
|
name: "Text",
|
|
332
413
|
prefix: "text",
|
|
@@ -391,7 +472,7 @@ var THEMES = {
|
|
|
391
472
|
nerd: "emoji:node env:node-short sep:dot emoji:folder fs:dir sep:dot emoji:branch git:branch git:status",
|
|
392
473
|
compact: "claude:model sep:slash fs:dir sep:slash git:branch",
|
|
393
474
|
colorful: "bold:magenta:claude:model sep:arrow cyan:fs:dir sep:arrow green:git:branch yellow:git:status sep:arrow blue:ctx:percent",
|
|
394
|
-
luca: "claude:model-letter git:repo git:branch git:dirty cost:total"
|
|
475
|
+
luca: "bold:magenta:claude:model-letter sep:pipe cyan:nerd:repo cyan:git:repo sep:none dim:text:: sep:none green:git:branch if:subdir(sep:none white:text:/ sep:none white:fs:relative) if:dirty(sep:pipe git:dirty) sep:pipe white:cost:total sep:newline bold:white:usage:5h-bar:8 usage:5h dim:usage:5h-reset sep:pipe bold:white:usage:week-bar:8 usage:week dim:usage:week-reset"
|
|
395
476
|
};
|
|
396
477
|
function getTheme(name) {
|
|
397
478
|
return THEMES[name] || null;
|
|
@@ -543,10 +624,10 @@ function getSampleDataJson() {
|
|
|
543
624
|
}
|
|
544
625
|
|
|
545
626
|
// src/runtime.ts
|
|
546
|
-
import * as
|
|
547
|
-
import * as
|
|
548
|
-
import * as
|
|
549
|
-
import { execSync } from "child_process";
|
|
627
|
+
import * as path3 from "path";
|
|
628
|
+
import * as os3 from "os";
|
|
629
|
+
import * as fs3 from "fs";
|
|
630
|
+
import { execSync as execSync2 } from "child_process";
|
|
550
631
|
|
|
551
632
|
// src/separators.ts
|
|
552
633
|
var SEPARATORS = {
|
|
@@ -569,6 +650,9 @@ var SEPARATORS = {
|
|
|
569
650
|
bullet: " \u25E6 ",
|
|
570
651
|
diamond: " \u25C7 ",
|
|
571
652
|
star: " \u2605 ",
|
|
653
|
+
// Line break
|
|
654
|
+
newline: "\n",
|
|
655
|
+
br: "\n",
|
|
572
656
|
// Powerline-style
|
|
573
657
|
powerline: "",
|
|
574
658
|
"powerline-left": "",
|
|
@@ -640,6 +724,225 @@ function getEmoji(name) {
|
|
|
640
724
|
return EMOJIS[name] || "";
|
|
641
725
|
}
|
|
642
726
|
|
|
727
|
+
// src/usage.ts
|
|
728
|
+
import * as fs2 from "fs";
|
|
729
|
+
import * as path2 from "path";
|
|
730
|
+
import * as os2 from "os";
|
|
731
|
+
import { execSync } from "child_process";
|
|
732
|
+
var CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
733
|
+
var CACHE_FILE = path2.join(os2.tmpdir(), "claudeline-usage-cache.json");
|
|
734
|
+
function readCache() {
|
|
735
|
+
try {
|
|
736
|
+
const raw = fs2.readFileSync(CACHE_FILE, "utf8");
|
|
737
|
+
const cached = JSON.parse(raw);
|
|
738
|
+
if (Date.now() - cached.fetched_at < CACHE_TTL_MS) {
|
|
739
|
+
return cached;
|
|
740
|
+
}
|
|
741
|
+
} catch {
|
|
742
|
+
}
|
|
743
|
+
return null;
|
|
744
|
+
}
|
|
745
|
+
function writeCache(data) {
|
|
746
|
+
try {
|
|
747
|
+
fs2.writeFileSync(CACHE_FILE, JSON.stringify({ data, fetched_at: Date.now() }));
|
|
748
|
+
} catch {
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
function getOAuthToken() {
|
|
752
|
+
try {
|
|
753
|
+
const raw = execSync(
|
|
754
|
+
'security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null',
|
|
755
|
+
{ encoding: "utf8" }
|
|
756
|
+
).trim();
|
|
757
|
+
const parsed = JSON.parse(raw);
|
|
758
|
+
if (typeof parsed === "string") return parsed;
|
|
759
|
+
if (parsed.claudeAiOauth?.accessToken) return parsed.claudeAiOauth.accessToken;
|
|
760
|
+
if (parsed.accessToken) return parsed.accessToken;
|
|
761
|
+
if (parsed.access_token) return parsed.access_token;
|
|
762
|
+
return null;
|
|
763
|
+
} catch {
|
|
764
|
+
return null;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
function fetchUsage() {
|
|
768
|
+
const token = getOAuthToken();
|
|
769
|
+
if (!token) return null;
|
|
770
|
+
try {
|
|
771
|
+
const result = execSync(
|
|
772
|
+
`curl -s --max-time 5 -H "Authorization: Bearer ${token}" -H "anthropic-beta: oauth-2025-04-20" "https://api.anthropic.com/api/oauth/usage"`,
|
|
773
|
+
{ encoding: "utf8" }
|
|
774
|
+
).trim();
|
|
775
|
+
const data = JSON.parse(result);
|
|
776
|
+
if (data.five_hour && data.seven_day) {
|
|
777
|
+
return data;
|
|
778
|
+
}
|
|
779
|
+
return null;
|
|
780
|
+
} catch {
|
|
781
|
+
return null;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
function getUsageData() {
|
|
785
|
+
const cached = readCache();
|
|
786
|
+
if (cached) return cached.data;
|
|
787
|
+
const data = fetchUsage();
|
|
788
|
+
if (data) {
|
|
789
|
+
writeCache(data);
|
|
790
|
+
}
|
|
791
|
+
return data;
|
|
792
|
+
}
|
|
793
|
+
function formatTimeUntil(isoDate) {
|
|
794
|
+
const reset = new Date(isoDate).getTime();
|
|
795
|
+
const now = Date.now();
|
|
796
|
+
let diff = Math.max(0, reset - now);
|
|
797
|
+
if (diff === 0) return "now";
|
|
798
|
+
const days = Math.floor(diff / 864e5);
|
|
799
|
+
diff %= 864e5;
|
|
800
|
+
const hours = Math.floor(diff / 36e5);
|
|
801
|
+
diff %= 36e5;
|
|
802
|
+
const minutes = Math.floor(diff / 6e4);
|
|
803
|
+
if (days > 0) return `${days}d ${hours}h`;
|
|
804
|
+
if (hours > 0) return `${hours}h ${minutes}m`;
|
|
805
|
+
return `${minutes}m`;
|
|
806
|
+
}
|
|
807
|
+
function makeBar(pct, width, label) {
|
|
808
|
+
const filled = Math.round(pct / 100 * width);
|
|
809
|
+
const bar = "\u25B0".repeat(filled) + "\u25B1".repeat(width - filled);
|
|
810
|
+
return label ? label + bar : bar;
|
|
811
|
+
}
|
|
812
|
+
function evaluateUsageComponent(key, args) {
|
|
813
|
+
const data = getUsageData();
|
|
814
|
+
if (!data) return "";
|
|
815
|
+
switch (key) {
|
|
816
|
+
case "5h":
|
|
817
|
+
return Math.round(data.five_hour.utilization) + "%";
|
|
818
|
+
case "week":
|
|
819
|
+
case "7d":
|
|
820
|
+
return Math.round(data.seven_day.utilization) + "%";
|
|
821
|
+
case "5h-reset":
|
|
822
|
+
return formatTimeUntil(data.five_hour.resets_at);
|
|
823
|
+
case "week-reset":
|
|
824
|
+
case "7d-reset":
|
|
825
|
+
return formatTimeUntil(data.seven_day.resets_at);
|
|
826
|
+
case "5h-bar": {
|
|
827
|
+
const width = args ? parseInt(args, 10) || 10 : 10;
|
|
828
|
+
return makeBar(data.five_hour.utilization, width, "H");
|
|
829
|
+
}
|
|
830
|
+
case "week-bar":
|
|
831
|
+
case "7d-bar": {
|
|
832
|
+
const width = args ? parseInt(args, 10) || 10 : 10;
|
|
833
|
+
return makeBar(data.seven_day.utilization, width, "W");
|
|
834
|
+
}
|
|
835
|
+
case "5h-emoji": {
|
|
836
|
+
const pct = data.five_hour.utilization;
|
|
837
|
+
if (pct < 50) return "\u{1F7E2}";
|
|
838
|
+
if (pct < 75) return "\u{1F7E1}";
|
|
839
|
+
if (pct < 90) return "\u{1F7E0}";
|
|
840
|
+
return "\u{1F534}";
|
|
841
|
+
}
|
|
842
|
+
case "week-emoji":
|
|
843
|
+
case "7d-emoji": {
|
|
844
|
+
const pct = data.seven_day.utilization;
|
|
845
|
+
if (pct < 50) return "\u{1F7E2}";
|
|
846
|
+
if (pct < 75) return "\u{1F7E1}";
|
|
847
|
+
if (pct < 90) return "\u{1F7E0}";
|
|
848
|
+
return "\u{1F534}";
|
|
849
|
+
}
|
|
850
|
+
default:
|
|
851
|
+
return "";
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
// src/nerdfonts.ts
|
|
856
|
+
var NERD_ICONS = {
|
|
857
|
+
// Files & Folders
|
|
858
|
+
"folder": "\uF07B",
|
|
859
|
+
"folder-open": "\uF07C",
|
|
860
|
+
"file": "\uF15B",
|
|
861
|
+
"file-code": "\uF1C9",
|
|
862
|
+
// Git
|
|
863
|
+
"branch": "\uE725",
|
|
864
|
+
"repo": "\uF401",
|
|
865
|
+
"commit": "\uF417",
|
|
866
|
+
"merge": "\uF419",
|
|
867
|
+
"tag": "\uF412",
|
|
868
|
+
"stash": "\uF01C",
|
|
869
|
+
"pr": "\uF407",
|
|
870
|
+
"diff": "\uF440",
|
|
871
|
+
"compare": "\uF47D",
|
|
872
|
+
// Status
|
|
873
|
+
"check": "\uF00C",
|
|
874
|
+
"x": "\uF00D",
|
|
875
|
+
"warn": "\uF071",
|
|
876
|
+
"error": "\uF057",
|
|
877
|
+
"info": "\uF05A",
|
|
878
|
+
"question": "\uF059",
|
|
879
|
+
"bell": "\uF0F3",
|
|
880
|
+
"pin": "\uF08D",
|
|
881
|
+
// Decorative
|
|
882
|
+
"star": "\uF005",
|
|
883
|
+
"fire": "\uF490",
|
|
884
|
+
"rocket": "\uF135",
|
|
885
|
+
"sparkle": "\uF005",
|
|
886
|
+
"lightning": "\uF0E7",
|
|
887
|
+
"heart": "\uF004",
|
|
888
|
+
"diamond": "\uF219",
|
|
889
|
+
"circle": "\uF111",
|
|
890
|
+
"square": "\uF0C8",
|
|
891
|
+
"triangle": "\uF0D8",
|
|
892
|
+
// Tech
|
|
893
|
+
"node": "\uE718",
|
|
894
|
+
"python": "\uE73C",
|
|
895
|
+
"rust": "\uE7A8",
|
|
896
|
+
"go": "\uE626",
|
|
897
|
+
"ruby": "\uE739",
|
|
898
|
+
"java": "\uE738",
|
|
899
|
+
"docker": "\uE7B0",
|
|
900
|
+
"terminal": "\uF120",
|
|
901
|
+
"code": "\uF121",
|
|
902
|
+
"database": "\uF1C0",
|
|
903
|
+
"cloud": "\uF0C2",
|
|
904
|
+
"server": "\uF233",
|
|
905
|
+
"package": "\uF487",
|
|
906
|
+
"gear": "\uF013",
|
|
907
|
+
"lock": "\uF023",
|
|
908
|
+
"unlock": "\uF09C",
|
|
909
|
+
"key": "\uF084",
|
|
910
|
+
"shield": "\uF132",
|
|
911
|
+
// Arrows
|
|
912
|
+
"up": "\uF062",
|
|
913
|
+
"down": "\uF063",
|
|
914
|
+
"left": "\uF060",
|
|
915
|
+
"right": "\uF061",
|
|
916
|
+
"arrow-right": "\uF061",
|
|
917
|
+
"arrow-left": "\uF060",
|
|
918
|
+
// Time
|
|
919
|
+
"clock": "\uF017",
|
|
920
|
+
"calendar": "\uF073",
|
|
921
|
+
"history": "\uF1DA",
|
|
922
|
+
// Money
|
|
923
|
+
"money": "\uF0D6",
|
|
924
|
+
"dollar": "\uF155",
|
|
925
|
+
// OS
|
|
926
|
+
"apple": "\uF179",
|
|
927
|
+
"linux": "\uF17C",
|
|
928
|
+
"windows": "\uF17A",
|
|
929
|
+
// Misc
|
|
930
|
+
"search": "\uF002",
|
|
931
|
+
"eye": "\uF06E",
|
|
932
|
+
"bug": "\uF188",
|
|
933
|
+
"wrench": "\uF0AD",
|
|
934
|
+
"plug": "\uF1E6",
|
|
935
|
+
"wifi": "\uF1EB",
|
|
936
|
+
"bluetooth": "\uF293",
|
|
937
|
+
"cpu": "\uF2DB",
|
|
938
|
+
"memory": "\uF538",
|
|
939
|
+
"home": "\uF015",
|
|
940
|
+
"user": "\uF007"
|
|
941
|
+
};
|
|
942
|
+
function getNerdIcon(name) {
|
|
943
|
+
return NERD_ICONS[name] || "";
|
|
944
|
+
}
|
|
945
|
+
|
|
643
946
|
// src/runtime.ts
|
|
644
947
|
function formatTokens(n) {
|
|
645
948
|
if (n >= 1e6) return Math.round(n / 1e6) + "M";
|
|
@@ -654,7 +957,7 @@ function formatDuration(ms) {
|
|
|
654
957
|
}
|
|
655
958
|
function execCommand(cmd) {
|
|
656
959
|
try {
|
|
657
|
-
return
|
|
960
|
+
return execSync2(cmd, { encoding: "utf8" }).trim();
|
|
658
961
|
} catch {
|
|
659
962
|
return "";
|
|
660
963
|
}
|
|
@@ -690,13 +993,13 @@ function evaluateFsComponent(key, data) {
|
|
|
690
993
|
case "path":
|
|
691
994
|
return data.workspace?.current_dir || data.cwd || "";
|
|
692
995
|
case "dir":
|
|
693
|
-
return
|
|
996
|
+
return path3.basename(data.workspace?.current_dir || data.cwd || "");
|
|
694
997
|
case "project":
|
|
695
|
-
return
|
|
998
|
+
return path3.basename(data.workspace?.project_dir || "");
|
|
696
999
|
case "project-path":
|
|
697
1000
|
return data.workspace?.project_dir || "";
|
|
698
1001
|
case "home":
|
|
699
|
-
return (data.workspace?.current_dir || data.cwd || "").replace(
|
|
1002
|
+
return (data.workspace?.current_dir || data.cwd || "").replace(os3.homedir(), "~");
|
|
700
1003
|
case "cwd":
|
|
701
1004
|
return data.cwd || "";
|
|
702
1005
|
case "relative": {
|
|
@@ -705,33 +1008,33 @@ function evaluateFsComponent(key, data) {
|
|
|
705
1008
|
if (proj && curr.startsWith(proj)) {
|
|
706
1009
|
return curr.slice(proj.length + 1) || ".";
|
|
707
1010
|
}
|
|
708
|
-
return
|
|
1011
|
+
return path3.basename(curr);
|
|
709
1012
|
}
|
|
710
1013
|
default:
|
|
711
1014
|
return "";
|
|
712
1015
|
}
|
|
713
1016
|
}
|
|
714
|
-
function evaluateGitComponent(key) {
|
|
1017
|
+
function evaluateGitComponent(key, noColor = false) {
|
|
715
1018
|
switch (key) {
|
|
716
1019
|
case "branch":
|
|
717
1020
|
return execCommand("git branch --show-current 2>/dev/null");
|
|
718
1021
|
case "status":
|
|
719
1022
|
try {
|
|
720
|
-
|
|
1023
|
+
execSync2("git diff --quiet 2>/dev/null");
|
|
721
1024
|
return "\u2713";
|
|
722
1025
|
} catch {
|
|
723
1026
|
return "*";
|
|
724
1027
|
}
|
|
725
1028
|
case "status-emoji":
|
|
726
1029
|
try {
|
|
727
|
-
|
|
1030
|
+
execSync2("git diff --quiet 2>/dev/null");
|
|
728
1031
|
return "\u2728";
|
|
729
1032
|
} catch {
|
|
730
1033
|
return "\u{1F4DD}";
|
|
731
1034
|
}
|
|
732
1035
|
case "status-word":
|
|
733
1036
|
try {
|
|
734
|
-
|
|
1037
|
+
execSync2("git diff --quiet 2>/dev/null");
|
|
735
1038
|
return "clean";
|
|
736
1039
|
} catch {
|
|
737
1040
|
return "dirty";
|
|
@@ -774,9 +1077,16 @@ function evaluateGitComponent(key) {
|
|
|
774
1077
|
const untracked = parseInt(execCommand("git ls-files --others --exclude-standard 2>/dev/null | wc -l")) || 0;
|
|
775
1078
|
if (staged === 0 && modified === 0 && untracked === 0) return "";
|
|
776
1079
|
const parts = [];
|
|
777
|
-
if (
|
|
778
|
-
|
|
779
|
-
|
|
1080
|
+
if (noColor) {
|
|
1081
|
+
if (staged > 0) parts.push("+" + staged);
|
|
1082
|
+
if (untracked > 0) parts.push("-" + untracked);
|
|
1083
|
+
if (modified > 0) parts.push("~" + modified);
|
|
1084
|
+
} else {
|
|
1085
|
+
const r = `\x1B[${RESET}m`;
|
|
1086
|
+
if (staged > 0) parts.push(`\x1B[${COLORS.green}m+${staged}${r}`);
|
|
1087
|
+
if (untracked > 0) parts.push(`\x1B[${COLORS.red}m-${untracked}${r}`);
|
|
1088
|
+
if (modified > 0) parts.push(`\x1B[${COLORS.yellow}m~${modified}${r}`);
|
|
1089
|
+
}
|
|
780
1090
|
return parts.join(" ");
|
|
781
1091
|
}
|
|
782
1092
|
case "commit":
|
|
@@ -788,7 +1098,12 @@ function evaluateGitComponent(key) {
|
|
|
788
1098
|
case "remote":
|
|
789
1099
|
return execCommand("git remote 2>/dev/null").split("\n")[0] || "";
|
|
790
1100
|
case "repo":
|
|
791
|
-
return
|
|
1101
|
+
return path3.basename(execCommand("git rev-parse --show-toplevel 2>/dev/null"));
|
|
1102
|
+
case "repo-branch": {
|
|
1103
|
+
const r = path3.basename(execCommand("git rev-parse --show-toplevel 2>/dev/null"));
|
|
1104
|
+
const b = execCommand("git branch --show-current 2>/dev/null");
|
|
1105
|
+
return r && b ? `${r}:${b}` : r || b;
|
|
1106
|
+
}
|
|
792
1107
|
case "user":
|
|
793
1108
|
return execCommand("git config user.name 2>/dev/null");
|
|
794
1109
|
case "email":
|
|
@@ -901,13 +1216,13 @@ function evaluateEnvComponent(key) {
|
|
|
901
1216
|
return match?.[1] || "";
|
|
902
1217
|
}
|
|
903
1218
|
case "user":
|
|
904
|
-
return
|
|
1219
|
+
return os3.userInfo().username;
|
|
905
1220
|
case "hostname":
|
|
906
|
-
return
|
|
1221
|
+
return os3.hostname();
|
|
907
1222
|
case "hostname-short":
|
|
908
|
-
return
|
|
1223
|
+
return os3.hostname().split(".")[0];
|
|
909
1224
|
case "shell":
|
|
910
|
-
return
|
|
1225
|
+
return path3.basename(process.env.SHELL || "");
|
|
911
1226
|
case "term":
|
|
912
1227
|
return process.env.TERM || "";
|
|
913
1228
|
case "os":
|
|
@@ -915,11 +1230,11 @@ function evaluateEnvComponent(key) {
|
|
|
915
1230
|
case "arch":
|
|
916
1231
|
return process.arch;
|
|
917
1232
|
case "os-release":
|
|
918
|
-
return
|
|
1233
|
+
return os3.release();
|
|
919
1234
|
case "cpus":
|
|
920
|
-
return
|
|
1235
|
+
return os3.cpus().length.toString();
|
|
921
1236
|
case "memory":
|
|
922
|
-
return Math.round(
|
|
1237
|
+
return Math.round(os3.totalmem() / 1024 / 1024 / 1024) + "GB";
|
|
923
1238
|
default:
|
|
924
1239
|
return "";
|
|
925
1240
|
}
|
|
@@ -971,33 +1286,37 @@ function evaluateCondition(condition) {
|
|
|
971
1286
|
switch (condition) {
|
|
972
1287
|
case "git":
|
|
973
1288
|
try {
|
|
974
|
-
|
|
1289
|
+
execSync2("git rev-parse --git-dir 2>/dev/null");
|
|
975
1290
|
return true;
|
|
976
1291
|
} catch {
|
|
977
1292
|
return false;
|
|
978
1293
|
}
|
|
979
1294
|
case "dirty":
|
|
980
1295
|
try {
|
|
981
|
-
|
|
1296
|
+
execSync2("git diff --quiet 2>/dev/null");
|
|
982
1297
|
return false;
|
|
983
1298
|
} catch {
|
|
984
1299
|
return true;
|
|
985
1300
|
}
|
|
986
1301
|
case "clean":
|
|
987
1302
|
try {
|
|
988
|
-
|
|
1303
|
+
execSync2("git diff --quiet 2>/dev/null");
|
|
989
1304
|
return true;
|
|
990
1305
|
} catch {
|
|
991
1306
|
return false;
|
|
992
1307
|
}
|
|
1308
|
+
case "subdir": {
|
|
1309
|
+
const root = execCommand("git rev-parse --show-toplevel 2>/dev/null");
|
|
1310
|
+
return root !== "" && process.cwd() !== root;
|
|
1311
|
+
}
|
|
993
1312
|
case "node":
|
|
994
|
-
return
|
|
1313
|
+
return fs3.existsSync("package.json");
|
|
995
1314
|
case "python":
|
|
996
|
-
return
|
|
1315
|
+
return fs3.existsSync("pyproject.toml") || fs3.existsSync("setup.py") || fs3.existsSync("requirements.txt");
|
|
997
1316
|
case "rust":
|
|
998
|
-
return
|
|
1317
|
+
return fs3.existsSync("Cargo.toml");
|
|
999
1318
|
case "go":
|
|
1000
|
-
return
|
|
1319
|
+
return fs3.existsSync("go.mod");
|
|
1001
1320
|
default:
|
|
1002
1321
|
return true;
|
|
1003
1322
|
}
|
|
@@ -1012,7 +1331,7 @@ function evaluateComponent(comp, data, options) {
|
|
|
1012
1331
|
result = evaluateFsComponent(comp.key, data);
|
|
1013
1332
|
break;
|
|
1014
1333
|
case "git":
|
|
1015
|
-
result = evaluateGitComponent(comp.key);
|
|
1334
|
+
result = evaluateGitComponent(comp.key, options.noColor);
|
|
1016
1335
|
break;
|
|
1017
1336
|
case "ctx":
|
|
1018
1337
|
result = evaluateContextComponent(comp.key, data, comp.args);
|
|
@@ -1023,6 +1342,9 @@ function evaluateComponent(comp, data, options) {
|
|
|
1023
1342
|
case "env":
|
|
1024
1343
|
result = evaluateEnvComponent(comp.key);
|
|
1025
1344
|
break;
|
|
1345
|
+
case "usage":
|
|
1346
|
+
result = evaluateUsageComponent(comp.key, comp.args);
|
|
1347
|
+
break;
|
|
1026
1348
|
case "time":
|
|
1027
1349
|
result = evaluateTimeComponent(comp.key, data);
|
|
1028
1350
|
break;
|
|
@@ -1032,6 +1354,9 @@ function evaluateComponent(comp, data, options) {
|
|
|
1032
1354
|
case "emoji":
|
|
1033
1355
|
result = options.noEmoji ? "" : getEmoji(comp.key);
|
|
1034
1356
|
break;
|
|
1357
|
+
case "nerd":
|
|
1358
|
+
result = options.noEmoji ? "" : getNerdIcon(comp.key);
|
|
1359
|
+
break;
|
|
1035
1360
|
case "text":
|
|
1036
1361
|
result = comp.key;
|
|
1037
1362
|
break;
|
|
@@ -1064,7 +1389,7 @@ function evaluateComponents(components, data, options) {
|
|
|
1064
1389
|
const comp = components[i];
|
|
1065
1390
|
const prev = components[i - 1];
|
|
1066
1391
|
const value = evaluateComponent(comp, data, options);
|
|
1067
|
-
if (i > 0 && comp.type !== "sep" && prev?.type !== "sep" && value) {
|
|
1392
|
+
if (i > 0 && comp.type !== "sep" && comp.type !== "conditional" && prev?.type !== "sep" && value) {
|
|
1068
1393
|
parts.push(" ");
|
|
1069
1394
|
}
|
|
1070
1395
|
if (value) {
|
|
@@ -1083,7 +1408,7 @@ function evaluateFormat(format, data, options = {}) {
|
|
|
1083
1408
|
}
|
|
1084
1409
|
|
|
1085
1410
|
// src/index.ts
|
|
1086
|
-
var VERSION = "1.
|
|
1411
|
+
var VERSION = "1.1.0";
|
|
1087
1412
|
async function readStdin() {
|
|
1088
1413
|
return new Promise((resolve, reject) => {
|
|
1089
1414
|
let input = "";
|
|
@@ -1099,9 +1424,19 @@ async function readStdin() {
|
|
|
1099
1424
|
}
|
|
1100
1425
|
program.command("run <format>").description("Evaluate a format string and output the status line").option("--disable-emoji", "Disable emoji output").option("--disable-color", "Disable color output").action(async (format, options) => {
|
|
1101
1426
|
try {
|
|
1427
|
+
let formatStr = format;
|
|
1428
|
+
if (format.startsWith("theme:")) {
|
|
1429
|
+
const themeName = format.slice(6);
|
|
1430
|
+
const theme = getTheme(themeName);
|
|
1431
|
+
if (!theme) {
|
|
1432
|
+
console.error(`Unknown theme: ${themeName}`);
|
|
1433
|
+
process.exit(1);
|
|
1434
|
+
}
|
|
1435
|
+
formatStr = theme;
|
|
1436
|
+
}
|
|
1102
1437
|
const input = await readStdin();
|
|
1103
1438
|
const data = JSON.parse(input);
|
|
1104
|
-
const output = evaluateFormat(
|
|
1439
|
+
const output = evaluateFormat(formatStr, data, {
|
|
1105
1440
|
noEmoji: options.disableEmoji ?? false,
|
|
1106
1441
|
noColor: options.disableColor ?? false
|
|
1107
1442
|
});
|
|
@@ -1160,6 +1495,23 @@ Multiple style prefixes can be chained: bold:green:git:branch
|
|
|
1160
1495
|
console.error(`Available themes: ${Object.keys(THEMES).join(", ")}`);
|
|
1161
1496
|
process.exit(1);
|
|
1162
1497
|
}
|
|
1498
|
+
if (options.install) {
|
|
1499
|
+
const result = install(`theme:${options.theme}`, options.project, {
|
|
1500
|
+
useBunx: options.useBunx,
|
|
1501
|
+
useNpx: options.useNpx,
|
|
1502
|
+
globalInstall: options.globalInstall,
|
|
1503
|
+
noEmoji: !options.emoji,
|
|
1504
|
+
noColor: !options.color
|
|
1505
|
+
});
|
|
1506
|
+
if (result.success) {
|
|
1507
|
+
console.log("\u2713 " + result.message);
|
|
1508
|
+
console.log("\nRestart Claude Code to see your new status line!");
|
|
1509
|
+
} else {
|
|
1510
|
+
console.error("\u2717 " + result.message);
|
|
1511
|
+
process.exit(1);
|
|
1512
|
+
}
|
|
1513
|
+
return;
|
|
1514
|
+
}
|
|
1163
1515
|
formatStr = theme;
|
|
1164
1516
|
}
|
|
1165
1517
|
if (!formatStr) {
|
package/package.json
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claudeline",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Customizable status line generator for Claude Code",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"claudeline": "./dist/index.js"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
|
-
"dist"
|
|
10
|
+
"dist",
|
|
11
|
+
".claude",
|
|
12
|
+
".claude-plugin"
|
|
11
13
|
],
|
|
12
14
|
"scripts": {
|
|
13
15
|
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
@@ -19,9 +21,10 @@
|
|
|
19
21
|
"claude-code",
|
|
20
22
|
"statusline",
|
|
21
23
|
"cli",
|
|
22
|
-
"terminal"
|
|
24
|
+
"terminal",
|
|
25
|
+
"claude-plugin"
|
|
23
26
|
],
|
|
24
|
-
"author": "",
|
|
27
|
+
"author": "Luca Silverentand",
|
|
25
28
|
"license": "MIT",
|
|
26
29
|
"devDependencies": {
|
|
27
30
|
"@types/node": "^20.11.0",
|