cc-statusline-pro 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/cli.js ADDED
@@ -0,0 +1,476 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ const command = process.argv[2];
7
+
8
+ if (command === 'init') {
9
+ initStatusline();
10
+ } else {
11
+ showHelp();
12
+ }
13
+
14
+ function showHelp() {
15
+ console.log(`
16
+ Claude Code Statusline Installer
17
+ =================================
18
+
19
+ Usage:
20
+ npx claude-code-statusline init
21
+
22
+ Commands:
23
+ init Install statusline to current project
24
+
25
+ Features:
26
+ - Beautiful colors and progress bars
27
+ - Real-time usage limits from Anthropic API
28
+ - Context window tracking
29
+ - Session time and cost tracking
30
+ - Code changes (lines added/removed)
31
+ - Git branch display
32
+
33
+ Example:
34
+ cd your-project
35
+ npx claude-code-statusline init
36
+ `);
37
+ }
38
+
39
+ function initStatusline() {
40
+ const cwd = process.cwd();
41
+ const claudeDir = path.join(cwd, '.claude');
42
+ const statuslineFile = path.join(claudeDir, 'statusline.js');
43
+ const settingsFile = path.join(claudeDir, 'settings.json');
44
+
45
+ console.log('');
46
+ console.log('🚀 Installing Claude Code Statusline...');
47
+ console.log('');
48
+
49
+ // Step 1: Create .claude directory if not exists
50
+ if (!fs.existsSync(claudeDir)) {
51
+ console.log('📁 Creating .claude directory...');
52
+ fs.mkdirSync(claudeDir, { recursive: true });
53
+ console.log(' ✓ Created .claude/');
54
+ } else {
55
+ console.log('📁 .claude directory already exists');
56
+ }
57
+
58
+ // Step 2: Copy statusline.js
59
+ console.log('');
60
+ console.log('📝 Installing statusline.js...');
61
+
62
+ const statuslineContent = getStatuslineTemplate();
63
+
64
+ if (fs.existsSync(statuslineFile)) {
65
+ console.log(' ⚠️ statusline.js already exists');
66
+ console.log(' 💾 Creating backup: statusline.js.backup');
67
+ fs.copyFileSync(statuslineFile, statuslineFile + '.backup');
68
+ }
69
+
70
+ fs.writeFileSync(statuslineFile, statuslineContent);
71
+
72
+ // Make executable on Unix systems
73
+ try {
74
+ fs.chmodSync(statuslineFile, '755');
75
+ } catch (e) {
76
+ // Windows doesn't need chmod
77
+ }
78
+
79
+ console.log(' ✓ Installed .claude/statusline.js');
80
+
81
+ // Step 3: Update settings.json
82
+ console.log('');
83
+ console.log('⚙️ Updating settings.json...');
84
+
85
+ let settings = {};
86
+
87
+ if (fs.existsSync(settingsFile)) {
88
+ try {
89
+ const content = fs.readFileSync(settingsFile, 'utf8');
90
+ settings = JSON.parse(content);
91
+ console.log(' ✓ Found existing settings.json');
92
+ } catch (e) {
93
+ console.log(' ⚠️ Could not parse settings.json, creating new one');
94
+ }
95
+ }
96
+
97
+ // Add or update statusLine config with absolute path
98
+ const absoluteStatuslinePath = path.resolve(statuslineFile);
99
+ const command = process.platform === 'win32'
100
+ ? `node "${absoluteStatuslinePath}"`
101
+ : absoluteStatuslinePath;
102
+
103
+ settings.statusLine = {
104
+ type: 'command',
105
+ command: command,
106
+ padding: 0
107
+ };
108
+
109
+ fs.writeFileSync(settingsFile, JSON.stringify(settings, null, 2));
110
+ console.log(' ✓ Updated .claude/settings.json');
111
+ console.log(` ✓ Command: ${command}`);
112
+
113
+ // Step 4: Success message
114
+ console.log('');
115
+ console.log('✅ Installation complete!');
116
+ console.log('');
117
+ console.log('Your statusline will display:');
118
+ console.log(' • Model name (bold cyan)');
119
+ console.log(' • Context usage with progress bar (green/yellow/red)');
120
+ console.log(' • Session time (blue)');
121
+ console.log(' • Plan usage limits from Anthropic API (green/yellow/red)');
122
+ console.log(' • Cost tracking (yellow)');
123
+ console.log(' • Code changes - lines added/removed (green/red)');
124
+ console.log(' • Git branch (cyan)');
125
+ console.log('');
126
+ console.log('The statusline will automatically appear in Claude Code!');
127
+ console.log('');
128
+ }
129
+
130
+ function getStatuslineTemplate() {
131
+ // Read from the template file in the same directory
132
+ const templatePath = path.join(__dirname, 'statusline-template.js');
133
+
134
+ if (fs.existsSync(templatePath)) {
135
+ return fs.readFileSync(templatePath, 'utf8');
136
+ }
137
+
138
+ // Fallback: return inline template
139
+ return `#!/usr/bin/env node
140
+
141
+ const { execSync } = require('child_process');
142
+ const fs = require('fs');
143
+ const path = require('path');
144
+ const https = require('https');
145
+ const os = require('os');
146
+
147
+ // ANSI Color Codes
148
+ const colors = {
149
+ reset: '\\x1b[0m',
150
+ bold: '\\x1b[1m',
151
+
152
+ // Regular colors
153
+ red: '\\x1b[31m',
154
+ green: '\\x1b[32m',
155
+ yellow: '\\x1b[33m',
156
+ blue: '\\x1b[34m',
157
+ magenta: '\\x1b[35m',
158
+ cyan: '\\x1b[36m',
159
+
160
+ // Bright colors
161
+ brightRed: '\\x1b[91m',
162
+ brightGreen: '\\x1b[92m',
163
+ brightYellow: '\\x1b[93m',
164
+ brightCyan: '\\x1b[96m',
165
+
166
+ // Combined styles
167
+ boldCyan: '\\x1b[1;36m',
168
+ };
169
+
170
+ // Cache for usage limits (5 minutes)
171
+ const CACHE_DURATION = 5 * 60 * 1000;
172
+ let usageLimitsCache = {
173
+ data: null,
174
+ timestamp: 0
175
+ };
176
+
177
+ // Read input JSON from stdin
178
+ let inputData = '';
179
+ process.stdin.on('data', chunk => {
180
+ inputData += chunk;
181
+ });
182
+
183
+ process.stdin.on('end', async () => {
184
+ try {
185
+ const data = JSON.parse(inputData);
186
+ const statusLine = await buildStatusLine(data);
187
+ process.stdout.write(statusLine);
188
+ } catch (error) {
189
+ process.stdout.write('Claude Code');
190
+ }
191
+ });
192
+
193
+ async function buildStatusLine(data) {
194
+ const parts = [];
195
+
196
+ // 1. Model name
197
+ const modelName = getModelDisplayName(data);
198
+ parts.push(modelName);
199
+
200
+ // 2. Context usage
201
+ const contextInfo = getContextUsage(data);
202
+ if (contextInfo) parts.push(contextInfo);
203
+
204
+ // 3. Session time
205
+ const sessionTime = getSessionTime(data);
206
+ if (sessionTime) parts.push(sessionTime);
207
+
208
+ // 4. Usage limits
209
+ const usageLimits = await getUsageLimits();
210
+ if (usageLimits) parts.push(usageLimits);
211
+
212
+ // 5. Cost
213
+ const costInfo = getCostInfo(data);
214
+ if (costInfo) parts.push(costInfo);
215
+
216
+ // 6. Code changes
217
+ const codeChanges = getCodeChanges(data);
218
+ if (codeChanges) parts.push(codeChanges);
219
+
220
+ // 7. Git branch
221
+ const gitBranch = getGitBranch(data);
222
+ if (gitBranch) parts.push(gitBranch);
223
+
224
+ return parts.join(' | ');
225
+ }
226
+
227
+ function getModelDisplayName(data) {
228
+ const name = data.model?.display_name || 'Claude';
229
+ return \`\${colors.boldCyan}\${name}\${colors.reset}\`;
230
+ }
231
+
232
+ function getContextUsage(data) {
233
+ if (!data.context_window?.current_usage) return null;
234
+
235
+ const usage = data.context_window.current_usage;
236
+ const windowSize = data.context_window.context_window_size;
237
+
238
+ const currentTokens = (usage.input_tokens || 0) +
239
+ (usage.cache_creation_input_tokens || 0) +
240
+ (usage.cache_read_input_tokens || 0);
241
+
242
+ const percentage = Math.round((currentTokens / windowSize) * 100);
243
+
244
+ const barColor = percentage < 40 ? colors.green :
245
+ percentage < 70 ? colors.yellow : colors.red;
246
+ const percentColor = barColor;
247
+
248
+ const barLength = 10;
249
+ const filled = Math.round((percentage / 100) * barLength);
250
+ const empty = barLength - filled;
251
+
252
+ const bar = \`\${barColor}\${'▓'.repeat(filled)}\${colors.reset}\${'░'.repeat(empty)}\`;
253
+
254
+ return \`\${bar} \${percentColor}\${percentage}%\${colors.reset}\`;
255
+ }
256
+
257
+ function getSessionTime(data) {
258
+ if (!data.session_id || !data.transcript_path) return null;
259
+
260
+ try {
261
+ const stats = fs.statSync(data.transcript_path);
262
+ const startTime = stats.birthtimeMs || stats.ctimeMs;
263
+ const elapsed = Math.floor((Date.now() - startTime) / 1000);
264
+
265
+ const hours = Math.floor(elapsed / 3600);
266
+ const minutes = Math.floor((elapsed % 3600) / 60);
267
+ const seconds = elapsed % 60;
268
+
269
+ let timeStr;
270
+ if (hours > 0) {
271
+ timeStr = \`⏱ \${hours}h \${minutes}m\`;
272
+ } else if (minutes > 0) {
273
+ timeStr = \`⏱ \${minutes}m \${seconds}s\`;
274
+ } else {
275
+ timeStr = \`⏱ \${seconds}s\`;
276
+ }
277
+
278
+ return \`\${colors.blue}\${timeStr}\${colors.reset}\`;
279
+ } catch (error) {
280
+ return null;
281
+ }
282
+ }
283
+
284
+ function getCostInfo(data) {
285
+ if (!data.cost?.total_cost_usd) return null;
286
+ const cost = data.cost.total_cost_usd.toFixed(4);
287
+ return \`\${colors.yellow}$\${cost}\${colors.reset}\`;
288
+ }
289
+
290
+ function getCodeChanges(data) {
291
+ if (!data.cost) return null;
292
+
293
+ const added = data.cost.total_lines_added || 0;
294
+ const removed = data.cost.total_lines_removed || 0;
295
+
296
+ if (added === 0 && removed === 0) return null;
297
+
298
+ const parts = [];
299
+ if (added > 0) parts.push(\`\${colors.green}+\${added}\${colors.reset}\`);
300
+ if (removed > 0) parts.push(\`\${colors.red}-\${removed}\${colors.reset}\`);
301
+
302
+ return parts.join(' ');
303
+ }
304
+
305
+ async function getUsageLimits() {
306
+ const now = Date.now();
307
+ if (usageLimitsCache.data && (now - usageLimitsCache.timestamp) < CACHE_DURATION) {
308
+ return formatUsageLimits(usageLimitsCache.data);
309
+ }
310
+
311
+ const usage = await fetchUsageLimitsFromAPI();
312
+
313
+ if (usage) {
314
+ usageLimitsCache.data = usage;
315
+ usageLimitsCache.timestamp = now;
316
+ return formatUsageLimits(usage);
317
+ }
318
+
319
+ return null;
320
+ }
321
+
322
+ function getAccessToken() {
323
+ const platform = os.platform();
324
+
325
+ try {
326
+ if (platform === 'darwin') {
327
+ const token = execSync(
328
+ 'security find-generic-password -s "Claude Code-credentials" -w',
329
+ { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }
330
+ ).trim();
331
+
332
+ const parsed = JSON.parse(token);
333
+ if (parsed.claudeAiOauth?.accessToken) {
334
+ return parsed.claudeAiOauth.accessToken;
335
+ }
336
+ return token;
337
+ } else if (platform === 'win32' || platform === 'linux') {
338
+ const appDataPaths = [
339
+ path.join(os.homedir(), '.claude', '.credentials.json'),
340
+ path.join(process.env.APPDATA || '', 'Claude Code', 'credentials'),
341
+ path.join(process.env.LOCALAPPDATA || '', 'Claude Code', 'credentials'),
342
+ path.join(os.homedir(), '.claude', 'credentials'),
343
+ ];
344
+
345
+ for (const credPath of appDataPaths) {
346
+ if (fs.existsSync(credPath)) {
347
+ const content = fs.readFileSync(credPath, 'utf8');
348
+ try {
349
+ const parsed = JSON.parse(content);
350
+ if (parsed.claudeAiOauth?.accessToken) {
351
+ return parsed.claudeAiOauth.accessToken;
352
+ }
353
+ if (parsed.access_token || parsed.token) {
354
+ return parsed.access_token || parsed.token;
355
+ }
356
+ } catch (e) {
357
+ return content.trim();
358
+ }
359
+ }
360
+ }
361
+ }
362
+ } catch (error) {
363
+ return null;
364
+ }
365
+
366
+ return null;
367
+ }
368
+
369
+ async function fetchUsageLimitsFromAPI() {
370
+ return new Promise((resolve) => {
371
+ const accessToken = getAccessToken();
372
+ if (!accessToken) {
373
+ resolve(null);
374
+ return;
375
+ }
376
+
377
+ const options = {
378
+ hostname: 'api.anthropic.com',
379
+ path: '/api/oauth/usage',
380
+ method: 'GET',
381
+ headers: {
382
+ 'Authorization': \`Bearer \${accessToken}\`,
383
+ 'anthropic-beta': 'oauth-2025-04-20',
384
+ 'User-Agent': 'claude-code/2.0.76'
385
+ },
386
+ timeout: 2000
387
+ };
388
+
389
+ const req = https.request(options, (res) => {
390
+ let data = '';
391
+ res.on('data', (chunk) => { data += chunk; });
392
+ res.on('end', () => {
393
+ try {
394
+ if (res.statusCode === 200) {
395
+ resolve(JSON.parse(data));
396
+ } else {
397
+ resolve(null);
398
+ }
399
+ } catch (error) {
400
+ resolve(null);
401
+ }
402
+ });
403
+ });
404
+
405
+ req.on('error', () => resolve(null));
406
+ req.on('timeout', () => { req.destroy(); resolve(null); });
407
+ req.end();
408
+ });
409
+ }
410
+
411
+ function formatUsageLimits(usage) {
412
+ if (!usage?.five_hour) return null;
413
+
414
+ const utilization = usage.five_hour.utilization;
415
+ const resetsAt = usage.five_hour.resets_at;
416
+
417
+ if (utilization === undefined || !resetsAt) return null;
418
+
419
+ const usageColor = utilization < 50 ? colors.green :
420
+ utilization < 80 ? colors.yellow : colors.red;
421
+
422
+ const parts = [];
423
+ parts.push(\`\${usageColor}Plan: \${Math.round(utilization)}% used\${colors.reset}\`);
424
+
425
+ const resetTime = new Date(resetsAt);
426
+ const diff = resetTime - new Date();
427
+
428
+ if (diff > 0) {
429
+ const hours = Math.floor(diff / (1000 * 60 * 60));
430
+ const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
431
+
432
+ let resetStr;
433
+ if (hours > 0) {
434
+ resetStr = \`Resets in \${hours}h \${minutes}m\`;
435
+ } else if (minutes > 0) {
436
+ resetStr = \`Resets in \${minutes}m\`;
437
+ } else {
438
+ resetStr = \`Resets soon\`;
439
+ }
440
+
441
+ parts.push(\`\${colors.magenta}\${resetStr}\${colors.reset}\`);
442
+ }
443
+
444
+ return parts.join(' | ');
445
+ }
446
+
447
+ function getGitBranch(data) {
448
+ try {
449
+ const cwd = data.workspace?.project_dir || data.workspace?.current_dir || process.cwd();
450
+ const gitDir = path.join(cwd, '.git');
451
+
452
+ if (!fs.existsSync(gitDir)) return null;
453
+
454
+ const branch = execSync('git rev-parse --abbrev-ref HEAD', {
455
+ cwd: cwd,
456
+ encoding: 'utf8',
457
+ stdio: ['pipe', 'pipe', 'ignore'],
458
+ env: { ...process.env, GIT_OPTIONAL_LOCKS: '0' }
459
+ }).trim();
460
+
461
+ if (branch && branch !== 'HEAD') {
462
+ return \`\${colors.cyan}⎇ \${branch}\${colors.reset}\`;
463
+ }
464
+
465
+ return null;
466
+ } catch (error) {
467
+ return null;
468
+ }
469
+ }
470
+ `;
471
+ }
472
+
473
+ // Run init if this is the main module
474
+ if (require.main === module) {
475
+ initStatusline();
476
+ }
package/install.ps1 ADDED
@@ -0,0 +1,92 @@
1
+ # PowerShell install script for Claude Code Statusline
2
+ # Usage: .\install.ps1
3
+
4
+ Write-Host ""
5
+ Write-Host "🚀 Installing Claude Code Statusline..." -ForegroundColor Cyan
6
+ Write-Host ""
7
+
8
+ # Check if .claude directory exists
9
+ if (-not (Test-Path ".claude")) {
10
+ Write-Host "📁 Creating .claude directory..." -ForegroundColor Yellow
11
+ New-Item -ItemType Directory -Path ".claude" | Out-Null
12
+ Write-Host " ✓ Created .claude/" -ForegroundColor Green
13
+ } else {
14
+ Write-Host "📁 .claude directory already exists" -ForegroundColor Yellow
15
+ }
16
+
17
+ # Backup existing statusline if it exists
18
+ if (Test-Path ".claude\statusline.js") {
19
+ Write-Host ""
20
+ Write-Host "⚠️ Backing up existing statusline.js..." -ForegroundColor Yellow
21
+ Copy-Item ".claude\statusline.js" ".claude\statusline.js.backup"
22
+ Write-Host " ✓ Backed up to .claude\statusline.js.backup" -ForegroundColor Green
23
+ }
24
+
25
+ # Copy statusline.js
26
+ Write-Host ""
27
+ Write-Host "📝 Installing statusline.js..." -ForegroundColor Yellow
28
+
29
+ $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
30
+ $templatePath = Join-Path $scriptDir "statusline-template.js"
31
+
32
+ if (Test-Path $templatePath) {
33
+ Copy-Item $templatePath ".claude\statusline.js"
34
+ Write-Host " ✓ Installed from local template" -ForegroundColor Green
35
+ } else {
36
+ Write-Host " ❌ Template not found. Please run from installer directory." -ForegroundColor Red
37
+ exit 1
38
+ }
39
+
40
+ # Update settings.json
41
+ Write-Host ""
42
+ Write-Host "⚙️ Updating settings.json..." -ForegroundColor Yellow
43
+
44
+ $settingsFile = ".claude\settings.json"
45
+
46
+ # Get absolute path for statusline.js
47
+ $absolutePath = Resolve-Path ".claude\statusline.js" | Select-Object -ExpandProperty Path
48
+ $command = "node `"$absolutePath`""
49
+
50
+ if (Test-Path $settingsFile) {
51
+ # Read and parse existing settings
52
+ $settings = Get-Content $settingsFile | ConvertFrom-Json
53
+
54
+ # Add or update statusLine
55
+ $settings | Add-Member -MemberType NoteProperty -Name "statusLine" -Value @{
56
+ type = "command"
57
+ command = $command
58
+ padding = 0
59
+ } -Force
60
+
61
+ # Write back to file
62
+ $settings | ConvertTo-Json -Depth 10 | Set-Content $settingsFile
63
+ Write-Host " ✓ Updated existing settings.json" -ForegroundColor Green
64
+ } else {
65
+ # Create new settings.json
66
+ @{
67
+ statusLine = @{
68
+ type = "command"
69
+ command = $command
70
+ padding = 0
71
+ }
72
+ } | ConvertTo-Json -Depth 10 | Set-Content $settingsFile
73
+ Write-Host " ✓ Created .claude\settings.json" -ForegroundColor Green
74
+ }
75
+
76
+ Write-Host " ✓ Command: $command" -ForegroundColor Green
77
+
78
+ # Success message
79
+ Write-Host ""
80
+ Write-Host "✅ Installation complete!" -ForegroundColor Green
81
+ Write-Host ""
82
+ Write-Host "Your statusline will display:" -ForegroundColor Cyan
83
+ Write-Host " • Model name (bold cyan)"
84
+ Write-Host " • Context usage with progress bar (green/yellow/red)"
85
+ Write-Host " • Session time (blue)"
86
+ Write-Host " • Plan usage limits from Anthropic API (green/yellow/red)"
87
+ Write-Host " • Cost tracking (yellow)"
88
+ Write-Host " • Code changes - lines added/removed (green/red)"
89
+ Write-Host " • Git branch (cyan)"
90
+ Write-Host ""
91
+ Write-Host "The statusline will automatically appear in Claude Code!" -ForegroundColor Green
92
+ Write-Host ""
package/install.sh ADDED
@@ -0,0 +1,106 @@
1
+ #!/bin/bash
2
+
3
+ # Simple install script for Claude Code Statusline
4
+ # Usage: curl -sSL https://your-url/install.sh | bash
5
+
6
+ set -e
7
+
8
+ echo ""
9
+ echo "🚀 Installing Claude Code Statusline..."
10
+ echo ""
11
+
12
+ # Check if .claude directory exists
13
+ if [ ! -d ".claude" ]; then
14
+ echo "📁 Creating .claude directory..."
15
+ mkdir -p .claude
16
+ echo " ✓ Created .claude/"
17
+ else
18
+ echo "📁 .claude directory already exists"
19
+ fi
20
+
21
+ # Backup existing statusline if it exists
22
+ if [ -f ".claude/statusline.js" ]; then
23
+ echo ""
24
+ echo "⚠️ Backing up existing statusline.js..."
25
+ cp .claude/statusline.js .claude/statusline.js.backup
26
+ echo " ✓ Backed up to .claude/statusline.js.backup"
27
+ fi
28
+
29
+ # Download or copy statusline.js
30
+ echo ""
31
+ echo "📝 Installing statusline.js..."
32
+
33
+ # If running from local directory
34
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
35
+ if [ -f "$SCRIPT_DIR/statusline-template.js" ]; then
36
+ cp "$SCRIPT_DIR/statusline-template.js" .claude/statusline.js
37
+ chmod +x .claude/statusline.js
38
+ echo " ✓ Installed from local template"
39
+ else
40
+ # Download from URL (if hosted)
41
+ # curl -sSL https://your-url/statusline.js -o .claude/statusline.js
42
+ echo " ❌ Template not found. Please run from installer directory."
43
+ exit 1
44
+ fi
45
+
46
+ # Update settings.json
47
+ echo ""
48
+ echo "⚙️ Updating settings.json..."
49
+
50
+ SETTINGS_FILE=".claude/settings.json"
51
+
52
+ # Get absolute path for statusline.js
53
+ ABSOLUTE_PATH="$(cd "$(dirname ".claude/statusline.js")" && pwd)/$(basename ".claude/statusline.js")"
54
+
55
+ # For macOS/Linux, use the absolute path directly (shebang will handle node)
56
+ # For better compatibility, prefix with node command
57
+ COMMAND="node \"$ABSOLUTE_PATH\""
58
+
59
+ if [ -f "$SETTINGS_FILE" ]; then
60
+ # Check if jq is available
61
+ if command -v jq &> /dev/null; then
62
+ # Use jq to update JSON
63
+ jq --arg cmd "$COMMAND" '.statusLine = {"type": "command", "command": $cmd, "padding": 0}' "$SETTINGS_FILE" > "$SETTINGS_FILE.tmp"
64
+ mv "$SETTINGS_FILE.tmp" "$SETTINGS_FILE"
65
+ echo " ✓ Updated existing settings.json"
66
+ else
67
+ echo " ⚠️ jq not found. Please manually add statusLine config to settings.json"
68
+ echo ""
69
+ echo " Add this to your .claude/settings.json:"
70
+ echo ' "statusLine": {'
71
+ echo ' "type": "command",'
72
+ echo " \"command\": \"$COMMAND\","
73
+ echo ' "padding": 0'
74
+ echo ' }'
75
+ fi
76
+ else
77
+ # Create new settings.json
78
+ cat > "$SETTINGS_FILE" << EOF
79
+ {
80
+ "statusLine": {
81
+ "type": "command",
82
+ "command": "$COMMAND",
83
+ "padding": 0
84
+ }
85
+ }
86
+ EOF
87
+ echo " ✓ Created .claude/settings.json"
88
+ fi
89
+
90
+ echo " ✓ Command: $COMMAND"
91
+
92
+ # Success message
93
+ echo ""
94
+ echo "✅ Installation complete!"
95
+ echo ""
96
+ echo "Your statusline will display:"
97
+ echo " • Model name (bold cyan)"
98
+ echo " • Context usage with progress bar (green/yellow/red)"
99
+ echo " • Session time (blue)"
100
+ echo " • Plan usage limits from Anthropic API (green/yellow/red)"
101
+ echo " • Cost tracking (yellow)"
102
+ echo " • Code changes - lines added/removed (green/red)"
103
+ echo " • Git branch (cyan)"
104
+ echo ""
105
+ echo "The statusline will automatically appear in Claude Code!"
106
+ echo ""
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "cc-statusline-pro",
3
+ "version": "1.0.0",
4
+ "description": "Beautiful statusline for Claude Code with usage limits, context tracking, and colors",
5
+ "main": "index.js",
6
+ "bin": {
7
+ "cc-statusline": "cli.js"
8
+ },
9
+ "scripts": {
10
+ "test": "node test.js"
11
+ },
12
+ "keywords": [
13
+ "claude-code",
14
+ "statusline",
15
+ "cli",
16
+ "anthropic"
17
+ ],
18
+ "author": "Your Name",
19
+ "license": "MIT",
20
+ "engines": {
21
+ "node": ">=14.0.0"
22
+ }
23
+ }