@chongdashu/cc-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/CHANGELOG.md +43 -0
- package/LICENSE +21 -0
- package/README.md +211 -0
- package/dist/index.js +789 -0
- package/dist/index.js.map +1 -0
- package/package.json +58 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.0.0] - 2025-08-13
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Initial release of cc-statusline
|
|
12
|
+
- Interactive configuration wizard with 2 simple prompts
|
|
13
|
+
- Bash script generation with optimized performance
|
|
14
|
+
- Real-time ccusage integration for usage statistics
|
|
15
|
+
- Preview mode for testing statusline scripts with mock data
|
|
16
|
+
- Auto-installation with settings.json configuration
|
|
17
|
+
- Support for directory, git, model, usage, session, token, and burn rate features
|
|
18
|
+
- TTY-aware color support with NO_COLOR environment variable respect
|
|
19
|
+
- Manual configuration instructions when auto-update fails
|
|
20
|
+
- Comprehensive documentation and examples
|
|
21
|
+
- Performance analysis and validation in preview mode
|
|
22
|
+
- Timestamp and npm URL in generated script headers
|
|
23
|
+
|
|
24
|
+
### Features
|
|
25
|
+
- š Working Directory display with `~` abbreviation
|
|
26
|
+
- šæ Git Branch integration
|
|
27
|
+
- š¤ Model Name & Version display
|
|
28
|
+
- šµ Real-time Usage & Cost tracking
|
|
29
|
+
- ā Session Time Remaining with progress bars
|
|
30
|
+
- š Token Statistics (optional)
|
|
31
|
+
- ā” Burn Rate monitoring (optional)
|
|
32
|
+
|
|
33
|
+
### Technical
|
|
34
|
+
- TypeScript with strict type checking
|
|
35
|
+
- ESM module support
|
|
36
|
+
- Commander.js for CLI interface
|
|
37
|
+
- Inquirer.js for interactive prompts
|
|
38
|
+
- Chalk for colorized output
|
|
39
|
+
- Ora for loading spinners
|
|
40
|
+
- Comprehensive error handling and validation
|
|
41
|
+
- Modular architecture for easy maintenance
|
|
42
|
+
|
|
43
|
+
[1.0.0]: https://github.com/chongdashu/cc-statusline/releases/tag/v1.0.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Chong-U Lim (AIOriented)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# cc-statusline
|
|
2
|
+
|
|
3
|
+
š **Dead simple statusline generator for Claude Code**
|
|
4
|
+
|
|
5
|
+
Transform your Claude Code experience with a beautiful, informative statusline showing directory, git branch, model info, usage stats, and more.
|
|
6
|
+
|
|
7
|
+
## ā” Super Quick Start
|
|
8
|
+
|
|
9
|
+
**Just run this one command:**
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx @chongdashu/cc-statusline init
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
That's it! Answer 2 simple questions and restart Claude Code. Done! š
|
|
16
|
+
|
|
17
|
+
### What you get:
|
|
18
|
+
- š Current directory
|
|
19
|
+
- šæ Git branch
|
|
20
|
+
- š¤ Claude model info
|
|
21
|
+
- šµ Real-time costs
|
|
22
|
+
- ā Session time remaining
|
|
23
|
+
- Plus optional token stats and burn rate
|
|
24
|
+
|
|
25
|
+
### Sample result:
|
|
26
|
+
```
|
|
27
|
+
š ~/my-project šæ main š¤ Opus 4.1 šµ $2.48 ($12.50/h) ā 2h 15m until reset (68%)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## šÆ More Options
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Preview an existing statusline.sh with mock data
|
|
34
|
+
cc-statusline preview .claude/statusline.sh
|
|
35
|
+
|
|
36
|
+
# Generate to custom location
|
|
37
|
+
cc-statusline init --output ./my-statusline.sh
|
|
38
|
+
|
|
39
|
+
# Skip auto-installation
|
|
40
|
+
cc-statusline init --no-install
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**How preview works:**
|
|
44
|
+
The `preview` command takes a path to an existing `statusline.sh` file and:
|
|
45
|
+
1. **Loads** your actual statusline script
|
|
46
|
+
2. **Runs** it with fake Claude Code data (directory: `/home/user/projects/my-project`, model: `Opus 4.1`, mock usage stats)
|
|
47
|
+
3. **Shows** you exactly what the output looks like
|
|
48
|
+
4. **Reports** performance and basic functionality
|
|
49
|
+
|
|
50
|
+
Perfect for testing your statusline changes before restarting Claude Code.
|
|
51
|
+
|
|
52
|
+
### Global Installation
|
|
53
|
+
```bash
|
|
54
|
+
# If you prefer global install
|
|
55
|
+
npm install -g @chongdashu/cc-statusline
|
|
56
|
+
cc-statusline init
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## šļø Available Features
|
|
60
|
+
|
|
61
|
+
**Default features (pre-selected):**
|
|
62
|
+
- š **Working Directory** - Current folder with `~` shorthand
|
|
63
|
+
- šæ **Git Branch** - Current branch name
|
|
64
|
+
- š¤ **Model Name** - Which Claude model you're using
|
|
65
|
+
- šµ **Usage & Cost** - Real-time cost tracking (requires ccusage)
|
|
66
|
+
- ā **Session Time** - Time until usage limit resets
|
|
67
|
+
|
|
68
|
+
**Optional features:**
|
|
69
|
+
- š **Token Statistics** - Total tokens used this session
|
|
70
|
+
- ā” **Burn Rate** - Tokens consumed per minute
|
|
71
|
+
|
|
72
|
+
## āļø How It Works
|
|
73
|
+
|
|
74
|
+
**Two simple questions:**
|
|
75
|
+
1. **What to show** - Pick features from a checklist (directory, git, model, costs, etc.)
|
|
76
|
+
2. **Colors & emojis** - Enable/disable colors and emoji icons
|
|
77
|
+
|
|
78
|
+
**Then it:**
|
|
79
|
+
- Generates a bash script optimized for speed
|
|
80
|
+
- Auto-installs to `.claude/statusline.sh`
|
|
81
|
+
- Updates your `.claude/settings.json`
|
|
82
|
+
- Shows you a preview of what it looks like
|
|
83
|
+
|
|
84
|
+
**Requirements:**
|
|
85
|
+
- Claude Code (obviously! š)
|
|
86
|
+
- `jq` command (usually pre-installed)
|
|
87
|
+
- `ccusage` for usage stats (works via `npx ccusage@latest` - no install needed)
|
|
88
|
+
|
|
89
|
+
## šØ Example Outputs
|
|
90
|
+
|
|
91
|
+
**Minimal setup:**
|
|
92
|
+
```
|
|
93
|
+
š ~/my-app šæ main š¤ Claude Sonnet
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**With usage tracking:**
|
|
97
|
+
```
|
|
98
|
+
š ~/my-app šæ main š¤ Opus 4.1 šµ $2.48 ($12.50/h)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Full features:**
|
|
102
|
+
```
|
|
103
|
+
š ~/projects/my-app šæ main š¤ Opus 4.1 ā 2h 15m until reset (68%) [======----] šµ $2.48 ($12.50/h)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## š Dependencies
|
|
107
|
+
|
|
108
|
+
**Required:**
|
|
109
|
+
- Claude Code (the tool you're already using!)
|
|
110
|
+
- `jq` for JSON processing (pre-installed on most systems)
|
|
111
|
+
|
|
112
|
+
**Optional:**
|
|
113
|
+
- `git` for branch display
|
|
114
|
+
- `ccusage` for usage stats (auto-installs via npx when needed)
|
|
115
|
+
|
|
116
|
+
**Check if you're ready:**
|
|
117
|
+
```bash
|
|
118
|
+
command -v jq && echo "ā
Ready to go!"
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## š What Gets Created
|
|
122
|
+
|
|
123
|
+
After running `cc-statusline init`, you'll have:
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
.claude/
|
|
127
|
+
āāā statusline.sh # Your custom statusline script
|
|
128
|
+
āāā settings.json # Auto-updated with statusline config
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Manual Setup (if auto-config fails):**
|
|
132
|
+
If the tool can't update your settings.json automatically, just add this:
|
|
133
|
+
|
|
134
|
+
```json
|
|
135
|
+
{
|
|
136
|
+
"statusLine": {
|
|
137
|
+
"type": "command",
|
|
138
|
+
"command": ".claude/statusline.sh",
|
|
139
|
+
"padding": 0
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Troubleshooting
|
|
145
|
+
|
|
146
|
+
### Statusline not showing
|
|
147
|
+
1. Restart Claude Code after installation
|
|
148
|
+
2. Verify `.claude/settings.json` contains:
|
|
149
|
+
```json
|
|
150
|
+
{
|
|
151
|
+
"statusLine": {
|
|
152
|
+
"type": "command",
|
|
153
|
+
"command": ".claude/statusline.sh",
|
|
154
|
+
"padding": 0
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Performance Issues
|
|
160
|
+
- Use `cc-statusline preview` to check execution time
|
|
161
|
+
- Reduce number of features if >500ms execution time
|
|
162
|
+
- Disable ccusage integration if not needed
|
|
163
|
+
|
|
164
|
+
### Missing Features
|
|
165
|
+
- Ensure `jq` is installed: `brew install jq` (macOS) or `apt install jq` (Ubuntu)
|
|
166
|
+
- Usage stats require ccusage (works automatically via `npx ccusage@latest`)
|
|
167
|
+
- Check script permissions: `chmod +x .claude/statusline.sh`
|
|
168
|
+
|
|
169
|
+
## Development
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
# Clone repository
|
|
173
|
+
git clone https://github.com/chongdashu/cc-statusline
|
|
174
|
+
cd cc-statusline
|
|
175
|
+
|
|
176
|
+
# Install dependencies
|
|
177
|
+
npm install
|
|
178
|
+
|
|
179
|
+
# Build project
|
|
180
|
+
npm run build
|
|
181
|
+
|
|
182
|
+
# Test locally
|
|
183
|
+
npm run dev
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Contributing
|
|
187
|
+
|
|
188
|
+
Contributions welcome! Please read our [Contributing Guide](CONTRIBUTING.md).
|
|
189
|
+
|
|
190
|
+
1. Fork the repository
|
|
191
|
+
2. Create feature branch (`git checkout -b feature/amazing-feature`)
|
|
192
|
+
3. Commit changes (`git commit -m 'Add amazing feature'`)
|
|
193
|
+
4. Push to branch (`git push origin feature/amazing-feature`)
|
|
194
|
+
5. Open Pull Request
|
|
195
|
+
|
|
196
|
+
## License
|
|
197
|
+
|
|
198
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
199
|
+
|
|
200
|
+
## Related Projects
|
|
201
|
+
|
|
202
|
+
- [ccusage](https://github.com/ryoppippi/ccusage) - Claude Code usage analytics
|
|
203
|
+
- [Claude Code](https://docs.anthropic.com/en/docs/claude-code) - Official documentation
|
|
204
|
+
|
|
205
|
+
## Changelog
|
|
206
|
+
|
|
207
|
+
See [CHANGELOG.md](CHANGELOG.md) for detailed release history.
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
**Made by [Chong-U](https://github.com/chongdashu) @ [AIOriented](https://aioriented.dev)**
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,789 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __esm = (fn, res) => function __init() {
|
|
5
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
|
+
};
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// node_modules/tsup/assets/esm_shims.js
|
|
13
|
+
import path from "path";
|
|
14
|
+
import { fileURLToPath } from "url";
|
|
15
|
+
var init_esm_shims = __esm({
|
|
16
|
+
"node_modules/tsup/assets/esm_shims.js"() {
|
|
17
|
+
"use strict";
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// src/utils/tester.ts
|
|
22
|
+
var tester_exports = {};
|
|
23
|
+
__export(tester_exports, {
|
|
24
|
+
analyzeTestResult: () => analyzeTestResult,
|
|
25
|
+
generateMockCcusageOutput: () => generateMockCcusageOutput,
|
|
26
|
+
generateMockClaudeInput: () => generateMockClaudeInput,
|
|
27
|
+
testStatuslineScript: () => testStatuslineScript
|
|
28
|
+
});
|
|
29
|
+
import { spawn } from "child_process";
|
|
30
|
+
import { promises as fs2 } from "fs";
|
|
31
|
+
import path3 from "path";
|
|
32
|
+
async function testStatuslineScript(script, mockData) {
|
|
33
|
+
const startTime = Date.now();
|
|
34
|
+
try {
|
|
35
|
+
const tempDir = "/tmp";
|
|
36
|
+
const scriptPath = path3.join(tempDir, `statusline-test-${Date.now()}.sh`);
|
|
37
|
+
await fs2.writeFile(scriptPath, script, { mode: 493 });
|
|
38
|
+
const input = mockData || generateMockClaudeInput();
|
|
39
|
+
const result = await executeScript(scriptPath, JSON.stringify(input));
|
|
40
|
+
await fs2.unlink(scriptPath).catch(() => {
|
|
41
|
+
});
|
|
42
|
+
const executionTime = Date.now() - startTime;
|
|
43
|
+
return {
|
|
44
|
+
success: result.success,
|
|
45
|
+
output: result.output,
|
|
46
|
+
error: result.error,
|
|
47
|
+
executionTime
|
|
48
|
+
};
|
|
49
|
+
} catch (error) {
|
|
50
|
+
return {
|
|
51
|
+
success: false,
|
|
52
|
+
output: "",
|
|
53
|
+
error: error instanceof Error ? error.message : String(error),
|
|
54
|
+
executionTime: Date.now() - startTime
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function generateMockClaudeInput(config) {
|
|
59
|
+
return {
|
|
60
|
+
session_id: "test-session-123",
|
|
61
|
+
transcript_path: "/home/user/.claude/conversations/test.jsonl",
|
|
62
|
+
cwd: "/home/user/projects/my-project",
|
|
63
|
+
workspace: {
|
|
64
|
+
current_dir: "/home/user/projects/my-project"
|
|
65
|
+
},
|
|
66
|
+
model: {
|
|
67
|
+
id: "claude-opus-4-1-20250805",
|
|
68
|
+
display_name: "Opus 4.1",
|
|
69
|
+
version: "20250805"
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function generateMockCcusageOutput() {
|
|
74
|
+
return {
|
|
75
|
+
blocks: [
|
|
76
|
+
{
|
|
77
|
+
id: "2025-08-13T08:00:00.000Z",
|
|
78
|
+
startTime: "2025-08-13T08:00:00.000Z",
|
|
79
|
+
endTime: "2025-08-13T13:00:00.000Z",
|
|
80
|
+
usageLimitResetTime: "2025-08-13T13:00:00.000Z",
|
|
81
|
+
actualEndTime: "2025-08-13T09:30:34.698Z",
|
|
82
|
+
isActive: true,
|
|
83
|
+
isGap: false,
|
|
84
|
+
entries: 12,
|
|
85
|
+
tokenCounts: {
|
|
86
|
+
inputTokens: 1250,
|
|
87
|
+
outputTokens: 2830,
|
|
88
|
+
cacheCreationInputTokens: 15e3,
|
|
89
|
+
cacheReadInputTokens: 45e3
|
|
90
|
+
},
|
|
91
|
+
totalTokens: 64080,
|
|
92
|
+
costUSD: 3.42,
|
|
93
|
+
models: ["claude-opus-4-1-20250805"],
|
|
94
|
+
burnRate: {
|
|
95
|
+
tokensPerMinute: 850.5,
|
|
96
|
+
tokensPerMinuteForIndicator: 850,
|
|
97
|
+
costPerHour: 12.45
|
|
98
|
+
},
|
|
99
|
+
projection: {
|
|
100
|
+
totalTokens: 128e3,
|
|
101
|
+
totalCost: 6.84,
|
|
102
|
+
remainingMinutes: 210
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
]
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
async function executeScript(scriptPath, input) {
|
|
109
|
+
return new Promise((resolve) => {
|
|
110
|
+
const process2 = spawn("bash", [scriptPath], {
|
|
111
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
112
|
+
});
|
|
113
|
+
let stdout = "";
|
|
114
|
+
let stderr = "";
|
|
115
|
+
process2.stdout.on("data", (data) => {
|
|
116
|
+
stdout += data.toString();
|
|
117
|
+
});
|
|
118
|
+
process2.stderr.on("data", (data) => {
|
|
119
|
+
stderr += data.toString();
|
|
120
|
+
});
|
|
121
|
+
process2.on("close", (code) => {
|
|
122
|
+
resolve({
|
|
123
|
+
success: code === 0,
|
|
124
|
+
output: stdout.trim(),
|
|
125
|
+
error: stderr.trim() || void 0
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
process2.on("error", (err) => {
|
|
129
|
+
resolve({
|
|
130
|
+
success: false,
|
|
131
|
+
output: "",
|
|
132
|
+
error: err.message
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
process2.stdin.write(input);
|
|
136
|
+
process2.stdin.end();
|
|
137
|
+
setTimeout(() => {
|
|
138
|
+
process2.kill();
|
|
139
|
+
resolve({
|
|
140
|
+
success: false,
|
|
141
|
+
output: stdout,
|
|
142
|
+
error: "Script execution timed out (5s)"
|
|
143
|
+
});
|
|
144
|
+
}, 5e3);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
function analyzeTestResult(result, config) {
|
|
148
|
+
const issues = [];
|
|
149
|
+
const suggestions = [];
|
|
150
|
+
let performance;
|
|
151
|
+
if (result.executionTime > 1e3) {
|
|
152
|
+
performance = "timeout";
|
|
153
|
+
issues.push("Script execution is very slow (>1s)");
|
|
154
|
+
} else if (result.executionTime > 500) {
|
|
155
|
+
performance = "slow";
|
|
156
|
+
issues.push("Script execution is slow (>500ms)");
|
|
157
|
+
} else if (result.executionTime > 100) {
|
|
158
|
+
performance = "good";
|
|
159
|
+
} else {
|
|
160
|
+
performance = "excellent";
|
|
161
|
+
}
|
|
162
|
+
let hasRequiredFeatures = true;
|
|
163
|
+
if (config.features.includes("directory") && !result.output.includes("projects")) {
|
|
164
|
+
hasRequiredFeatures = false;
|
|
165
|
+
issues.push("Directory feature not working properly");
|
|
166
|
+
}
|
|
167
|
+
if (config.features.includes("model") && !result.output.includes("Opus")) {
|
|
168
|
+
hasRequiredFeatures = false;
|
|
169
|
+
issues.push("Model feature not working properly");
|
|
170
|
+
}
|
|
171
|
+
if (config.features.includes("git") && config.ccusageIntegration && !result.output.includes("git")) {
|
|
172
|
+
suggestions.push("Git integration may require actual git repository");
|
|
173
|
+
}
|
|
174
|
+
if (result.error) {
|
|
175
|
+
issues.push(`Script errors: ${result.error}`);
|
|
176
|
+
}
|
|
177
|
+
if (!result.success) {
|
|
178
|
+
issues.push("Script failed to execute successfully");
|
|
179
|
+
}
|
|
180
|
+
if (config.features.length > 6) {
|
|
181
|
+
suggestions.push("Consider reducing number of features for better performance");
|
|
182
|
+
}
|
|
183
|
+
if (config.ccusageIntegration && result.executionTime > 200) {
|
|
184
|
+
suggestions.push("ccusage integration may slow down statusline - consider caching");
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
performance,
|
|
188
|
+
hasRequiredFeatures,
|
|
189
|
+
issues,
|
|
190
|
+
suggestions
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
var init_tester = __esm({
|
|
194
|
+
"src/utils/tester.ts"() {
|
|
195
|
+
"use strict";
|
|
196
|
+
init_esm_shims();
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// src/cli/preview.ts
|
|
201
|
+
var preview_exports = {};
|
|
202
|
+
__export(preview_exports, {
|
|
203
|
+
previewCommand: () => previewCommand
|
|
204
|
+
});
|
|
205
|
+
import { promises as fs3 } from "fs";
|
|
206
|
+
import chalk2 from "chalk";
|
|
207
|
+
import ora2 from "ora";
|
|
208
|
+
async function previewCommand(scriptPath) {
|
|
209
|
+
console.log(chalk2.cyan("\u{1F50D} Statusline Preview Mode\n"));
|
|
210
|
+
let script;
|
|
211
|
+
try {
|
|
212
|
+
const spinner = ora2(`Loading statusline script from ${scriptPath}...`).start();
|
|
213
|
+
script = await fs3.readFile(scriptPath, "utf-8");
|
|
214
|
+
spinner.succeed("Script loaded!");
|
|
215
|
+
const headerMatch = script.match(/# Theme: (\w+) \| Colors: (\w+) \| Features: ([^\n]+)/i);
|
|
216
|
+
if (headerMatch) {
|
|
217
|
+
console.log(chalk2.yellow("Detected Configuration:"));
|
|
218
|
+
console.log(` Theme: ${headerMatch[1]}`);
|
|
219
|
+
console.log(` Colors: ${headerMatch[2]}`);
|
|
220
|
+
console.log(` Features: ${headerMatch[3]}
|
|
221
|
+
`);
|
|
222
|
+
}
|
|
223
|
+
const generationMatch = script.match(/# Generated by cc-statusline.*\n# Custom Claude Code statusline - Created: ([^\n]+)/i);
|
|
224
|
+
if (generationMatch) {
|
|
225
|
+
console.log(chalk2.gray(`Generated: ${generationMatch[1]}
|
|
226
|
+
`));
|
|
227
|
+
}
|
|
228
|
+
} catch (error) {
|
|
229
|
+
console.error(chalk2.red(`\u274C Failed to load script: ${error instanceof Error ? error.message : String(error)}`));
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
const testSpinner = ora2("Testing statusline with mock data...").start();
|
|
233
|
+
const mockInput = generateMockClaudeInput();
|
|
234
|
+
console.log(chalk2.gray("\nMock Claude Code Input:"));
|
|
235
|
+
console.log(chalk2.gray(JSON.stringify(mockInput, null, 2)));
|
|
236
|
+
const testResult = await testStatuslineScript(script, mockInput);
|
|
237
|
+
if (testResult.success) {
|
|
238
|
+
testSpinner.succeed(`Test completed in ${testResult.executionTime}ms`);
|
|
239
|
+
console.log(chalk2.green("\n\u2705 Statusline Output:"));
|
|
240
|
+
console.log(chalk2.white("\u2501".repeat(60)));
|
|
241
|
+
console.log(testResult.output);
|
|
242
|
+
console.log(chalk2.white("\u2501".repeat(60)));
|
|
243
|
+
console.log(chalk2.cyan(`
|
|
244
|
+
\u{1F4CA} Performance: ${getPerformanceEmoji(getPerformanceLevel(testResult.executionTime))} ${getPerformanceLevel(testResult.executionTime)} (${testResult.executionTime}ms)`));
|
|
245
|
+
if (testResult.output.includes("\u{1F4C1}") || testResult.output.includes("\u{1F33F}") || testResult.output.includes("\u{1F916}")) {
|
|
246
|
+
console.log(chalk2.green("\u2705 Statusline features appear to be working"));
|
|
247
|
+
} else {
|
|
248
|
+
console.log(chalk2.yellow("\u26A0\uFE0F Basic features may not be displaying correctly"));
|
|
249
|
+
}
|
|
250
|
+
} else {
|
|
251
|
+
testSpinner.fail("Test failed");
|
|
252
|
+
console.error(chalk2.red(`
|
|
253
|
+
\u274C Error: ${testResult.error}`));
|
|
254
|
+
if (testResult.output) {
|
|
255
|
+
console.log(chalk2.gray("\nPartial output:"));
|
|
256
|
+
console.log(testResult.output);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
console.log(chalk2.green("\n\u2728 Preview complete! Use `cc-statusline init` to generate a new statusline."));
|
|
260
|
+
}
|
|
261
|
+
function getPerformanceEmoji(performance) {
|
|
262
|
+
switch (performance) {
|
|
263
|
+
case "excellent":
|
|
264
|
+
return "\u{1F680}";
|
|
265
|
+
case "good":
|
|
266
|
+
return "\u2705";
|
|
267
|
+
case "slow":
|
|
268
|
+
return "\u26A0\uFE0F";
|
|
269
|
+
case "timeout":
|
|
270
|
+
return "\u{1F40C}";
|
|
271
|
+
default:
|
|
272
|
+
return "\u2753";
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
function getPerformanceLevel(executionTime) {
|
|
276
|
+
if (executionTime > 1e3) return "timeout";
|
|
277
|
+
if (executionTime > 500) return "slow";
|
|
278
|
+
if (executionTime > 100) return "good";
|
|
279
|
+
return "excellent";
|
|
280
|
+
}
|
|
281
|
+
var init_preview = __esm({
|
|
282
|
+
"src/cli/preview.ts"() {
|
|
283
|
+
"use strict";
|
|
284
|
+
init_esm_shims();
|
|
285
|
+
init_tester();
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
// src/index.ts
|
|
290
|
+
init_esm_shims();
|
|
291
|
+
import { Command } from "commander";
|
|
292
|
+
|
|
293
|
+
// src/cli/commands.ts
|
|
294
|
+
init_esm_shims();
|
|
295
|
+
|
|
296
|
+
// src/cli/prompts.ts
|
|
297
|
+
init_esm_shims();
|
|
298
|
+
import inquirer from "inquirer";
|
|
299
|
+
async function collectConfiguration() {
|
|
300
|
+
console.log("\u{1F680} Welcome to cc-statusline! Let's create your custom Claude Code statusline.\n");
|
|
301
|
+
const config = await inquirer.prompt([
|
|
302
|
+
{
|
|
303
|
+
type: "checkbox",
|
|
304
|
+
name: "features",
|
|
305
|
+
message: "What would you like to display in your statusline?",
|
|
306
|
+
choices: [
|
|
307
|
+
{ name: "\u{1F4C1} Working Directory", value: "directory", checked: true },
|
|
308
|
+
{ name: "\u{1F33F} Git Branch", value: "git", checked: true },
|
|
309
|
+
{ name: "\u{1F916} Model Name & Version", value: "model", checked: true },
|
|
310
|
+
{ name: "\u{1F4B5} Usage & Cost", value: "usage", checked: true },
|
|
311
|
+
{ name: "\u231B Session Time Remaining", value: "session", checked: true },
|
|
312
|
+
{ name: "\u{1F4CA} Token Statistics", value: "tokens", checked: false },
|
|
313
|
+
{ name: "\u26A1 Burn Rate (tokens/min)", value: "burnrate", checked: false }
|
|
314
|
+
],
|
|
315
|
+
validate: (answer) => {
|
|
316
|
+
if (answer.length < 1) {
|
|
317
|
+
return "You must choose at least one feature.";
|
|
318
|
+
}
|
|
319
|
+
return true;
|
|
320
|
+
}
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
type: "confirm",
|
|
324
|
+
name: "colors",
|
|
325
|
+
message: "Enable colors and emojis?",
|
|
326
|
+
default: true
|
|
327
|
+
}
|
|
328
|
+
]);
|
|
329
|
+
return {
|
|
330
|
+
features: config.features,
|
|
331
|
+
runtime: "bash",
|
|
332
|
+
colors: config.colors,
|
|
333
|
+
theme: "detailed",
|
|
334
|
+
ccusageIntegration: true,
|
|
335
|
+
// Always enabled since npx works
|
|
336
|
+
logging: false,
|
|
337
|
+
customEmojis: false
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// src/generators/bash-generator.ts
|
|
342
|
+
init_esm_shims();
|
|
343
|
+
|
|
344
|
+
// src/features/colors.ts
|
|
345
|
+
init_esm_shims();
|
|
346
|
+
function generateColorBashCode(config) {
|
|
347
|
+
if (!config.enabled) {
|
|
348
|
+
return `
|
|
349
|
+
# ---- color helpers (disabled) ----
|
|
350
|
+
use_color=0
|
|
351
|
+
C() { :; }
|
|
352
|
+
RST() { :; }
|
|
353
|
+
`;
|
|
354
|
+
}
|
|
355
|
+
return `
|
|
356
|
+
# ---- color helpers (TTY-aware, respect NO_COLOR) ----
|
|
357
|
+
use_color=1
|
|
358
|
+
[ -t 1 ] || use_color=0
|
|
359
|
+
[ -n "$NO_COLOR" ] && use_color=0
|
|
360
|
+
|
|
361
|
+
C() { if [ "$use_color" -eq 1 ]; then printf '\\033[%sm' "$1"; fi; }
|
|
362
|
+
RST() { if [ "$use_color" -eq 1 ]; then printf '\\033[0m'; fi; }
|
|
363
|
+
`;
|
|
364
|
+
}
|
|
365
|
+
function generateBasicColors() {
|
|
366
|
+
return `
|
|
367
|
+
# ---- basic colors ----
|
|
368
|
+
dir_color() { if [ "$use_color" -eq 1 ]; then printf '\\033[1;36m'; fi; } # cyan
|
|
369
|
+
model_color() { if [ "$use_color" -eq 1 ]; then printf '\\033[1;35m'; fi; } # magenta
|
|
370
|
+
version_color() { if [ "$use_color" -eq 1 ]; then printf '\\033[1;33m'; fi; } # yellow
|
|
371
|
+
rst() { if [ "$use_color" -eq 1 ]; then printf '\\033[0m'; fi; }
|
|
372
|
+
`;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// src/features/git.ts
|
|
376
|
+
init_esm_shims();
|
|
377
|
+
function generateGitBashCode(config, colors) {
|
|
378
|
+
if (!config.enabled) return "";
|
|
379
|
+
const colorCode = colors ? `
|
|
380
|
+
# ---- git colors ----
|
|
381
|
+
git_color() { if [ "$use_color" -eq 1 ]; then printf '\\033[1;32m'; fi; }
|
|
382
|
+
rst() { if [ "$use_color" -eq 1 ]; then printf '\\033[0m'; fi; }
|
|
383
|
+
` : `
|
|
384
|
+
git_color() { :; }
|
|
385
|
+
rst() { :; }
|
|
386
|
+
`;
|
|
387
|
+
return `${colorCode}
|
|
388
|
+
# ---- git ----
|
|
389
|
+
git_branch=""
|
|
390
|
+
if git rev-parse --git-dir >/dev/null 2>&1; then
|
|
391
|
+
git_branch=$(git branch --show-current 2>/dev/null || git rev-parse --short HEAD 2>/dev/null)
|
|
392
|
+
fi`;
|
|
393
|
+
}
|
|
394
|
+
function generateGitDisplayCode(config, colors, emojis) {
|
|
395
|
+
if (!config.enabled) return "";
|
|
396
|
+
const branchEmoji = emojis ? "\u{1F33F}" : "git:";
|
|
397
|
+
let displayCode = `
|
|
398
|
+
# git display
|
|
399
|
+
if [ -n "$git_branch" ]; then
|
|
400
|
+
printf ' ${branchEmoji} %s%s%s' "$(git_color)" "$git_branch" "$(rst)"
|
|
401
|
+
fi`;
|
|
402
|
+
return displayCode;
|
|
403
|
+
}
|
|
404
|
+
function generateGitUtilities() {
|
|
405
|
+
return `
|
|
406
|
+
# git utilities
|
|
407
|
+
num_or_zero() { v="$1"; [[ "$v" =~ ^[0-9]+$ ]] && echo "$v" || echo 0; }`;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// src/features/usage.ts
|
|
411
|
+
init_esm_shims();
|
|
412
|
+
function generateUsageBashCode(config, colors) {
|
|
413
|
+
if (!config.enabled) return "";
|
|
414
|
+
const colorCode = colors ? `
|
|
415
|
+
# ---- usage colors ----
|
|
416
|
+
usage_color() { if [ "$use_color" -eq 1 ]; then printf '\\033[1;35m'; fi; }
|
|
417
|
+
cost_color() { if [ "$use_color" -eq 1 ]; then printf '\\033[1;36m'; fi; }
|
|
418
|
+
session_color() {
|
|
419
|
+
rem_pct=$(( 100 - session_pct ))
|
|
420
|
+
if (( rem_pct <= 10 )); then SCLR='1;31'
|
|
421
|
+
elif (( rem_pct <= 25 )); then SCLR='1;33'
|
|
422
|
+
else SCLR='1;32'; fi
|
|
423
|
+
if [ "$use_color" -eq 1 ]; then printf '\\033[%sm' "$SCLR"; fi
|
|
424
|
+
}
|
|
425
|
+
` : `
|
|
426
|
+
usage_color() { :; }
|
|
427
|
+
cost_color() { :; }
|
|
428
|
+
session_color() { :; }
|
|
429
|
+
`;
|
|
430
|
+
return `${colorCode}
|
|
431
|
+
# ---- ccusage integration ----
|
|
432
|
+
session_txt=""; session_pct=0; session_bar=""
|
|
433
|
+
cost_usd=""; cost_per_hour=""; tpm=""; tot_tokens=""
|
|
434
|
+
|
|
435
|
+
if command -v jq >/dev/null 2>&1; then
|
|
436
|
+
blocks_output=$(npx ccusage@latest blocks --json 2>/dev/null || ccusage blocks --json 2>/dev/null)
|
|
437
|
+
if [ -n "$blocks_output" ]; then
|
|
438
|
+
active_block=$(echo "$blocks_output" | jq -c '.blocks[] | select(.isActive == true)' 2>/dev/null | head -n1)
|
|
439
|
+
if [ -n "$active_block" ]; then${config.showCost ? `
|
|
440
|
+
cost_usd=$(echo "$active_block" | jq -r '.costUSD // empty')
|
|
441
|
+
cost_per_hour=$(echo "$active_block" | jq -r '.burnRate.costPerHour // empty')` : ""}${config.showTokens ? `
|
|
442
|
+
tot_tokens=$(echo "$active_block" | jq -r '.totalTokens // empty')` : ""}${config.showBurnRate ? `
|
|
443
|
+
tpm=$(echo "$active_block" | jq -r '.burnRate.tokensPerMinute // empty')` : ""}${config.showSession || config.showProgressBar ? `
|
|
444
|
+
|
|
445
|
+
# Session time calculation
|
|
446
|
+
reset_time_str=$(echo "$active_block" | jq -r '.usageLimitResetTime // .endTime // empty')
|
|
447
|
+
start_time_str=$(echo "$active_block" | jq -r '.startTime // empty')
|
|
448
|
+
|
|
449
|
+
if [ -n "$reset_time_str" ] && [ -n "$start_time_str" ]; then
|
|
450
|
+
start_sec=$(to_epoch "$start_time_str"); end_sec=$(to_epoch "$reset_time_str"); now_sec=$(date +%s)
|
|
451
|
+
total=$(( end_sec - start_sec )); (( total<1 )) && total=1
|
|
452
|
+
elapsed=$(( now_sec - start_sec )); (( elapsed<0 ))&&elapsed=0; (( elapsed>total ))&&elapsed=$total
|
|
453
|
+
session_pct=$(( elapsed * 100 / total ))
|
|
454
|
+
remaining=$(( end_sec - now_sec )); (( remaining<0 )) && remaining=0
|
|
455
|
+
rh=$(( remaining / 3600 )); rm=$(( (remaining % 3600) / 60 ))
|
|
456
|
+
end_hm=$(fmt_time_hm "$end_sec")${config.showSession ? `
|
|
457
|
+
session_txt="$(printf '%dh %dm until reset at %s (%d%%)' "$rh" "$rm" "$end_hm" "$session_pct")"` : ""}${config.showProgressBar ? `
|
|
458
|
+
session_bar=$(progress_bar "$session_pct" 10)` : ""}
|
|
459
|
+
fi` : ""}
|
|
460
|
+
fi
|
|
461
|
+
fi
|
|
462
|
+
fi`;
|
|
463
|
+
}
|
|
464
|
+
function generateUsageUtilities() {
|
|
465
|
+
return `
|
|
466
|
+
# ---- time helpers ----
|
|
467
|
+
to_epoch() {
|
|
468
|
+
ts="$1"
|
|
469
|
+
if command -v gdate >/dev/null 2>&1; then gdate -d "$ts" +%s 2>/dev/null && return; fi
|
|
470
|
+
date -u -j -f "%Y-%m-%dT%H:%M:%S%z" "\${ts/Z/+0000}" +%s 2>/dev/null && return
|
|
471
|
+
python3 - "$ts" <<'PY' 2>/dev/null
|
|
472
|
+
import sys, datetime
|
|
473
|
+
s=sys.argv[1].replace('Z','+00:00')
|
|
474
|
+
print(int(datetime.datetime.fromisoformat(s).timestamp()))
|
|
475
|
+
PY
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
fmt_time_hm() {
|
|
479
|
+
epoch="$1"
|
|
480
|
+
if date -r 0 +%s >/dev/null 2>&1; then date -r "$epoch" +"%H:%M"; else date -d "@$epoch" +"%H:%M"; fi
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
progress_bar() {
|
|
484
|
+
pct="\${1:-0}"; width="\${2:-10}"
|
|
485
|
+
[[ "$pct" =~ ^[0-9]+$ ]] || pct=0; ((pct<0))&&pct=0; ((pct>100))&&pct=100
|
|
486
|
+
filled=$(( pct * width / 100 )); empty=$(( width - filled ))
|
|
487
|
+
printf '%*s' "$filled" '' | tr ' ' '='
|
|
488
|
+
printf '%*s' "$empty" '' | tr ' ' '-'
|
|
489
|
+
}`;
|
|
490
|
+
}
|
|
491
|
+
function generateUsageDisplayCode(config, colors, emojis) {
|
|
492
|
+
if (!config.enabled) return "";
|
|
493
|
+
let displayCode = "";
|
|
494
|
+
if (config.showSession) {
|
|
495
|
+
const sessionEmoji = emojis ? "\u231B" : "session:";
|
|
496
|
+
displayCode += `
|
|
497
|
+
# session time
|
|
498
|
+
if [ -n "$session_txt" ]; then
|
|
499
|
+
printf ' ${sessionEmoji} %s%s%s' "$(session_color)" "$session_txt" "$(rst)"${config.showProgressBar ? `
|
|
500
|
+
printf ' %s[%s]%s' "$(session_color)" "$session_bar" "$(rst)"` : ""}
|
|
501
|
+
fi`;
|
|
502
|
+
}
|
|
503
|
+
if (config.showCost) {
|
|
504
|
+
const costEmoji = emojis ? "\u{1F4B5}" : "$";
|
|
505
|
+
displayCode += `
|
|
506
|
+
# cost
|
|
507
|
+
if [ -n "$cost_usd" ] && [[ "$cost_usd" =~ ^[0-9.]+$ ]]; then
|
|
508
|
+
if [ -n "$cost_per_hour" ] && [[ "$cost_per_hour" =~ ^[0-9.]+$ ]]; then
|
|
509
|
+
printf ' ${costEmoji} %s$%.2f ($%.2f/h)%s' "$(cost_color)" "$cost_usd" "$cost_per_hour" "$(rst)"
|
|
510
|
+
else
|
|
511
|
+
printf ' ${costEmoji} %s$%.2f%s' "$(cost_color)" "$cost_usd" "$(rst)"
|
|
512
|
+
fi
|
|
513
|
+
fi`;
|
|
514
|
+
}
|
|
515
|
+
if (config.showTokens) {
|
|
516
|
+
const tokenEmoji = emojis ? "\u{1F4CA}" : "tok:";
|
|
517
|
+
displayCode += `
|
|
518
|
+
# tokens
|
|
519
|
+
if [ -n "$tot_tokens" ] && [[ "$tot_tokens" =~ ^[0-9]+$ ]]; then
|
|
520
|
+
if [ -n "$tpm" ] && [[ "$tpm" =~ ^[0-9.]+$ ]] && ${config.showBurnRate ? "true" : "false"}; then
|
|
521
|
+
printf ' ${tokenEmoji} %s%s tok (%.0f tpm)%s' "$(usage_color)" "$tot_tokens" "$tpm" "$(rst)"
|
|
522
|
+
else
|
|
523
|
+
printf ' ${tokenEmoji} %s%s tok%s' "$(usage_color)" "$tot_tokens" "$(rst)"
|
|
524
|
+
fi
|
|
525
|
+
fi`;
|
|
526
|
+
}
|
|
527
|
+
return displayCode;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// src/generators/bash-generator.ts
|
|
531
|
+
function generateBashStatusline(config) {
|
|
532
|
+
const hasGit = config.features.includes("git");
|
|
533
|
+
const hasUsage = config.features.some((f) => ["usage", "session", "tokens", "burnrate"].includes(f));
|
|
534
|
+
const hasDirectory = config.features.includes("directory");
|
|
535
|
+
const hasModel = config.features.includes("model");
|
|
536
|
+
const usageConfig = {
|
|
537
|
+
enabled: hasUsage && config.ccusageIntegration,
|
|
538
|
+
showCost: config.features.includes("usage"),
|
|
539
|
+
showTokens: config.features.includes("tokens"),
|
|
540
|
+
showBurnRate: config.features.includes("burnrate"),
|
|
541
|
+
showSession: config.features.includes("session"),
|
|
542
|
+
showProgressBar: config.theme !== "minimal" && config.features.includes("session")
|
|
543
|
+
};
|
|
544
|
+
const gitConfig = {
|
|
545
|
+
enabled: hasGit,
|
|
546
|
+
showBranch: hasGit,
|
|
547
|
+
showChanges: false,
|
|
548
|
+
// Removed delta changes per user request
|
|
549
|
+
compactMode: config.theme === "compact"
|
|
550
|
+
};
|
|
551
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
552
|
+
const script = `#!/bin/bash
|
|
553
|
+
# Generated by cc-statusline (https://www.npmjs.com/package/@chongdashu/cc-statusline)
|
|
554
|
+
# Custom Claude Code statusline - Created: ${timestamp}
|
|
555
|
+
# Theme: ${config.theme} | Colors: ${config.colors} | Features: ${config.features.join(", ")}
|
|
556
|
+
|
|
557
|
+
${config.logging ? generateLoggingCode() : ""}
|
|
558
|
+
input=$(cat)
|
|
559
|
+
${generateColorBashCode({ enabled: config.colors, theme: config.theme })}
|
|
560
|
+
${config.colors ? generateBasicColors() : ""}
|
|
561
|
+
${hasUsage ? generateUsageUtilities() : ""}
|
|
562
|
+
${hasGit ? generateGitUtilities() : ""}
|
|
563
|
+
${generateBasicDataExtraction(hasDirectory, hasModel)}
|
|
564
|
+
${hasGit ? generateGitBashCode(gitConfig, config.colors) : ""}
|
|
565
|
+
${hasUsage ? generateUsageBashCode(usageConfig, config.colors) : ""}
|
|
566
|
+
${config.logging ? generateLoggingOutput() : ""}
|
|
567
|
+
${generateDisplaySection(config, gitConfig, usageConfig)}
|
|
568
|
+
`;
|
|
569
|
+
return script.replace(/\n\n\n+/g, "\n\n").trim() + "\n";
|
|
570
|
+
}
|
|
571
|
+
function generateLoggingCode() {
|
|
572
|
+
return `
|
|
573
|
+
LOG_FILE="\${HOME}/.claude/statusline.log"
|
|
574
|
+
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
|
|
575
|
+
|
|
576
|
+
# ---- logging ----
|
|
577
|
+
{
|
|
578
|
+
echo "[$TIMESTAMP] Status line triggered with input:"
|
|
579
|
+
(echo "$input" | jq . 2>/dev/null) || echo "$input"
|
|
580
|
+
echo "---"
|
|
581
|
+
} >> "$LOG_FILE" 2>/dev/null
|
|
582
|
+
`;
|
|
583
|
+
}
|
|
584
|
+
function generateBasicDataExtraction(hasDirectory, hasModel) {
|
|
585
|
+
return `
|
|
586
|
+
# ---- basics ----
|
|
587
|
+
if command -v jq >/dev/null 2>&1; then${hasDirectory ? `
|
|
588
|
+
current_dir=$(echo "$input" | jq -r '.workspace.current_dir // .cwd // "unknown"' 2>/dev/null | sed "s|^$HOME|~|g")` : ""}${hasModel ? `
|
|
589
|
+
model_name=$(echo "$input" | jq -r '.model.display_name // "Claude"' 2>/dev/null)
|
|
590
|
+
model_version=$(echo "$input" | jq -r '.model.version // ""' 2>/dev/null)` : ""}
|
|
591
|
+
else${hasDirectory ? `
|
|
592
|
+
current_dir="unknown"` : ""}${hasModel ? `
|
|
593
|
+
model_name="Claude"; model_version=""` : ""}
|
|
594
|
+
fi
|
|
595
|
+
`;
|
|
596
|
+
}
|
|
597
|
+
function generateLoggingOutput() {
|
|
598
|
+
return `
|
|
599
|
+
# ---- log extracted data ----
|
|
600
|
+
{
|
|
601
|
+
echo "[$TIMESTAMP] Extracted: dir=\${current_dir:-}, model=\${model_name:-}, version=\${model_version:-}, git=\${git_branch:-}, cost=\${cost_usd:-}, cost_ph=\${cost_per_hour:-}, tokens=\${tot_tokens:-}, tpm=\${tpm:-}, session_pct=\${session_pct:-}"
|
|
602
|
+
} >> "$LOG_FILE" 2>/dev/null
|
|
603
|
+
`;
|
|
604
|
+
}
|
|
605
|
+
function generateDisplaySection(config, gitConfig, usageConfig) {
|
|
606
|
+
const emojis = config.colors && !config.customEmojis;
|
|
607
|
+
let displayCode = `
|
|
608
|
+
# ---- render statusline ----`;
|
|
609
|
+
if (config.features.includes("directory")) {
|
|
610
|
+
const dirEmoji = emojis ? "\u{1F4C1}" : "dir:";
|
|
611
|
+
displayCode += `
|
|
612
|
+
printf '${dirEmoji} %s%s%s' "$(dir_color)" "$current_dir" "$(rst)"`;
|
|
613
|
+
}
|
|
614
|
+
displayCode += generateGitDisplayCode(gitConfig, config.colors, emojis);
|
|
615
|
+
if (config.features.includes("model")) {
|
|
616
|
+
const modelEmoji = emojis ? "\u{1F916}" : "model:";
|
|
617
|
+
displayCode += `
|
|
618
|
+
printf ' ${modelEmoji} %s%s%s' "$(model_color)" "$model_name" "$(rst)"
|
|
619
|
+
if [ -n "$model_version" ] && [ "$model_version" != "null" ]; then
|
|
620
|
+
printf ' \u{1F3F7}\uFE0F %s%s%s' "$(version_color)" "$model_version" "$(rst)"
|
|
621
|
+
fi`;
|
|
622
|
+
}
|
|
623
|
+
displayCode += generateUsageDisplayCode(usageConfig, config.colors, emojis);
|
|
624
|
+
return displayCode;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// src/utils/validator.ts
|
|
628
|
+
init_esm_shims();
|
|
629
|
+
function validateConfig(config) {
|
|
630
|
+
const errors = [];
|
|
631
|
+
const warnings = [];
|
|
632
|
+
if (!config.features || config.features.length === 0) {
|
|
633
|
+
errors.push("At least one display feature must be selected");
|
|
634
|
+
}
|
|
635
|
+
if (!["bash", "python", "node"].includes(config.runtime)) {
|
|
636
|
+
errors.push(`Invalid runtime: ${config.runtime}`);
|
|
637
|
+
}
|
|
638
|
+
if (!["minimal", "detailed", "compact"].includes(config.theme)) {
|
|
639
|
+
errors.push(`Invalid theme: ${config.theme}`);
|
|
640
|
+
}
|
|
641
|
+
const usageFeatures = ["usage", "session", "tokens", "burnrate"];
|
|
642
|
+
const hasUsageFeatures = config.features.some((f) => usageFeatures.includes(f));
|
|
643
|
+
if (hasUsageFeatures && !config.ccusageIntegration) {
|
|
644
|
+
warnings.push("Usage features selected but ccusage integration is disabled. Some features may not work properly.");
|
|
645
|
+
}
|
|
646
|
+
if (config.features.length > 5) {
|
|
647
|
+
warnings.push("Many features selected. This may impact statusline performance.");
|
|
648
|
+
}
|
|
649
|
+
if (config.customEmojis && !config.colors) {
|
|
650
|
+
warnings.push("Custom emojis enabled but colors disabled. Visual distinction may be limited.");
|
|
651
|
+
}
|
|
652
|
+
return {
|
|
653
|
+
isValid: errors.length === 0,
|
|
654
|
+
errors,
|
|
655
|
+
warnings
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// src/utils/installer.ts
|
|
660
|
+
init_esm_shims();
|
|
661
|
+
import { promises as fs } from "fs";
|
|
662
|
+
import path2 from "path";
|
|
663
|
+
async function installStatusline(script, outputPath, config) {
|
|
664
|
+
try {
|
|
665
|
+
const dir = path2.dirname(outputPath);
|
|
666
|
+
await fs.mkdir(dir, { recursive: true });
|
|
667
|
+
await fs.writeFile(outputPath, script, { mode: 493 });
|
|
668
|
+
await updateSettingsJson(dir, path2.basename(outputPath));
|
|
669
|
+
} catch (error) {
|
|
670
|
+
throw new Error(`Failed to install statusline: ${error instanceof Error ? error.message : String(error)}`);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
async function updateSettingsJson(claudeDir, scriptName) {
|
|
674
|
+
const settingsPath = path2.join(claudeDir, "settings.json");
|
|
675
|
+
try {
|
|
676
|
+
let settings = {};
|
|
677
|
+
try {
|
|
678
|
+
const settingsContent = await fs.readFile(settingsPath, "utf-8");
|
|
679
|
+
settings = JSON.parse(settingsContent);
|
|
680
|
+
} catch {
|
|
681
|
+
}
|
|
682
|
+
settings.statusLine = {
|
|
683
|
+
type: "command",
|
|
684
|
+
command: `.claude/${scriptName}`,
|
|
685
|
+
padding: 0
|
|
686
|
+
};
|
|
687
|
+
await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2));
|
|
688
|
+
} catch (error) {
|
|
689
|
+
console.warn(`Warning: Could not update settings.json: ${error instanceof Error ? error.message : String(error)}`);
|
|
690
|
+
throw new Error("SETTINGS_UPDATE_FAILED");
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// src/cli/commands.ts
|
|
695
|
+
import chalk from "chalk";
|
|
696
|
+
import ora from "ora";
|
|
697
|
+
import path4 from "path";
|
|
698
|
+
async function initCommand(options) {
|
|
699
|
+
try {
|
|
700
|
+
const spinner = ora("Initializing statusline generator...").start();
|
|
701
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
702
|
+
spinner.stop();
|
|
703
|
+
const config = await collectConfiguration();
|
|
704
|
+
const validation = validateConfig(config);
|
|
705
|
+
if (!validation.isValid) {
|
|
706
|
+
console.error(chalk.red("\u274C Configuration validation failed:"));
|
|
707
|
+
validation.errors.forEach((error) => console.error(chalk.red(` \u2022 ${error}`)));
|
|
708
|
+
process.exit(1);
|
|
709
|
+
}
|
|
710
|
+
const generationSpinner = ora("Generating statusline script...").start();
|
|
711
|
+
const script = generateBashStatusline(config);
|
|
712
|
+
const filename = "statusline.sh";
|
|
713
|
+
generationSpinner.succeed("Statusline script generated!");
|
|
714
|
+
console.log(chalk.cyan("\n\u2728 Your statusline will look like:"));
|
|
715
|
+
console.log(chalk.white("\u2501".repeat(60)));
|
|
716
|
+
const { testStatuslineScript: testStatuslineScript2, generateMockClaudeInput: generateMockClaudeInput2 } = await Promise.resolve().then(() => (init_tester(), tester_exports));
|
|
717
|
+
const mockInput = generateMockClaudeInput2();
|
|
718
|
+
const testResult = await testStatuslineScript2(script, mockInput);
|
|
719
|
+
if (testResult.success) {
|
|
720
|
+
console.log(testResult.output);
|
|
721
|
+
} else {
|
|
722
|
+
console.log(chalk.gray("\u{1F4C1} ~/projects/my-app \u{1F33F} main \u{1F916} Claude \u{1F4B5} $2.48 ($12.50/h)"));
|
|
723
|
+
console.log(chalk.gray("(Preview unavailable - will work when Claude Code runs it)"));
|
|
724
|
+
}
|
|
725
|
+
console.log(chalk.white("\u2501".repeat(60)));
|
|
726
|
+
const outputPath = options.output || `./.claude/${filename}`;
|
|
727
|
+
const resolvedPath = path4.resolve(outputPath);
|
|
728
|
+
if (options.install !== false) {
|
|
729
|
+
const installSpinner = ora("Installing statusline...").start();
|
|
730
|
+
try {
|
|
731
|
+
await installStatusline(script, resolvedPath, config);
|
|
732
|
+
installSpinner.succeed("\u2705 Statusline installed!");
|
|
733
|
+
console.log(chalk.green("\n\u{1F389} Success! Your custom statusline is ready!"));
|
|
734
|
+
console.log(chalk.cyan(`
|
|
735
|
+
\u{1F4C1} Generated file: ${chalk.white(resolvedPath)}`));
|
|
736
|
+
console.log(chalk.cyan("\nNext steps:"));
|
|
737
|
+
console.log(chalk.white(" 1. Restart Claude Code to see your new statusline"));
|
|
738
|
+
console.log(chalk.white(" 2. Usage statistics work via: npx ccusage@latest"));
|
|
739
|
+
} catch (error) {
|
|
740
|
+
installSpinner.fail("Failed to install statusline");
|
|
741
|
+
if (error instanceof Error && error.message === "SETTINGS_UPDATE_FAILED") {
|
|
742
|
+
console.log(chalk.yellow("\n\u26A0\uFE0F Settings.json could not be updated automatically."));
|
|
743
|
+
console.log(chalk.cyan("\nManual Configuration Required:"));
|
|
744
|
+
console.log(chalk.white("Add this to your .claude/settings.json file:"));
|
|
745
|
+
console.log(chalk.gray("\n{"));
|
|
746
|
+
console.log(chalk.gray(' "statusLine": {'));
|
|
747
|
+
console.log(chalk.gray(' "type": "command",'));
|
|
748
|
+
console.log(chalk.gray(` "command": ".claude/statusline.sh",`));
|
|
749
|
+
console.log(chalk.gray(' "padding": 0'));
|
|
750
|
+
console.log(chalk.gray(" }"));
|
|
751
|
+
console.log(chalk.gray("}"));
|
|
752
|
+
console.log(chalk.cyan(`
|
|
753
|
+
\u{1F4C1} Statusline script saved to: ${chalk.white(resolvedPath)}`));
|
|
754
|
+
} else {
|
|
755
|
+
console.error(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
756
|
+
console.log(chalk.cyan(`
|
|
757
|
+
\u{1F4C1} You can manually save the script to: ${chalk.white(resolvedPath)}`));
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
} else {
|
|
761
|
+
console.log(chalk.green("\n\u2705 Statusline generated successfully!"));
|
|
762
|
+
console.log(chalk.cyan(`
|
|
763
|
+
\u{1F4C1} Save this script to: ${chalk.white(resolvedPath)}`));
|
|
764
|
+
console.log(chalk.cyan("\nThen restart Claude Code to see your new statusline."));
|
|
765
|
+
}
|
|
766
|
+
} catch (error) {
|
|
767
|
+
console.error(chalk.red("\u274C An error occurred:"));
|
|
768
|
+
console.error(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
769
|
+
process.exit(1);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// src/index.ts
|
|
774
|
+
import chalk3 from "chalk";
|
|
775
|
+
var program = new Command();
|
|
776
|
+
program.name("cc-statusline").description("Interactive CLI tool for generating custom Claude Code statuslines").version("1.0.0");
|
|
777
|
+
program.command("init").description("Create a custom statusline with interactive prompts").option("-o, --output <path>", "Output path for statusline.sh", "./.claude/statusline.sh").option("--no-install", "Don't automatically install to .claude/statusline.sh").action(initCommand);
|
|
778
|
+
program.command("preview").description("Preview existing statusline.sh with mock data").argument("<script-path>", "Path to statusline.sh file to preview").action(async (scriptPath) => {
|
|
779
|
+
const { previewCommand: previewCommand2 } = await Promise.resolve().then(() => (init_preview(), preview_exports));
|
|
780
|
+
await previewCommand2(scriptPath);
|
|
781
|
+
});
|
|
782
|
+
program.command("test").description("Test statusline with real Claude Code JSON input").option("-c, --config <path>", "Configuration file to test").action(() => {
|
|
783
|
+
console.log(chalk3.yellow("Test command coming soon!"));
|
|
784
|
+
});
|
|
785
|
+
if (!process.argv.slice(2).length) {
|
|
786
|
+
program.outputHelp();
|
|
787
|
+
}
|
|
788
|
+
program.parse(process.argv);
|
|
789
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../node_modules/tsup/assets/esm_shims.js","../src/utils/tester.ts","../src/cli/preview.ts","../src/index.ts","../src/cli/commands.ts","../src/cli/prompts.ts","../src/generators/bash-generator.ts","../src/features/colors.ts","../src/features/git.ts","../src/features/usage.ts","../src/utils/validator.ts","../src/utils/installer.ts"],"sourcesContent":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n","import { StatuslineConfig } from '../cli/prompts.js'\nimport { spawn } from 'child_process'\nimport { promises as fs } from 'fs'\nimport path from 'path'\n\nexport interface TestResult {\n success: boolean\n output: string\n error?: string\n executionTime: number\n}\n\nexport async function testStatuslineScript(script: string, mockData?: any): Promise<TestResult> {\n const startTime = Date.now()\n \n try {\n // Create temporary script file\n const tempDir = '/tmp'\n const scriptPath = path.join(tempDir, `statusline-test-${Date.now()}.sh`)\n \n await fs.writeFile(scriptPath, script, { mode: 0o755 })\n \n // Generate mock input if not provided\n const input = mockData || generateMockClaudeInput()\n \n // Execute script\n const result = await executeScript(scriptPath, JSON.stringify(input))\n \n // Cleanup\n await fs.unlink(scriptPath).catch(() => {}) // Ignore cleanup errors\n \n const executionTime = Date.now() - startTime\n \n return {\n success: result.success,\n output: result.output,\n error: result.error,\n executionTime\n }\n \n } catch (error) {\n return {\n success: false,\n output: '',\n error: error instanceof Error ? error.message : String(error),\n executionTime: Date.now() - startTime\n }\n }\n}\n\nexport function generateMockClaudeInput(config?: Partial<StatuslineConfig>): any {\n return {\n session_id: \"test-session-123\",\n transcript_path: \"/home/user/.claude/conversations/test.jsonl\",\n cwd: \"/home/user/projects/my-project\",\n workspace: {\n current_dir: \"/home/user/projects/my-project\"\n },\n model: {\n id: \"claude-opus-4-1-20250805\",\n display_name: \"Opus 4.1\",\n version: \"20250805\"\n }\n }\n}\n\nexport function generateMockCcusageOutput(): any {\n return {\n blocks: [\n {\n id: \"2025-08-13T08:00:00.000Z\",\n startTime: \"2025-08-13T08:00:00.000Z\",\n endTime: \"2025-08-13T13:00:00.000Z\",\n usageLimitResetTime: \"2025-08-13T13:00:00.000Z\",\n actualEndTime: \"2025-08-13T09:30:34.698Z\",\n isActive: true,\n isGap: false,\n entries: 12,\n tokenCounts: {\n inputTokens: 1250,\n outputTokens: 2830,\n cacheCreationInputTokens: 15000,\n cacheReadInputTokens: 45000\n },\n totalTokens: 64080,\n costUSD: 3.42,\n models: [\"claude-opus-4-1-20250805\"],\n burnRate: {\n tokensPerMinute: 850.5,\n tokensPerMinuteForIndicator: 850,\n costPerHour: 12.45\n },\n projection: {\n totalTokens: 128000,\n totalCost: 6.84,\n remainingMinutes: 210\n }\n }\n ]\n }\n}\n\nasync function executeScript(scriptPath: string, input: string): Promise<{ success: boolean, output: string, error?: string }> {\n return new Promise((resolve) => {\n const process = spawn('bash', [scriptPath], {\n stdio: ['pipe', 'pipe', 'pipe']\n })\n \n let stdout = ''\n let stderr = ''\n \n process.stdout.on('data', (data) => {\n stdout += data.toString()\n })\n \n process.stderr.on('data', (data) => {\n stderr += data.toString()\n })\n \n process.on('close', (code) => {\n resolve({\n success: code === 0,\n output: stdout.trim(),\n error: stderr.trim() || undefined\n })\n })\n \n process.on('error', (err) => {\n resolve({\n success: false,\n output: '',\n error: err.message\n })\n })\n \n // Send input and close stdin\n process.stdin.write(input)\n process.stdin.end()\n \n // Timeout after 5 seconds\n setTimeout(() => {\n process.kill()\n resolve({\n success: false,\n output: stdout,\n error: 'Script execution timed out (5s)'\n })\n }, 5000)\n })\n}\n\nexport function analyzeTestResult(result: TestResult, config: StatuslineConfig): {\n performance: 'excellent' | 'good' | 'slow' | 'timeout'\n hasRequiredFeatures: boolean\n issues: string[]\n suggestions: string[]\n} {\n const issues: string[] = []\n const suggestions: string[] = []\n \n // Performance analysis\n let performance: 'excellent' | 'good' | 'slow' | 'timeout'\n if (result.executionTime > 1000) {\n performance = 'timeout'\n issues.push('Script execution is very slow (>1s)')\n } else if (result.executionTime > 500) {\n performance = 'slow'\n issues.push('Script execution is slow (>500ms)')\n } else if (result.executionTime > 100) {\n performance = 'good'\n } else {\n performance = 'excellent'\n }\n \n // Feature validation\n let hasRequiredFeatures = true\n \n if (config.features.includes('directory') && !result.output.includes('projects')) {\n hasRequiredFeatures = false\n issues.push('Directory feature not working properly')\n }\n \n if (config.features.includes('model') && !result.output.includes('Opus')) {\n hasRequiredFeatures = false\n issues.push('Model feature not working properly')\n }\n \n if (config.features.includes('git') && config.ccusageIntegration && !result.output.includes('git')) {\n suggestions.push('Git integration may require actual git repository')\n }\n \n // Error analysis\n if (result.error) {\n issues.push(`Script errors: ${result.error}`)\n }\n \n if (!result.success) {\n issues.push('Script failed to execute successfully')\n }\n \n // Performance suggestions\n if (config.features.length > 6) {\n suggestions.push('Consider reducing number of features for better performance')\n }\n \n if (config.ccusageIntegration && result.executionTime > 200) {\n suggestions.push('ccusage integration may slow down statusline - consider caching')\n }\n \n return {\n performance,\n hasRequiredFeatures,\n issues,\n suggestions\n }\n}","import { StatuslineConfig } from './prompts.js'\nimport { generateBashStatusline } from '../generators/bash-generator.js'\nimport { testStatuslineScript, generateMockClaudeInput, analyzeTestResult } from '../utils/tester.js'\nimport { promises as fs } from 'fs'\nimport chalk from 'chalk'\nimport ora from 'ora'\n\nexport async function previewCommand(scriptPath: string): Promise<void> {\n console.log(chalk.cyan('š Statusline Preview Mode\\n'))\n \n let script: string\n \n // Load existing statusline script\n try {\n const spinner = ora(`Loading statusline script from ${scriptPath}...`).start()\n script = await fs.readFile(scriptPath, 'utf-8')\n spinner.succeed('Script loaded!')\n \n // Try to extract config info from the script header\n const headerMatch = script.match(/# Theme: (\\w+) \\| Colors: (\\w+) \\| Features: ([^\\n]+)/i)\n if (headerMatch) {\n console.log(chalk.yellow('Detected Configuration:'))\n console.log(` Theme: ${headerMatch[1]}`)\n console.log(` Colors: ${headerMatch[2]}`) \n console.log(` Features: ${headerMatch[3]}\\n`)\n }\n \n // Extract generation info if available\n const generationMatch = script.match(/# Generated by cc-statusline.*\\n# Custom Claude Code statusline - Created: ([^\\n]+)/i)\n if (generationMatch) {\n console.log(chalk.gray(`Generated: ${generationMatch[1]}\\n`))\n }\n \n } catch (error) {\n console.error(chalk.red(`ā Failed to load script: ${error instanceof Error ? error.message : String(error)}`))\n return\n }\n \n // Test the script\n const testSpinner = ora('Testing statusline with mock data...').start()\n const mockInput = generateMockClaudeInput()\n \n console.log(chalk.gray('\\nMock Claude Code Input:'))\n console.log(chalk.gray(JSON.stringify(mockInput, null, 2)))\n \n const testResult = await testStatuslineScript(script, mockInput)\n \n if (testResult.success) {\n testSpinner.succeed(`Test completed in ${testResult.executionTime}ms`)\n \n console.log(chalk.green('\\nā
Statusline Output:'))\n console.log(chalk.white('ā'.repeat(60)))\n console.log(testResult.output)\n console.log(chalk.white('ā'.repeat(60)))\n \n // Basic performance analysis\n console.log(chalk.cyan(`\\nš Performance: ${getPerformanceEmoji(getPerformanceLevel(testResult.executionTime))} ${getPerformanceLevel(testResult.executionTime)} (${testResult.executionTime}ms)`))\n \n // Basic output validation\n if (testResult.output.includes('š') || testResult.output.includes('šæ') || testResult.output.includes('š¤')) {\n console.log(chalk.green('ā
Statusline features appear to be working'))\n } else {\n console.log(chalk.yellow('ā ļø Basic features may not be displaying correctly'))\n }\n \n } else {\n testSpinner.fail('Test failed')\n console.error(chalk.red(`\\nā Error: ${testResult.error}`))\n if (testResult.output) {\n console.log(chalk.gray('\\nPartial output:'))\n console.log(testResult.output)\n }\n }\n \n console.log(chalk.green('\\n⨠Preview complete! Use `cc-statusline init` to generate a new statusline.'))\n}\n\nfunction getPerformanceEmoji(performance: string): string {\n switch (performance) {\n case 'excellent': return 'š'\n case 'good': return 'ā
'\n case 'slow': return 'ā ļø'\n case 'timeout': return 'š'\n default: return 'ā'\n }\n}\n\nfunction getPerformanceLevel(executionTime: number): string {\n if (executionTime > 1000) return 'timeout'\n if (executionTime > 500) return 'slow'\n if (executionTime > 100) return 'good'\n return 'excellent'\n}","import { Command } from 'commander'\nimport { initCommand } from './cli/commands.js'\nimport chalk from 'chalk'\n\nconst program = new Command()\n\nprogram\n .name('cc-statusline')\n .description('Interactive CLI tool for generating custom Claude Code statuslines')\n .version('1.0.0')\n\nprogram\n .command('init')\n .description('Create a custom statusline with interactive prompts')\n .option('-o, --output <path>', 'Output path for statusline.sh', './.claude/statusline.sh')\n .option('--no-install', 'Don\\'t automatically install to .claude/statusline.sh')\n .action(initCommand)\n\nprogram\n .command('preview')\n .description('Preview existing statusline.sh with mock data')\n .argument('<script-path>', 'Path to statusline.sh file to preview')\n .action(async (scriptPath) => {\n const { previewCommand } = await import('./cli/preview.js')\n await previewCommand(scriptPath)\n })\n\nprogram\n .command('test')\n .description('Test statusline with real Claude Code JSON input')\n .option('-c, --config <path>', 'Configuration file to test')\n .action(() => {\n console.log(chalk.yellow('Test command coming soon!'))\n })\n\n// Show help if no command provided\nif (!process.argv.slice(2).length) {\n program.outputHelp()\n}\n\nprogram.parse(process.argv)","import { collectConfiguration, displayConfigSummary } from './prompts.js'\nimport { generateBashStatusline } from '../generators/bash-generator.js'\nimport { validateConfig } from '../utils/validator.js'\nimport { installStatusline } from '../utils/installer.js'\nimport chalk from 'chalk'\nimport ora from 'ora'\nimport path from 'path'\n\ninterface InitOptions {\n output?: string\n install?: boolean\n}\n\nexport async function initCommand(options: InitOptions): Promise<void> {\n try {\n const spinner = ora('Initializing statusline generator...').start()\n await new Promise(resolve => setTimeout(resolve, 500)) // Brief pause for UX\n spinner.stop()\n\n // Collect user configuration\n const config = await collectConfiguration()\n \n // Validate configuration\n const validation = validateConfig(config)\n if (!validation.isValid) {\n console.error(chalk.red('ā Configuration validation failed:'))\n validation.errors.forEach(error => console.error(chalk.red(` ⢠${error}`)))\n process.exit(1)\n }\n\n // Generate statusline script\n const generationSpinner = ora('Generating statusline script...').start()\n \n const script = generateBashStatusline(config)\n const filename = 'statusline.sh'\n \n generationSpinner.succeed('Statusline script generated!')\n\n // Show preview of what it will look like\n console.log(chalk.cyan('\\n⨠Your statusline will look like:'))\n console.log(chalk.white('ā'.repeat(60)))\n \n // Generate preview using the test function\n const { testStatuslineScript, generateMockClaudeInput } = await import('../utils/tester.js')\n const mockInput = generateMockClaudeInput()\n const testResult = await testStatuslineScript(script, mockInput)\n \n if (testResult.success) {\n console.log(testResult.output)\n } else {\n console.log(chalk.gray('š ~/projects/my-app šæ main š¤ Claude šµ $2.48 ($12.50/h)'))\n console.log(chalk.gray('(Preview unavailable - will work when Claude Code runs it)'))\n }\n \n console.log(chalk.white('ā'.repeat(60)))\n\n // Determine output path\n const outputPath = options.output || `./.claude/${filename}`\n const resolvedPath = path.resolve(outputPath)\n\n // Install the statusline\n if (options.install !== false) {\n const installSpinner = ora('Installing statusline...').start()\n \n try {\n await installStatusline(script, resolvedPath, config)\n installSpinner.succeed('ā
Statusline installed!')\n \n console.log(chalk.green('\\nš Success! Your custom statusline is ready!'))\n console.log(chalk.cyan(`\\nš Generated file: ${chalk.white(resolvedPath)}`))\n console.log(chalk.cyan('\\nNext steps:'))\n console.log(chalk.white(' 1. Restart Claude Code to see your new statusline'))\n console.log(chalk.white(' 2. Usage statistics work via: npx ccusage@latest'))\n \n } catch (error) {\n installSpinner.fail('Failed to install statusline')\n \n if (error instanceof Error && error.message === 'SETTINGS_UPDATE_FAILED') {\n console.log(chalk.yellow('\\nā ļø Settings.json could not be updated automatically.'))\n console.log(chalk.cyan('\\nManual Configuration Required:'))\n console.log(chalk.white('Add this to your .claude/settings.json file:'))\n console.log(chalk.gray('\\n{'))\n console.log(chalk.gray(' \"statusLine\": {'))\n console.log(chalk.gray(' \"type\": \"command\",'))\n console.log(chalk.gray(` \"command\": \".claude/statusline.sh\",`))\n console.log(chalk.gray(' \"padding\": 0'))\n console.log(chalk.gray(' }'))\n console.log(chalk.gray('}'))\n console.log(chalk.cyan(`\\nš Statusline script saved to: ${chalk.white(resolvedPath)}`))\n } else {\n console.error(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`))\n console.log(chalk.cyan(`\\nš You can manually save the script to: ${chalk.white(resolvedPath)}`))\n }\n }\n } else {\n // Just display where to save it\n console.log(chalk.green('\\nā
Statusline generated successfully!'))\n console.log(chalk.cyan(`\\nš Save this script to: ${chalk.white(resolvedPath)}`))\n console.log(chalk.cyan('\\nThen restart Claude Code to see your new statusline.'))\n }\n\n } catch (error) {\n console.error(chalk.red('ā An error occurred:'))\n console.error(chalk.red(error instanceof Error ? error.message : String(error)))\n process.exit(1)\n }\n}","import inquirer from 'inquirer'\n\nexport interface StatuslineConfig {\n features: string[]\n runtime: 'bash' | 'python' | 'node'\n colors: boolean\n theme: 'minimal' | 'detailed' | 'compact'\n ccusageIntegration: boolean\n logging: boolean\n customEmojis: boolean\n}\n\nexport async function collectConfiguration(): Promise<StatuslineConfig> {\n console.log('š Welcome to cc-statusline! Let\\'s create your custom Claude Code statusline.\\n')\n\n const config = await inquirer.prompt([\n {\n type: 'checkbox',\n name: 'features',\n message: 'What would you like to display in your statusline?',\n choices: [\n { name: 'š Working Directory', value: 'directory', checked: true },\n { name: 'šæ Git Branch', value: 'git', checked: true },\n { name: 'š¤ Model Name & Version', value: 'model', checked: true },\n { name: 'šµ Usage & Cost', value: 'usage', checked: true },\n { name: 'ā Session Time Remaining', value: 'session', checked: true },\n { name: 'š Token Statistics', value: 'tokens', checked: false },\n { name: 'ā” Burn Rate (tokens/min)', value: 'burnrate', checked: false }\n ],\n validate: (answer: string[]) => {\n if (answer.length < 1) {\n return 'You must choose at least one feature.'\n }\n return true\n }\n },\n {\n type: 'confirm',\n name: 'colors',\n message: 'Enable colors and emojis?',\n default: true\n }\n ])\n\n // Set intelligent defaults\n return {\n features: config.features,\n runtime: 'bash',\n colors: config.colors,\n theme: 'detailed',\n ccusageIntegration: true, // Always enabled since npx works\n logging: false,\n customEmojis: false\n } as StatuslineConfig\n}\n\nexport function displayConfigSummary(config: StatuslineConfig): void {\n console.log('\\nā
Configuration Summary:')\n console.log(` Runtime: ${config.runtime}`)\n console.log(` Theme: ${config.theme}`)\n console.log(` Colors: ${config.colors ? 'ā
' : 'ā'}`)\n console.log(` Features: ${config.features.join(', ')}`)\n \n if (config.ccusageIntegration) {\n console.log(' š ccusage integration enabled')\n }\n \n if (config.logging) {\n console.log(' š Debug logging enabled')\n }\n \n console.log('')\n}","import { StatuslineConfig } from '../cli/prompts.js'\nimport { generateColorBashCode, generateBasicColors } from '../features/colors.js'\nimport { generateGitBashCode, generateGitDisplayCode, generateGitUtilities } from '../features/git.js'\nimport { generateUsageBashCode, generateUsageDisplayCode, generateUsageUtilities } from '../features/usage.js'\n\nexport function generateBashStatusline(config: StatuslineConfig): string {\n const hasGit = config.features.includes('git')\n const hasUsage = config.features.some(f => ['usage', 'session', 'tokens', 'burnrate'].includes(f))\n const hasDirectory = config.features.includes('directory')\n const hasModel = config.features.includes('model')\n\n // Build usage feature config\n const usageConfig = {\n enabled: hasUsage && config.ccusageIntegration,\n showCost: config.features.includes('usage'),\n showTokens: config.features.includes('tokens'),\n showBurnRate: config.features.includes('burnrate'),\n showSession: config.features.includes('session'),\n showProgressBar: config.theme !== 'minimal' && config.features.includes('session')\n }\n\n // Build git feature config\n const gitConfig = {\n enabled: hasGit,\n showBranch: hasGit,\n showChanges: false, // Removed delta changes per user request\n compactMode: config.theme === 'compact'\n }\n\n const timestamp = new Date().toISOString()\n const script = `#!/bin/bash\n# Generated by cc-statusline (https://www.npmjs.com/package/@chongdashu/cc-statusline)\n# Custom Claude Code statusline - Created: ${timestamp}\n# Theme: ${config.theme} | Colors: ${config.colors} | Features: ${config.features.join(', ')}\n\n${config.logging ? generateLoggingCode() : ''}\ninput=$(cat)\n${generateColorBashCode({ enabled: config.colors, theme: config.theme })}\n${config.colors ? generateBasicColors() : ''}\n${hasUsage ? generateUsageUtilities() : ''}\n${hasGit ? generateGitUtilities() : ''}\n${generateBasicDataExtraction(hasDirectory, hasModel)}\n${hasGit ? generateGitBashCode(gitConfig, config.colors) : ''}\n${hasUsage ? generateUsageBashCode(usageConfig, config.colors) : ''}\n${config.logging ? generateLoggingOutput() : ''}\n${generateDisplaySection(config, gitConfig, usageConfig)}\n`\n\n return script.replace(/\\n\\n\\n+/g, '\\n\\n').trim() + '\\n'\n}\n\nfunction generateLoggingCode(): string {\n return `\nLOG_FILE=\"\\${HOME}/.claude/statusline.log\"\nTIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')\n\n# ---- logging ----\n{\n echo \"[$TIMESTAMP] Status line triggered with input:\"\n (echo \"$input\" | jq . 2>/dev/null) || echo \"$input\"\n echo \"---\"\n} >> \"$LOG_FILE\" 2>/dev/null\n`\n}\n\nfunction generateBasicDataExtraction(hasDirectory: boolean, hasModel: boolean): string {\n return `\n# ---- basics ----\nif command -v jq >/dev/null 2>&1; then${hasDirectory ? `\n current_dir=$(echo \"$input\" | jq -r '.workspace.current_dir // .cwd // \"unknown\"' 2>/dev/null | sed \"s|^$HOME|~|g\")` : ''}${hasModel ? `\n model_name=$(echo \"$input\" | jq -r '.model.display_name // \"Claude\"' 2>/dev/null)\n model_version=$(echo \"$input\" | jq -r '.model.version // \"\"' 2>/dev/null)` : ''}\nelse${hasDirectory ? `\n current_dir=\"unknown\"` : ''}${hasModel ? `\n model_name=\"Claude\"; model_version=\"\"` : ''}\nfi\n`\n}\n\nfunction generateLoggingOutput(): string {\n return `\n# ---- log extracted data ----\n{\n echo \"[\\$TIMESTAMP] Extracted: dir=\\${current_dir:-}, model=\\${model_name:-}, version=\\${model_version:-}, git=\\${git_branch:-}, cost=\\${cost_usd:-}, cost_ph=\\${cost_per_hour:-}, tokens=\\${tot_tokens:-}, tpm=\\${tpm:-}, session_pct=\\${session_pct:-}\"\n} >> \"$LOG_FILE\" 2>/dev/null\n`\n}\n\nfunction generateDisplaySection(config: StatuslineConfig, gitConfig: any, usageConfig: any): string {\n const emojis = config.colors && !config.customEmojis\n\n let displayCode = `\n# ---- render statusline ----`\n\n // Directory\n if (config.features.includes('directory')) {\n const dirEmoji = emojis ? 'š' : 'dir:'\n displayCode += `\nprintf '${dirEmoji} %s%s%s' \"$(dir_color)\" \"$current_dir\" \"$(rst)\"`\n }\n\n // Git\n displayCode += generateGitDisplayCode(gitConfig, config.colors, emojis)\n\n // Model\n if (config.features.includes('model')) {\n const modelEmoji = emojis ? 'š¤' : 'model:'\n displayCode += `\nprintf ' ${modelEmoji} %s%s%s' \"$(model_color)\" \"$model_name\" \"$(rst)\"\nif [ -n \"$model_version\" ] && [ \"$model_version\" != \"null\" ]; then\n printf ' š·ļø %s%s%s' \"$(version_color)\" \"$model_version\" \"$(rst)\"\nfi`\n }\n\n // Usage features\n displayCode += generateUsageDisplayCode(usageConfig, config.colors, emojis)\n\n return displayCode\n}","export interface ColorConfig {\n enabled: boolean\n theme: 'minimal' | 'detailed' | 'compact'\n}\n\nexport function generateColorBashCode(config: ColorConfig): string {\n if (!config.enabled) {\n return `\n# ---- color helpers (disabled) ----\nuse_color=0\nC() { :; }\nRST() { :; }\n`\n }\n\n return `\n# ---- color helpers (TTY-aware, respect NO_COLOR) ----\nuse_color=1\n[ -t 1 ] || use_color=0\n[ -n \"$NO_COLOR\" ] && use_color=0\n\nC() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[%sm' \"$1\"; fi; }\nRST() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[0m'; fi; }\n`\n}\n\nexport function generateBasicColors(): string {\n return `\n# ---- basic colors ----\ndir_color() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[1;36m'; fi; } # cyan\nmodel_color() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[1;35m'; fi; } # magenta \nversion_color() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[1;33m'; fi; } # yellow\nrst() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[0m'; fi; }\n`\n}\n\nexport const COLOR_CODES = {\n // Basic colors\n BLACK: '30',\n RED: '31', \n GREEN: '32',\n YELLOW: '33',\n BLUE: '34',\n MAGENTA: '35',\n CYAN: '36',\n WHITE: '37',\n \n // Bright colors (bold)\n BRIGHT_BLACK: '1;30',\n BRIGHT_RED: '1;31',\n BRIGHT_GREEN: '1;32', \n BRIGHT_YELLOW: '1;33',\n BRIGHT_BLUE: '1;34',\n BRIGHT_MAGENTA: '1;35',\n BRIGHT_CYAN: '1;36',\n BRIGHT_WHITE: '1;37',\n \n // Reset\n RESET: '0'\n} as const\n\nexport function getThemeColors(theme: 'minimal' | 'detailed' | 'compact') {\n switch (theme) {\n case 'minimal':\n return {\n directory: COLOR_CODES.CYAN,\n git: COLOR_CODES.GREEN,\n model: COLOR_CODES.MAGENTA,\n usage: COLOR_CODES.YELLOW,\n session: COLOR_CODES.BLUE\n }\n case 'detailed':\n return {\n directory: COLOR_CODES.BRIGHT_CYAN,\n git: COLOR_CODES.BRIGHT_GREEN,\n model: COLOR_CODES.BRIGHT_MAGENTA,\n usage: COLOR_CODES.BRIGHT_YELLOW,\n session: COLOR_CODES.BRIGHT_BLUE\n }\n case 'compact':\n return {\n directory: COLOR_CODES.CYAN,\n git: COLOR_CODES.GREEN,\n model: COLOR_CODES.BLUE,\n usage: COLOR_CODES.YELLOW,\n session: COLOR_CODES.RED\n }\n }\n}","export interface GitFeature {\n enabled: boolean\n showBranch: boolean\n showChanges: boolean\n compactMode: boolean\n}\n\nexport function generateGitBashCode(config: GitFeature, colors: boolean): string {\n if (!config.enabled) return ''\n\n const colorCode = colors ? `\n# ---- git colors ----\ngit_color() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[1;32m'; fi; }\nrst() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[0m'; fi; }\n` : `\ngit_color() { :; }\nrst() { :; }\n`\n\n return `${colorCode}\n# ---- git ----\ngit_branch=\"\"\nif git rev-parse --git-dir >/dev/null 2>&1; then\n git_branch=$(git branch --show-current 2>/dev/null || git rev-parse --short HEAD 2>/dev/null)\nfi`\n}\n\nexport function generateGitDisplayCode(config: GitFeature, colors: boolean, emojis: boolean): string {\n if (!config.enabled) return ''\n\n const branchEmoji = emojis ? 'šæ' : 'git:'\n\n let displayCode = `\n# git display\nif [ -n \"$git_branch\" ]; then\n printf ' ${branchEmoji} %s%s%s' \"$(git_color)\" \"$git_branch\" \"$(rst)\"\nfi`\n\n return displayCode\n}\n\nexport function generateGitUtilities(): string {\n return `\n# git utilities\nnum_or_zero() { v=\"$1\"; [[ \"$v\" =~ ^[0-9]+$ ]] && echo \"$v\" || echo 0; }`\n}","export interface UsageFeature {\n enabled: boolean\n showCost: boolean\n showTokens: boolean\n showBurnRate: boolean\n showSession: boolean\n showProgressBar: boolean\n}\n\nexport function generateUsageBashCode(config: UsageFeature, colors: boolean): string {\n if (!config.enabled) return ''\n\n const colorCode = colors ? `\n# ---- usage colors ----\nusage_color() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[1;35m'; fi; }\ncost_color() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[1;36m'; fi; }\nsession_color() { \n rem_pct=$(( 100 - session_pct ))\n if (( rem_pct <= 10 )); then SCLR='1;31'\n elif (( rem_pct <= 25 )); then SCLR='1;33'\n else SCLR='1;32'; fi\n if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[%sm' \"$SCLR\"; fi\n}\n` : `\nusage_color() { :; }\ncost_color() { :; }\nsession_color() { :; }\n`\n\n return `${colorCode}\n# ---- ccusage integration ----\nsession_txt=\"\"; session_pct=0; session_bar=\"\"\ncost_usd=\"\"; cost_per_hour=\"\"; tpm=\"\"; tot_tokens=\"\"\n\nif command -v jq >/dev/null 2>&1; then\n blocks_output=$(npx ccusage@latest blocks --json 2>/dev/null || ccusage blocks --json 2>/dev/null)\n if [ -n \"$blocks_output\" ]; then\n active_block=$(echo \"$blocks_output\" | jq -c '.blocks[] | select(.isActive == true)' 2>/dev/null | head -n1)\n if [ -n \"$active_block\" ]; then${config.showCost ? `\n cost_usd=$(echo \"$active_block\" | jq -r '.costUSD // empty')\n cost_per_hour=$(echo \"$active_block\" | jq -r '.burnRate.costPerHour // empty')` : ''}${config.showTokens ? `\n tot_tokens=$(echo \"$active_block\" | jq -r '.totalTokens // empty')` : ''}${config.showBurnRate ? `\n tpm=$(echo \"$active_block\" | jq -r '.burnRate.tokensPerMinute // empty')` : ''}${config.showSession || config.showProgressBar ? `\n \n # Session time calculation\n reset_time_str=$(echo \"$active_block\" | jq -r '.usageLimitResetTime // .endTime // empty')\n start_time_str=$(echo \"$active_block\" | jq -r '.startTime // empty')\n \n if [ -n \"$reset_time_str\" ] && [ -n \"$start_time_str\" ]; then\n start_sec=$(to_epoch \"$start_time_str\"); end_sec=$(to_epoch \"$reset_time_str\"); now_sec=$(date +%s)\n total=$(( end_sec - start_sec )); (( total<1 )) && total=1\n elapsed=$(( now_sec - start_sec )); (( elapsed<0 ))&&elapsed=0; (( elapsed>total ))&&elapsed=$total\n session_pct=$(( elapsed * 100 / total ))\n remaining=$(( end_sec - now_sec )); (( remaining<0 )) && remaining=0\n rh=$(( remaining / 3600 )); rm=$(( (remaining % 3600) / 60 ))\n end_hm=$(fmt_time_hm \"$end_sec\")${config.showSession ? `\n session_txt=\"$(printf '%dh %dm until reset at %s (%d%%)' \"$rh\" \"$rm\" \"$end_hm\" \"$session_pct\")\"` : ''}${config.showProgressBar ? `\n session_bar=$(progress_bar \"$session_pct\" 10)` : ''}\n fi` : ''}\n fi\n fi\nfi`\n}\n\nexport function generateUsageUtilities(): string {\n return `\n# ---- time helpers ----\nto_epoch() {\n ts=\"$1\"\n if command -v gdate >/dev/null 2>&1; then gdate -d \"$ts\" +%s 2>/dev/null && return; fi\n date -u -j -f \"%Y-%m-%dT%H:%M:%S%z\" \"\\${ts/Z/+0000}\" +%s 2>/dev/null && return\n python3 - \"$ts\" <<'PY' 2>/dev/null\nimport sys, datetime\ns=sys.argv[1].replace('Z','+00:00')\nprint(int(datetime.datetime.fromisoformat(s).timestamp()))\nPY\n}\n\nfmt_time_hm() {\n epoch=\"$1\"\n if date -r 0 +%s >/dev/null 2>&1; then date -r \"$epoch\" +\"%H:%M\"; else date -d \"@$epoch\" +\"%H:%M\"; fi\n}\n\nprogress_bar() {\n pct=\"\\${1:-0}\"; width=\"\\${2:-10}\"\n [[ \"$pct\" =~ ^[0-9]+$ ]] || pct=0; ((pct<0))&&pct=0; ((pct>100))&&pct=100\n filled=$(( pct * width / 100 )); empty=$(( width - filled ))\n printf '%*s' \"$filled\" '' | tr ' ' '='\n printf '%*s' \"$empty\" '' | tr ' ' '-'\n}`\n}\n\nexport function generateUsageDisplayCode(config: UsageFeature, colors: boolean, emojis: boolean): string {\n if (!config.enabled) return ''\n\n let displayCode = ''\n\n if (config.showSession) {\n const sessionEmoji = emojis ? 'ā' : 'session:'\n displayCode += `\n# session time\nif [ -n \"$session_txt\" ]; then\n printf ' ${sessionEmoji} %s%s%s' \"$(session_color)\" \"$session_txt\" \"$(rst)\"${config.showProgressBar ? `\n printf ' %s[%s]%s' \"$(session_color)\" \"$session_bar\" \"$(rst)\"` : ''}\nfi`\n }\n\n if (config.showCost) {\n const costEmoji = emojis ? 'šµ' : '$'\n displayCode += `\n# cost\nif [ -n \"$cost_usd\" ] && [[ \"$cost_usd\" =~ ^[0-9.]+$ ]]; then\n if [ -n \"$cost_per_hour\" ] && [[ \"$cost_per_hour\" =~ ^[0-9.]+$ ]]; then\n printf ' ${costEmoji} %s$%.2f ($%.2f/h)%s' \"$(cost_color)\" \"$cost_usd\" \"$cost_per_hour\" \"$(rst)\"\n else\n printf ' ${costEmoji} %s$%.2f%s' \"$(cost_color)\" \"$cost_usd\" \"$(rst)\"\n fi\nfi`\n }\n\n if (config.showTokens) {\n const tokenEmoji = emojis ? 'š' : 'tok:'\n displayCode += `\n# tokens\nif [ -n \"$tot_tokens\" ] && [[ \"$tot_tokens\" =~ ^[0-9]+$ ]]; then\n if [ -n \"$tpm\" ] && [[ \"$tpm\" =~ ^[0-9.]+$ ]] && ${config.showBurnRate ? 'true' : 'false'}; then\n printf ' ${tokenEmoji} %s%s tok (%.0f tpm)%s' \"$(usage_color)\" \"$tot_tokens\" \"$tpm\" \"$(rst)\"\n else\n printf ' ${tokenEmoji} %s%s tok%s' \"$(usage_color)\" \"$tot_tokens\" \"$(rst)\"\n fi\nfi`\n }\n\n return displayCode\n}","import { StatuslineConfig } from '../cli/prompts.js'\n\nexport interface ValidationResult {\n isValid: boolean\n errors: string[]\n warnings: string[]\n}\n\nexport function validateConfig(config: StatuslineConfig): ValidationResult {\n const errors: string[] = []\n const warnings: string[] = []\n\n // Validate features\n if (!config.features || config.features.length === 0) {\n errors.push('At least one display feature must be selected')\n }\n\n // Validate runtime\n if (!['bash', 'python', 'node'].includes(config.runtime)) {\n errors.push(`Invalid runtime: ${config.runtime}`)\n }\n\n // Validate theme\n if (!['minimal', 'detailed', 'compact'].includes(config.theme)) {\n errors.push(`Invalid theme: ${config.theme}`)\n }\n\n // Check for usage features without ccusage integration\n const usageFeatures = ['usage', 'session', 'tokens', 'burnrate']\n const hasUsageFeatures = config.features.some(f => usageFeatures.includes(f))\n \n if (hasUsageFeatures && !config.ccusageIntegration) {\n warnings.push('Usage features selected but ccusage integration is disabled. Some features may not work properly.')\n }\n\n // Warn about performance with many features\n if (config.features.length > 5) {\n warnings.push('Many features selected. This may impact statusline performance.')\n }\n\n // Validate color/emoji consistency\n if (config.customEmojis && !config.colors) {\n warnings.push('Custom emojis enabled but colors disabled. Visual distinction may be limited.')\n }\n\n return {\n isValid: errors.length === 0,\n errors,\n warnings\n }\n}\n\nexport function validateDependencies(): {\n jq: boolean\n git: boolean\n ccusage: boolean\n python?: boolean\n node?: boolean\n} {\n // This would check system dependencies\n // For now, return placeholder\n return {\n jq: true, // Would check: command -v jq >/dev/null 2>&1\n git: true, // Would check: command -v git >/dev/null 2>&1\n ccusage: false, // Would check: command -v ccusage >/dev/null 2>&1\n python: true, // Would check: command -v python3 >/dev/null 2>&1\n node: true // Would check: command -v node >/dev/null 2>&1\n }\n}","import { StatuslineConfig } from '../cli/prompts.js'\nimport { promises as fs } from 'fs'\nimport path from 'path'\n\nexport async function installStatusline(\n script: string,\n outputPath: string,\n config: StatuslineConfig\n): Promise<void> {\n try {\n // Ensure the directory exists\n const dir = path.dirname(outputPath)\n await fs.mkdir(dir, { recursive: true })\n\n // Write the script\n await fs.writeFile(outputPath, script, { mode: 0o755 })\n\n // Update .claude/settings.json if it exists\n await updateSettingsJson(dir, path.basename(outputPath))\n\n // Note: statusline-config.json removed per user feedback - not needed\n // The statusline script contains all necessary configuration info\n\n } catch (error) {\n throw new Error(`Failed to install statusline: ${error instanceof Error ? error.message : String(error)}`)\n }\n}\n\nasync function updateSettingsJson(claudeDir: string, scriptName: string): Promise<void> {\n const settingsPath = path.join(claudeDir, 'settings.json')\n \n try {\n let settings: any = {}\n \n // Try to read existing settings\n try {\n const settingsContent = await fs.readFile(settingsPath, 'utf-8')\n settings = JSON.parse(settingsContent)\n } catch {\n // File doesn't exist or invalid JSON, start fresh\n }\n\n // Update statusLine configuration\n settings.statusLine = {\n type: 'command',\n command: `.claude/${scriptName}`,\n padding: 0\n }\n\n // Write updated settings\n await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2))\n \n } catch (error) {\n // Settings update failed, but don't fail the entire installation\n console.warn(`Warning: Could not update settings.json: ${error instanceof Error ? error.message : String(error)}`)\n throw new Error('SETTINGS_UPDATE_FAILED') // Signal that manual config is needed\n }\n}\n\nexport async function checkClaudeCodeSetup(): Promise<{\n hasClaudeDir: boolean\n hasSettings: boolean\n currentStatusline?: string\n}> {\n const claudeDir = './.claude'\n const settingsPath = path.join(claudeDir, 'settings.json')\n \n try {\n const dirExists = await fs.access(claudeDir).then(() => true).catch(() => false)\n const settingsExists = await fs.access(settingsPath).then(() => true).catch(() => false)\n \n let currentStatusline: string | undefined\n \n if (settingsExists) {\n try {\n const settings = JSON.parse(await fs.readFile(settingsPath, 'utf-8'))\n currentStatusline = settings.statusLine?.command\n } catch {\n // Ignore JSON parse errors\n }\n }\n \n return {\n hasClaudeDir: dirExists,\n hasSettings: settingsExists,\n currentStatusline\n }\n } catch {\n return {\n hasClaudeDir: false,\n hasSettings: false\n }\n }\n}"],"mappings":"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","names":["fs","path","process","fs","chalk","ora","path","path","testStatuslineScript","generateMockClaudeInput","chalk","previewCommand"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@chongdashu/cc-statusline",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Interactive CLI tool for generating custom Claude Code statuslines",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"cc-statusline": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsup",
|
|
12
|
+
"dev": "tsup --watch",
|
|
13
|
+
"start": "node dist/index.js",
|
|
14
|
+
"test": "npm run build && node dist/test.js",
|
|
15
|
+
"prepublishOnly": "npm run build"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"claude-code",
|
|
19
|
+
"claude",
|
|
20
|
+
"statusline",
|
|
21
|
+
"status-line",
|
|
22
|
+
"cli",
|
|
23
|
+
"terminal",
|
|
24
|
+
"productivity",
|
|
25
|
+
"development",
|
|
26
|
+
"anthropic",
|
|
27
|
+
"ai-tools"
|
|
28
|
+
],
|
|
29
|
+
"author": {
|
|
30
|
+
"name": "Chong-U",
|
|
31
|
+
"email": "chong-u@aioriented.dev",
|
|
32
|
+
"url": "https://github.com/chongdashu"
|
|
33
|
+
},
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"commander": "^11.1.0",
|
|
37
|
+
"inquirer": "^9.2.12",
|
|
38
|
+
"chalk": "^5.3.0",
|
|
39
|
+
"ora": "^7.0.1"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/inquirer": "^9.0.7",
|
|
43
|
+
"@types/node": "^20.10.5",
|
|
44
|
+
"tsup": "^8.0.1",
|
|
45
|
+
"typescript": "^5.3.3"
|
|
46
|
+
},
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=16.0.0"
|
|
49
|
+
},
|
|
50
|
+
"repository": {
|
|
51
|
+
"type": "git",
|
|
52
|
+
"url": "git+https://github.com/chongdashu/cc-statusline.git"
|
|
53
|
+
},
|
|
54
|
+
"bugs": {
|
|
55
|
+
"url": "https://github.com/chongdashu/cc-statusline/issues"
|
|
56
|
+
},
|
|
57
|
+
"homepage": "https://github.com/chongdashu/cc-statusline#readme"
|
|
58
|
+
}
|