coder-config 0.49.2-beta → 0.49.4-beta
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/lib/constants.js +1 -1
- package/package.json +1 -1
- package/ui/dist/assets/index-C0-_yjwc.css +32 -0
- package/ui/dist/assets/{index-zVvhh8v5.js → index-DyuQWsYT.js} +67 -57
- package/ui/dist/index.html +2 -2
- package/ui/routes/statuslines.js +253 -125
- package/ui/dist/assets/index-C0x-uedt.css +0 -32
package/ui/dist/index.html
CHANGED
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
|
|
20
20
|
<!-- PWA Manifest -->
|
|
21
21
|
<link rel="manifest" href="/manifest.json">
|
|
22
|
-
<script type="module" crossorigin src="/assets/index-
|
|
23
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
22
|
+
<script type="module" crossorigin src="/assets/index-DyuQWsYT.js"></script>
|
|
23
|
+
<link rel="stylesheet" crossorigin href="/assets/index-C0-_yjwc.css">
|
|
24
24
|
</head>
|
|
25
25
|
<body>
|
|
26
26
|
<div id="root"></div>
|
package/ui/routes/statuslines.js
CHANGED
|
@@ -1,181 +1,309 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Statuslines Routes
|
|
2
|
+
* Statuslines Routes
|
|
3
|
+
*
|
|
4
|
+
* Claude Code's statusLine feature sends JSON session data to a script via stdin.
|
|
5
|
+
* Scripts parse it with jq and print text for the status bar.
|
|
6
|
+
*
|
|
7
|
+
* Settings format in ~/.claude/settings.json:
|
|
8
|
+
* { "statusLine": { "type": "command", "command": "~/.claude/statuslines/<id>.sh" } }
|
|
9
|
+
*
|
|
10
|
+
* JSON fields available: model.display_name, context_window.used_percentage,
|
|
11
|
+
* context_window.context_window_size, context_window.current_usage.*,
|
|
12
|
+
* cost.total_cost_usd, cost.total_duration_ms, cost.total_lines_added,
|
|
13
|
+
* cost.total_lines_removed, workspace.current_dir, session_id, version
|
|
3
14
|
*/
|
|
4
15
|
|
|
5
16
|
const fs = require('fs');
|
|
6
17
|
const path = require('path');
|
|
7
18
|
const os = require('os');
|
|
19
|
+
const { execSync } = require('child_process');
|
|
20
|
+
|
|
21
|
+
const STATUSLINES_DIR = path.join(os.homedir(), '.claude', 'statuslines');
|
|
22
|
+
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Script templates
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
const SCRIPTS = {
|
|
28
|
+
minimal: `#!/bin/bash
|
|
29
|
+
# Minimal: model name and context percentage
|
|
30
|
+
input=$(cat)
|
|
31
|
+
MODEL=$(echo "$input" | jq -r '.model.display_name // "?"')
|
|
32
|
+
PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
|
|
33
|
+
echo "* $MODEL $PCT% ctx"
|
|
34
|
+
`,
|
|
35
|
+
|
|
36
|
+
'context-bar': `#!/bin/bash
|
|
37
|
+
# Context Bar: model with dot-gauge context usage
|
|
38
|
+
input=$(cat)
|
|
39
|
+
MODEL=$(echo "$input" | jq -r '.model.display_name // "?"')
|
|
40
|
+
PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
|
|
41
|
+
FILLED=$((PCT / 10)); EMPTY=$((10 - FILLED))
|
|
42
|
+
BAR=""; for ((i=0; i<FILLED; i++)); do BAR="$BAR●"; done
|
|
43
|
+
for ((i=0; i<EMPTY; i++)); do BAR="$BAR○"; done
|
|
44
|
+
echo "* $MODEL ctx $BAR $PCT%"
|
|
45
|
+
`,
|
|
46
|
+
|
|
47
|
+
'git-context': `#!/bin/bash
|
|
48
|
+
# Git + Context: model, context bar, git branch, lines changed
|
|
49
|
+
input=$(cat)
|
|
50
|
+
MODEL=$(echo "$input" | jq -r '.model.display_name // "?"')
|
|
51
|
+
PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
|
|
52
|
+
LINES_ADD=$(echo "$input" | jq -r '.cost.total_lines_added // 0')
|
|
53
|
+
LINES_REM=$(echo "$input" | jq -r '.cost.total_lines_removed // 0')
|
|
54
|
+
FILLED=$((PCT / 10)); EMPTY=$((10 - FILLED))
|
|
55
|
+
BAR=""; for ((i=0; i<FILLED; i++)); do BAR="$BAR●"; done
|
|
56
|
+
for ((i=0; i<EMPTY; i++)); do BAR="$BAR○"; done
|
|
57
|
+
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo '')
|
|
58
|
+
OUT="* $MODEL | ctx $BAR $PCT%"
|
|
59
|
+
[ -n "$BRANCH" ] && OUT="$OUT | $BRANCH"
|
|
60
|
+
[ "$LINES_ADD" != "0" ] || [ "$LINES_REM" != "0" ] && OUT="$OUT | +$LINES_ADD -$LINES_REM"
|
|
61
|
+
echo "$OUT"
|
|
62
|
+
`,
|
|
63
|
+
|
|
64
|
+
full: `#!/bin/bash
|
|
65
|
+
# Full: model, context with token counts, lines changed, git branch, duration, cost
|
|
66
|
+
input=$(cat)
|
|
67
|
+
MODEL=$(echo "$input" | jq -r '.model.display_name // "?"')
|
|
68
|
+
PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
|
|
69
|
+
CTX_USED=$(echo "$input" | jq -r '((.context_window.current_usage.input_tokens // 0) + (.context_window.current_usage.cache_creation_input_tokens // 0) + (.context_window.current_usage.cache_read_input_tokens // 0))')
|
|
70
|
+
CTX_MAX=$(echo "$input" | jq -r '.context_window.context_window_size // 200000')
|
|
71
|
+
LINES_ADD=$(echo "$input" | jq -r '.cost.total_lines_added // 0')
|
|
72
|
+
LINES_REM=$(echo "$input" | jq -r '.cost.total_lines_removed // 0')
|
|
73
|
+
DUR_MS=$(echo "$input" | jq -r '.cost.total_duration_ms // 0')
|
|
74
|
+
COST=$(echo "$input" | jq -r '.cost.total_cost_usd // 0')
|
|
75
|
+
|
|
76
|
+
FILLED=$((PCT / 10)); EMPTY=$((10 - FILLED))
|
|
77
|
+
BAR=""; for ((i=0; i<FILLED; i++)); do BAR="$BAR●"; done
|
|
78
|
+
for ((i=0; i<EMPTY; i++)); do BAR="$BAR○"; done
|
|
79
|
+
|
|
80
|
+
CTX_K=$(awk "BEGIN {printf \\"%.1fK\\", $CTX_USED/1000}")
|
|
81
|
+
MAX_K=$(awk "BEGIN {printf \\"%.1fK\\", $CTX_MAX/1000}")
|
|
82
|
+
HOURS=$((DUR_MS / 3600000)); MINS=$(((DUR_MS % 3600000) / 60000))
|
|
83
|
+
COST_FMT=$(printf '$%.3f' $COST)
|
|
84
|
+
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo '')
|
|
85
|
+
|
|
86
|
+
OUT="* $MODEL | ctx $BAR $CTX_K/$MAX_K"
|
|
87
|
+
[ "$LINES_ADD" != "0" ] || [ "$LINES_REM" != "0" ] && OUT="$OUT | +$LINES_ADD -$LINES_REM"
|
|
88
|
+
[ -n "$BRANCH" ] && OUT="$OUT | $BRANCH"
|
|
89
|
+
[ "$HOURS" -gt 0 ] && OUT="$OUT | \${HOURS}h \${MINS}m" || OUT="$OUT | \${MINS}m"
|
|
90
|
+
OUT="$OUT | $COST_FMT"
|
|
91
|
+
echo "$OUT"
|
|
92
|
+
`,
|
|
93
|
+
|
|
94
|
+
'cost-tracker': `#!/bin/bash
|
|
95
|
+
# Cost Tracker: model, session cost, duration
|
|
96
|
+
input=$(cat)
|
|
97
|
+
MODEL=$(echo "$input" | jq -r '.model.display_name // "?"')
|
|
98
|
+
COST=$(echo "$input" | jq -r '.cost.total_cost_usd // 0')
|
|
99
|
+
DUR_MS=$(echo "$input" | jq -r '.cost.total_duration_ms // 0')
|
|
100
|
+
COST_FMT=$(printf '$%.3f' $COST)
|
|
101
|
+
MINS=$((DUR_MS / 60000)); SECS=$(((DUR_MS % 60000) / 1000))
|
|
102
|
+
echo "* $MODEL | $COST_FMT | \${MINS}m \${SECS}s"
|
|
103
|
+
`,
|
|
104
|
+
|
|
105
|
+
multiline: `#!/bin/bash
|
|
106
|
+
# Multiline: line 1 = model + git, line 2 = color context bar + cost
|
|
107
|
+
input=$(cat)
|
|
108
|
+
MODEL=$(echo "$input" | jq -r '.model.display_name // "?"')
|
|
109
|
+
PCT=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)
|
|
110
|
+
COST=$(echo "$input" | jq -r '.cost.total_cost_usd // 0')
|
|
111
|
+
DUR_MS=$(echo "$input" | jq -r '.cost.total_duration_ms // 0')
|
|
112
|
+
|
|
113
|
+
GREEN='\\033[32m'; YELLOW='\\033[33m'; RED='\\033[31m'
|
|
114
|
+
CYAN='\\033[36m'; RESET='\\033[0m'
|
|
115
|
+
[ "$PCT" -ge 90 ] && BAR_COLOR="$RED" || { [ "$PCT" -ge 70 ] && BAR_COLOR="$YELLOW" || BAR_COLOR="$GREEN"; }
|
|
116
|
+
|
|
117
|
+
FILLED=$((PCT / 10)); EMPTY=$((10 - FILLED))
|
|
118
|
+
BAR=""; for ((i=0; i<FILLED; i++)); do BAR="$BAR█"; done
|
|
119
|
+
for ((i=0; i<EMPTY; i++)); do BAR="$BAR░"; done
|
|
120
|
+
|
|
121
|
+
MINS=$((DUR_MS / 60000)); SECS=$(((DUR_MS % 60000) / 1000))
|
|
122
|
+
COST_FMT=$(printf '$%.3f' $COST)
|
|
123
|
+
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo '')
|
|
124
|
+
[ -n "$BRANCH" ] && BRANCH_STR=" | $BRANCH" || BRANCH_STR=""
|
|
125
|
+
|
|
126
|
+
echo -e "$CYAN* $MODEL$RESET$BRANCH_STR"
|
|
127
|
+
echo -e "$BAR_COLOR$BAR$RESET $PCT% | $YELLOW$COST_FMT$RESET | \${MINS}m \${SECS}s"
|
|
128
|
+
`,
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// ---------------------------------------------------------------------------
|
|
132
|
+
// Preset metadata (no script content here — see SCRIPTS above)
|
|
133
|
+
// ---------------------------------------------------------------------------
|
|
8
134
|
|
|
9
|
-
/**
|
|
10
|
-
* Preset statusline library.
|
|
11
|
-
* Each preset's `command` is set as `statusCommand` in ~/.claude/settings.json.
|
|
12
|
-
* Empty command string = remove statusCommand (use Claude Code built-in).
|
|
13
|
-
*/
|
|
14
135
|
const PRESETS = [
|
|
15
136
|
{
|
|
16
|
-
id: '
|
|
17
|
-
name: '
|
|
18
|
-
description: '
|
|
19
|
-
preview: '
|
|
20
|
-
command: '',
|
|
137
|
+
id: 'disabled',
|
|
138
|
+
name: 'Disabled',
|
|
139
|
+
description: 'No status bar shown',
|
|
140
|
+
preview: '',
|
|
21
141
|
category: 'Built-in',
|
|
22
142
|
},
|
|
23
143
|
{
|
|
24
|
-
id: '
|
|
25
|
-
name: '
|
|
26
|
-
description: '
|
|
27
|
-
preview: '
|
|
28
|
-
|
|
29
|
-
category: 'Git',
|
|
144
|
+
id: 'minimal',
|
|
145
|
+
name: 'Minimal',
|
|
146
|
+
description: 'Model name and context percentage',
|
|
147
|
+
preview: '* opus-4-6 37% ctx',
|
|
148
|
+
category: 'Simple',
|
|
30
149
|
},
|
|
31
150
|
{
|
|
32
|
-
id: '
|
|
33
|
-
name: '
|
|
34
|
-
description: '
|
|
35
|
-
preview: '
|
|
36
|
-
|
|
37
|
-
category: 'Git',
|
|
151
|
+
id: 'context-bar',
|
|
152
|
+
name: 'Context Bar',
|
|
153
|
+
description: 'Model with a ●○ dot gauge showing context usage',
|
|
154
|
+
preview: '* opus-4-6 ctx ●●●●○○○○○○ 37%',
|
|
155
|
+
category: 'Simple',
|
|
38
156
|
},
|
|
39
157
|
{
|
|
40
|
-
id: 'git-
|
|
41
|
-
name: 'Git
|
|
42
|
-
description: '
|
|
43
|
-
preview: ' main
|
|
44
|
-
command: "branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo '?') && ahead=$(git rev-list --count @{u}..HEAD 2>/dev/null || echo 0) && behind=$(git rev-list --count HEAD..@{u} 2>/dev/null || echo 0) && echo \" ${branch} ↑${ahead} ↓${behind}\"",
|
|
158
|
+
id: 'git-context',
|
|
159
|
+
name: 'Git + Context',
|
|
160
|
+
description: 'Model, context bar, git branch, and lines changed this session',
|
|
161
|
+
preview: '* opus-4-6 | ctx ●●●●○○○○○○ 37% | main | +146 -13',
|
|
45
162
|
category: 'Git',
|
|
46
163
|
},
|
|
47
164
|
{
|
|
48
|
-
id: '
|
|
49
|
-
name: '
|
|
50
|
-
description: '
|
|
51
|
-
preview: '
|
|
52
|
-
command: "repo=$(basename $(git rev-parse --show-toplevel 2>/dev/null) 2>/dev/null || basename $PWD) && branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo '') && count=$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ') && echo \"${repo} ${branch} ${count} pending\"",
|
|
165
|
+
id: 'full',
|
|
166
|
+
name: 'Full',
|
|
167
|
+
description: 'Everything: model, context with token counts, lines, branch, duration, cost',
|
|
168
|
+
preview: '* opus-4-6 | ctx ●●●●○○○○○○ 74.4K/200.0K | +146 -13 | main | 5h 2m | $0.142',
|
|
53
169
|
category: 'Git',
|
|
54
170
|
},
|
|
55
171
|
{
|
|
56
|
-
id: '
|
|
57
|
-
name: '
|
|
58
|
-
description: '
|
|
59
|
-
preview: '
|
|
60
|
-
|
|
61
|
-
category: 'System',
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
id: 'datetime',
|
|
65
|
-
name: 'Date & Time',
|
|
66
|
-
description: 'Full date and time',
|
|
67
|
-
preview: ' Mon Mar 04 14:32',
|
|
68
|
-
command: "date +' %a %b %d %H:%M'",
|
|
69
|
-
category: 'System',
|
|
172
|
+
id: 'cost-tracker',
|
|
173
|
+
name: 'Cost Tracker',
|
|
174
|
+
description: 'Model, total session cost, and elapsed time',
|
|
175
|
+
preview: '* opus-4-6 | $0.142 | 32m 15s',
|
|
176
|
+
category: 'Cost',
|
|
70
177
|
},
|
|
71
178
|
{
|
|
72
|
-
id: '
|
|
73
|
-
name: '
|
|
74
|
-
description: '
|
|
75
|
-
preview: '
|
|
76
|
-
|
|
77
|
-
category: 'System',
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
id: 'git-clock',
|
|
81
|
-
name: 'Git + Clock',
|
|
82
|
-
description: 'Branch and current time',
|
|
83
|
-
preview: ' main 14:32',
|
|
84
|
-
command: "branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo '') && echo \" ${branch} $(date +'%H:%M')\"",
|
|
85
|
-
category: 'Combo',
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
id: 'git-project-clock',
|
|
89
|
-
name: 'Git + Project + Clock',
|
|
90
|
-
description: 'Repo name, branch with diff stats, and time',
|
|
91
|
-
preview: 'coder-config main +3 ~1 14:32',
|
|
92
|
-
command: "repo=$(basename $(git rev-parse --show-toplevel 2>/dev/null) 2>/dev/null || basename $PWD) && branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo '') && added=$(git diff --stat HEAD 2>/dev/null | grep -E '^.*\\+' | tail -1 | grep -o '[0-9]* insertion' | grep -o '[0-9]*' || echo 0) && deleted=$(git diff --stat HEAD 2>/dev/null | grep -E 'deletion' | tail -1 | grep -o '[0-9]* deletion' | grep -o '[0-9]*' || echo 0) && echo \"${repo} ${branch} +${added} ~${deleted} $(date +'%H:%M')\"",
|
|
93
|
-
category: 'Combo',
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
id: 'minimal-branch',
|
|
97
|
-
name: 'Minimal',
|
|
98
|
-
description: 'Just the git branch, nothing else',
|
|
99
|
-
preview: 'main',
|
|
100
|
-
command: "git rev-parse --abbrev-ref HEAD 2>/dev/null || echo ''",
|
|
101
|
-
category: 'Minimal',
|
|
179
|
+
id: 'multiline',
|
|
180
|
+
name: 'Multiline',
|
|
181
|
+
description: 'Two rows: model + branch on top, color-coded context bar + cost below',
|
|
182
|
+
preview: '* opus-4-6 | main\n█████░░░░░ 37% | $0.142 | 32m 15s',
|
|
183
|
+
category: 'Cost',
|
|
102
184
|
},
|
|
103
185
|
{
|
|
104
186
|
id: 'custom',
|
|
105
|
-
name: 'Custom',
|
|
106
|
-
description: 'Write your own
|
|
107
|
-
preview:
|
|
108
|
-
command: null, // null = user-defined
|
|
187
|
+
name: 'Custom Script',
|
|
188
|
+
description: 'Write your own bash script — receives Claude Code JSON via stdin',
|
|
189
|
+
preview: null,
|
|
109
190
|
category: 'Custom',
|
|
110
191
|
},
|
|
111
192
|
];
|
|
112
193
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
// Helpers
|
|
196
|
+
// ---------------------------------------------------------------------------
|
|
197
|
+
|
|
198
|
+
function scriptPath(presetId) {
|
|
199
|
+
return path.join(STATUSLINES_DIR, `${presetId}.sh`);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function expandHome(p) {
|
|
203
|
+
return p.replace(/^~/, os.homedir());
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function settingsPath() {
|
|
207
|
+
return path.join(os.homedir(), '.claude', 'settings.json');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function readSettings() {
|
|
211
|
+
const p = settingsPath();
|
|
212
|
+
if (!fs.existsSync(p)) return {};
|
|
213
|
+
try { return JSON.parse(fs.readFileSync(p, 'utf8')); } catch { return {}; }
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function writeSettings(settings) {
|
|
217
|
+
const p = settingsPath();
|
|
218
|
+
const dir = path.dirname(p);
|
|
219
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
220
|
+
fs.writeFileSync(p, JSON.stringify(settings, null, 2) + '\n', 'utf8');
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function ensureScriptDir() {
|
|
224
|
+
if (!fs.existsSync(STATUSLINES_DIR)) fs.mkdirSync(STATUSLINES_DIR, { recursive: true });
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function writeScript(presetId, content) {
|
|
228
|
+
ensureScriptDir();
|
|
229
|
+
const p = scriptPath(presetId);
|
|
230
|
+
fs.writeFileSync(p, content, 'utf8');
|
|
231
|
+
fs.chmodSync(p, 0o755);
|
|
232
|
+
return p;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function commandPathInSettings(settings) {
|
|
236
|
+
return settings?.statusLine?.command || null;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function matchPresetFromCommand(cmd) {
|
|
240
|
+
if (!cmd) return 'disabled';
|
|
241
|
+
// Match against known script paths
|
|
242
|
+
for (const preset of PRESETS) {
|
|
243
|
+
if (preset.id === 'disabled' || preset.id === 'custom') continue;
|
|
244
|
+
const expected = scriptPath(preset.id);
|
|
245
|
+
const expectedHome = expected.replace(os.homedir(), '~');
|
|
246
|
+
if (cmd === expected || cmd === expectedHome) return preset.id;
|
|
247
|
+
}
|
|
248
|
+
return 'custom';
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// ---------------------------------------------------------------------------
|
|
252
|
+
// Route handlers
|
|
253
|
+
// ---------------------------------------------------------------------------
|
|
254
|
+
|
|
116
255
|
function getStatuslinePresets() {
|
|
117
256
|
return { presets: PRESETS };
|
|
118
257
|
}
|
|
119
258
|
|
|
120
|
-
/**
|
|
121
|
-
* Reads current statusCommand from ~/.claude/settings.json
|
|
122
|
-
*/
|
|
123
259
|
function getCurrentStatusline() {
|
|
124
|
-
const
|
|
260
|
+
const settings = readSettings();
|
|
261
|
+
const cmd = commandPathInSettings(settings);
|
|
262
|
+
const presetId = matchPresetFromCommand(cmd);
|
|
125
263
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
264
|
+
// For custom, also return the current script content
|
|
265
|
+
let scriptContent = '';
|
|
266
|
+
if (presetId === 'custom' && cmd) {
|
|
267
|
+
const resolved = expandHome(cmd);
|
|
268
|
+
if (fs.existsSync(resolved)) {
|
|
269
|
+
scriptContent = fs.readFileSync(resolved, 'utf8');
|
|
129
270
|
}
|
|
130
|
-
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
131
|
-
const command = settings.statusCommand || '';
|
|
132
|
-
const matched = PRESETS.find(p => p.command !== null && p.command === command);
|
|
133
|
-
return {
|
|
134
|
-
command,
|
|
135
|
-
presetId: matched ? matched.id : (command ? 'custom' : 'default'),
|
|
136
|
-
};
|
|
137
|
-
} catch (e) {
|
|
138
|
-
return { command: '', presetId: 'default', error: e.message };
|
|
139
271
|
}
|
|
272
|
+
|
|
273
|
+
return { command: cmd || '', presetId, scriptContent };
|
|
140
274
|
}
|
|
141
275
|
|
|
142
276
|
/**
|
|
143
|
-
*
|
|
144
|
-
*
|
|
277
|
+
* Apply a preset or custom script.
|
|
278
|
+
* body: { presetId: string, scriptContent?: string }
|
|
145
279
|
*/
|
|
146
280
|
function setStatusline(body) {
|
|
147
|
-
const {
|
|
148
|
-
const settingsPath = path.join(os.homedir(), '.claude', 'settings.json');
|
|
281
|
+
const { presetId, scriptContent } = body;
|
|
149
282
|
|
|
150
283
|
try {
|
|
151
|
-
const
|
|
152
|
-
if (!fs.existsSync(claudeDir)) {
|
|
153
|
-
fs.mkdirSync(claudeDir, { recursive: true });
|
|
154
|
-
}
|
|
284
|
+
const settings = readSettings();
|
|
155
285
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
} catch (e) {
|
|
161
|
-
settings = {};
|
|
162
|
-
}
|
|
286
|
+
if (presetId === 'disabled') {
|
|
287
|
+
delete settings.statusLine;
|
|
288
|
+
writeSettings(settings);
|
|
289
|
+
return { success: true, presetId: 'disabled', command: '' };
|
|
163
290
|
}
|
|
164
291
|
|
|
165
|
-
|
|
166
|
-
|
|
292
|
+
let cmd;
|
|
293
|
+
|
|
294
|
+
if (presetId === 'custom') {
|
|
295
|
+
if (!scriptContent) return { success: false, error: 'scriptContent required for custom preset' };
|
|
296
|
+
cmd = writeScript('custom', scriptContent);
|
|
167
297
|
} else {
|
|
168
|
-
|
|
298
|
+
const template = SCRIPTS[presetId];
|
|
299
|
+
if (!template) return { success: false, error: `Unknown preset: ${presetId}` };
|
|
300
|
+
cmd = writeScript(presetId, template);
|
|
169
301
|
}
|
|
170
302
|
|
|
171
|
-
|
|
303
|
+
settings.statusLine = { type: 'command', command: cmd };
|
|
304
|
+
writeSettings(settings);
|
|
172
305
|
|
|
173
|
-
|
|
174
|
-
return {
|
|
175
|
-
success: true,
|
|
176
|
-
command: command || '',
|
|
177
|
-
presetId: matched ? matched.id : (command ? 'custom' : 'default'),
|
|
178
|
-
};
|
|
306
|
+
return { success: true, presetId, command: cmd };
|
|
179
307
|
} catch (e) {
|
|
180
308
|
return { success: false, error: e.message };
|
|
181
309
|
}
|