@kurokeita/add-skill 1.18.0 → 1.19.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.
@@ -0,0 +1,479 @@
1
+ ---
2
+ name: statusline-setup
3
+ description: 'Set up the Claude Code statusline with cwd, git branch, model, context %, session token totals, and 5h/7d quota usage with ETA. Use when asked to "set up my statusline", "install statusline", "configure Claude Code statusline", or to add a colored status line showing rate limits and token usage. Detects OS and installs the appropriate variant (bash + jq on Linux/macOS, PowerShell on Windows).'
4
+ ---
5
+
6
+ # Statusline Setup
7
+
8
+ Install a Claude Code statusline that renders, left to right, pipe-separated:
9
+
10
+ 1. **cwd** — 24-bit ANSI `#5EFFFF` (`38;2;94;255;255`), with `$HOME` / `%USERPROFILE%` shortened to `~`.
11
+ 2. **git branch** in parentheses — 24-bit ANSI `#C24870` (`38;2;194;72;112`), only when inside a repo.
12
+ 3. **model display name + effort** in parens — 24-bit ANSI `#E89440` (`38;2;232;148;64`), effort only when present.
13
+ 4. **`ctx N%`** — 24-bit ANSI `#009AFB` (`38;2;0;154;251`), the `context_window.used_percentage`.
14
+ 5. **tokens** as `↑<in> ↓<out>` — 24-bit ANSI `#937bda` (`38;2;147;123;218`), its own pipe-separated segment immediately after `ctx`.
15
+ 6. **`5h:N%(eta)`** and **`7d:N%(eta)`** — colored independently by remaining quota (see below).
16
+
17
+ Segments are joined with ANSI `0;37` pipes (` | `).
18
+
19
+ ## When to Use This Skill
20
+
21
+ - "Set up my Claude Code statusline"
22
+ - "Install the statusline"
23
+ - "Configure my statusline with token counts and quota"
24
+ - Any request to add a colored status line showing context %, rate limits, or session tokens.
25
+
26
+ ## OS Detection
27
+
28
+ Detect the platform first and install only the matching variant:
29
+
30
+ - **Linux / macOS** → write `~/.claude/statusline-command.sh` (pure bash + `jq`), wire into `~/.claude/settings.json`.
31
+ - **Windows** → write `%USERPROFILE%\.claude\statusline-command.ps1`, wire into `%USERPROFILE%\.claude\settings.json`.
32
+
33
+ ## Token Formatting
34
+
35
+ - `< 1000` → raw integer (e.g. `↑850 ↓120`)
36
+ - `>= 1_000` → one decimal + `k` (e.g. `↑1.2k ↓34.5k`)
37
+ - `>= 1_000_000` → one decimal + `M` (e.g. `↑1.2M ↓3.4M`)
38
+ - Missing/unparseable transcript → omit the segment entirely (no stray separator, never render `↑0 ↓0`).
39
+
40
+ ## Token Source
41
+
42
+ Claude Code does **not** include token totals in the statusline JSON. Read `transcript_path` (JSONL, one message per line) and sum across **assistant** messages:
43
+
44
+ - `input` = `message.usage.input_tokens` + `message.usage.cache_creation_input_tokens` + `message.usage.cache_read_input_tokens`
45
+ - `output` = `message.usage.output_tokens`
46
+
47
+ Skip lines that fail JSON parsing or lack `message.usage`. If `transcript_path` is missing or the file does not exist, omit the tokens segment.
48
+
49
+ ## Quota Colors (by REMAINING quota = 100 - used%)
50
+
51
+ | Remaining | Color | 24-bit ANSI |
52
+ |-----------|-------|-------------|
53
+ | `> 60%` | bright green `#4eba65` | `38;2;78;186;101` |
54
+ | `31–60%` | amber `#de8e3e` | `38;2;222;142;62` |
55
+ | `11–30%` | orange `#ea580c` | `38;2;234;88;12` |
56
+ | `<= 10%` | deep red-orange `#b74426` | `38;2;183;68;38` |
57
+
58
+ The 5h and 7d windows must be colored **independently**.
59
+
60
+ ## ETA Format (from epoch seconds in `resets_at`)
61
+
62
+ - `> 1 day` → `Xd Yh`
63
+ - `> 1 hour` → `Xh Ym`
64
+ - else → `Xm`
65
+ - past → `now`
66
+
67
+ ## Input Schema (stdin JSON)
68
+
69
+ Any field may be missing:
70
+
71
+ ```
72
+ cwd
73
+ transcript_path
74
+ model.display_name
75
+ effort.level
76
+ context_window.used_percentage
77
+ rate_limits.five_hour.used_percentage
78
+ rate_limits.five_hour.resets_at
79
+ rate_limits.seven_day.used_percentage
80
+ rate_limits.seven_day.resets_at
81
+ ```
82
+
83
+ ## Linux / macOS Implementation
84
+
85
+ Write `~/.claude/statusline-command.sh` (chmod +x). Pure bash + `jq` — no Python. `jq` must be installed (`apt install jq`, `brew install jq`, etc.); if missing, the script prints a one-line hint and exits 0 so the statusline area stays clean.
86
+
87
+ JSON parsing rules:
88
+
89
+ - Read the full stdin payload **once** with `cat`, parse all fields with a single `jq` call.
90
+ - For the JSONL transcript, invoke `jq` **once over the whole file** (do not call `jq` per line) — use a streaming filter that reduces over `inputs`.
91
+
92
+ `~/.claude/statusline-command.sh`:
93
+
94
+ ```bash
95
+ #!/usr/bin/env bash
96
+ # Claude Code statusline — pure bash + jq.
97
+
98
+ set -u
99
+
100
+ if ! command -v jq >/dev/null 2>&1; then
101
+ printf '%s' "statusline: jq not installed"
102
+ exit 0
103
+ fi
104
+
105
+ C_CWD=$'\033[38;2;94;255;255m'
106
+ C_BRANCH=$'\033[38;2;194;72;112m'
107
+ C_PIPE=$'\033[0;37m'
108
+ C_MODEL=$'\033[38;2;232;148;64m'
109
+ C_CTX=$'\033[38;2;0;154;251m'
110
+ C_TOK=$'\033[38;2;147;123;218m'
111
+ RESET=$'\033[0m'
112
+
113
+ GREEN=$'\033[38;2;78;186;101m'
114
+ AMBER=$'\033[38;2;222;142;62m'
115
+ ORANGE=$'\033[38;2;234;88;12m'
116
+ REDORG=$'\033[38;2;183;68;38m'
117
+
118
+ quota_color() {
119
+ local used="$1"
120
+ local rem=$(( 100 - used ))
121
+ if (( rem > 60 )); then printf '%s' "$GREEN"
122
+ elif (( rem > 30 )); then printf '%s' "$AMBER"
123
+ elif (( rem > 10 )); then printf '%s' "$ORANGE"
124
+ else printf '%s' "$REDORG"
125
+ fi
126
+ }
127
+
128
+ fmt_eta() {
129
+ local resets="$1"
130
+ [[ -z "$resets" || "$resets" == "null" ]] && { printf ''; return; }
131
+ local now delta days hours mins
132
+ now=$(date +%s)
133
+ delta=$(( resets - now ))
134
+ if (( delta <= 0 )); then printf 'now'
135
+ elif (( delta >= 86400 )); then
136
+ days=$(( delta / 86400 )); hours=$(( (delta % 86400) / 3600 ))
137
+ printf '%dd %dh' "$days" "$hours"
138
+ elif (( delta >= 3600 )); then
139
+ hours=$(( delta / 3600 )); mins=$(( (delta % 3600) / 60 ))
140
+ printf '%dh %dm' "$hours" "$mins"
141
+ else
142
+ mins=$(( delta / 60 ))
143
+ printf '%dm' "$mins"
144
+ fi
145
+ }
146
+
147
+ fmt_tokens() {
148
+ local n="$1"
149
+ if (( n < 1000 )); then printf '%d' "$n"
150
+ elif (( n < 1000000 )); then awk -v n="$n" 'BEGIN{printf "%.1fk", n/1000}'
151
+ else awk -v n="$n" 'BEGIN{printf "%.1fM", n/1000000}'
152
+ fi
153
+ }
154
+
155
+ shorten_home() {
156
+ local p="$1"
157
+ [[ -z "$p" ]] && return
158
+ if [[ "$p" == "$HOME" ]]; then printf '~'
159
+ elif [[ "$p" == "$HOME"/* ]]; then printf '~%s' "${p#$HOME}"
160
+ else printf '%s' "$p"
161
+ fi
162
+ }
163
+
164
+ # --- read & parse stdin payload in a single jq call ---
165
+ payload=$(cat)
166
+ if [[ -z "${payload// }" ]]; then payload='{}'; fi
167
+
168
+ read_fields() {
169
+ jq -r '
170
+ [
171
+ .cwd // "",
172
+ .transcript_path // "",
173
+ .model.display_name // "",
174
+ .effort.level // "",
175
+ (.context_window.used_percentage // "" | tostring),
176
+ (.rate_limits.five_hour.used_percentage // "" | tostring),
177
+ (.rate_limits.five_hour.resets_at // "" | tostring),
178
+ (.rate_limits.seven_day.used_percentage // "" | tostring),
179
+ (.rate_limits.seven_day.resets_at // "" | tostring)
180
+ ] | join("\u001f")
181
+ ' 2>/dev/null <<<"$payload"
182
+ }
183
+
184
+ IFS=$'\x1f' read -r cwd transcript model effort ctx five_used five_reset seven_used seven_reset < <(read_fields)
185
+
186
+ segments=()
187
+
188
+ if [[ -n "$cwd" ]]; then
189
+ segments+=("${C_CWD}$(shorten_home "$cwd")${RESET}")
190
+ fi
191
+
192
+ # Branch: must use git -C "$cwd", never PWD.
193
+ branch=""
194
+ if [[ -n "$cwd" ]]; then
195
+ branch=$(git -C "$cwd" rev-parse --abbrev-ref HEAD 2>/dev/null) || branch=""
196
+ fi
197
+ if [[ -n "$branch" ]]; then
198
+ segments+=("${C_BRANCH}(${branch})${RESET}")
199
+ fi
200
+
201
+ if [[ -n "$model" ]]; then
202
+ if [[ -n "$effort" ]]; then
203
+ segments+=("${C_MODEL}${model} (${effort})${RESET}")
204
+ else
205
+ segments+=("${C_MODEL}${model}${RESET}")
206
+ fi
207
+ fi
208
+
209
+ if [[ -n "$ctx" ]]; then
210
+ ctx_int=$(awk -v n="$ctx" 'BEGIN{printf "%d", (n+0.5)}')
211
+ segments+=("${C_CTX}ctx ${ctx_int}%${RESET}")
212
+ fi
213
+
214
+ # --- tokens: one jq pass over the whole JSONL transcript ---
215
+ if [[ -n "$transcript" && -f "$transcript" ]]; then
216
+ tok=$(jq -rs '
217
+ reduce .[] as $m ({i:0,o:0};
218
+ ($m.message.usage // null) as $u
219
+ | if $u == null then .
220
+ else
221
+ .i += (($u.input_tokens // 0) + ($u.cache_creation_input_tokens // 0) + ($u.cache_read_input_tokens // 0))
222
+ | .o += ($u.output_tokens // 0)
223
+ end
224
+ )
225
+ | "\(.i)\t\(.o)"
226
+ ' "$transcript" 2>/dev/null) || tok=""
227
+ if [[ -n "$tok" ]]; then
228
+ IFS=$'\t' read -r ti to <<<"$tok"
229
+ if (( ti > 0 || to > 0 )); then
230
+ segments+=("${C_TOK}↑$(fmt_tokens "$ti") ↓$(fmt_tokens "$to")${RESET}")
231
+ fi
232
+ fi
233
+ fi
234
+
235
+ if [[ -n "$five_used" ]]; then
236
+ fu=$(awk -v n="$five_used" 'BEGIN{printf "%d", (n+0.5)}')
237
+ eta=$(fmt_eta "$five_reset")
238
+ segments+=("$(quota_color "$fu")5h:${fu}%(${eta})${RESET}")
239
+ fi
240
+ if [[ -n "$seven_used" ]]; then
241
+ su=$(awk -v n="$seven_used" 'BEGIN{printf "%d", (n+0.5)}')
242
+ eta=$(fmt_eta "$seven_reset")
243
+ segments+=("$(quota_color "$su")7d:${su}%(${eta})${RESET}")
244
+ fi
245
+
246
+ sep=" ${C_PIPE}|${RESET} "
247
+ out=""
248
+ for i in "${!segments[@]}"; do
249
+ if (( i == 0 )); then out="${segments[$i]}"
250
+ else out="${out}${sep}${segments[$i]}"
251
+ fi
252
+ done
253
+ printf '%s' "$out"
254
+ ```
255
+
256
+ Wire into `~/.claude/settings.json` (merge into existing JSON, don't clobber other keys):
257
+
258
+ ```json
259
+ {
260
+ "statusLine": {
261
+ "type": "command",
262
+ "command": "bash ~/.claude/statusline-command.sh"
263
+ }
264
+ }
265
+ ```
266
+
267
+ ## Windows Implementation
268
+
269
+ Write `%USERPROFILE%\.claude\statusline-command.ps1`. Use `ConvertFrom-Json` (no Python dep). Read the JSONL transcript with `Get-Content -ReadCount 0` and `ConvertFrom-Json` per line inside try/catch. Use `[char]27` for ANSI escapes (works on PS 5.1 and 7+). Read stdin with `[Console]::In.ReadToEnd()`.
270
+
271
+ `%USERPROFILE%\.claude\statusline-command.ps1`:
272
+
273
+ ```powershell
274
+ $ErrorActionPreference = 'SilentlyContinue'
275
+ [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
276
+ $OutputEncoding = [System.Text.Encoding]::UTF8
277
+ $ESC = [char]27
278
+
279
+ $C_CWD = "$ESC[38;2;94;255;255m"
280
+ $C_BRANCH = "$ESC[38;2;194;72;112m"
281
+ $C_PIPE = "$ESC[0;37m"
282
+ $C_MODEL = "$ESC[38;2;232;148;64m"
283
+ $C_CTX = "$ESC[38;2;0;154;251m"
284
+ $C_TOK = "$ESC[38;2;147;123;218m"
285
+ $RESET = "$ESC[0m"
286
+
287
+ $GREEN = "$ESC[38;2;78;186;101m"
288
+ $AMBER = "$ESC[38;2;222;142;62m"
289
+ $ORANGE = "$ESC[38;2;234;88;12m"
290
+ $REDORG = "$ESC[38;2;183;68;38m"
291
+
292
+ function QuotaColor([double]$used) {
293
+ $remaining = 100 - $used
294
+ if ($remaining -gt 60) { return $GREEN }
295
+ if ($remaining -gt 30) { return $AMBER }
296
+ if ($remaining -gt 10) { return $ORANGE }
297
+ return $REDORG
298
+ }
299
+
300
+ function FmtEta($resetsAt) {
301
+ if ($null -eq $resetsAt) { return "" }
302
+ $delta = [int64]$resetsAt - [int64](Get-Date -UFormat %s)
303
+ if ($delta -le 0) { return "now" }
304
+ $days = [math]::Floor($delta / 86400); $delta = $delta % 86400
305
+ $hours = [math]::Floor($delta / 3600); $delta = $delta % 3600
306
+ $mins = [math]::Floor($delta / 60)
307
+ if ($days -gt 0) { return "${days}d ${hours}h" }
308
+ if ($hours -gt 0) { return "${hours}h ${mins}m" }
309
+ return "${mins}m"
310
+ }
311
+
312
+ function FmtTokens([int64]$n) {
313
+ if ($n -lt 1000) { return "$n" }
314
+ if ($n -lt 1000000) { return ("{0:N1}k" -f ($n / 1000.0)) }
315
+ return ("{0:N1}M" -f ($n / 1000000.0))
316
+ }
317
+
318
+ function ShortenHome($p) {
319
+ if (-not $p) { return "" }
320
+ $home = $env:USERPROFILE
321
+ if ($p -eq $home) { return "~" }
322
+ if ($p.StartsWith("$home\")) { return "~" + $p.Substring($home.Length) }
323
+ return $p
324
+ }
325
+
326
+ function GetBranch($cwd) {
327
+ if (-not $cwd) { return $null }
328
+ try {
329
+ $b = & git -C "$cwd" rev-parse --abbrev-ref HEAD 2>$null
330
+ if ($LASTEXITCODE -eq 0 -and $b) { return $b.Trim() }
331
+ } catch {}
332
+ return $null
333
+ }
334
+
335
+ function SumTokens($path) {
336
+ if (-not $path) { return $null }
337
+ if (-not (Test-Path -LiteralPath $path)) { return $null }
338
+ $sumIn = 0; $sumOut = 0; $found = $false
339
+ $lines = Get-Content -LiteralPath $path -ReadCount 0 -ErrorAction SilentlyContinue
340
+ if ($null -eq $lines) { return $null }
341
+ foreach ($line in $lines) {
342
+ if (-not $line) { continue }
343
+ try { $obj = $line | ConvertFrom-Json -ErrorAction Stop } catch { continue }
344
+ $msg = $obj.message
345
+ if (-not $msg) { continue }
346
+ $usage = $msg.usage
347
+ if (-not $usage) { continue }
348
+ $found = $true
349
+ if ($usage.input_tokens) { $sumIn += [int64]$usage.input_tokens }
350
+ if ($usage.cache_creation_input_tokens) { $sumIn += [int64]$usage.cache_creation_input_tokens }
351
+ if ($usage.cache_read_input_tokens) { $sumIn += [int64]$usage.cache_read_input_tokens }
352
+ if ($usage.output_tokens) { $sumOut += [int64]$usage.output_tokens }
353
+ }
354
+ if (-not $found) { return $null }
355
+ return ,@($sumIn, $sumOut)
356
+ }
357
+
358
+ $raw = [Console]::In.ReadToEnd()
359
+ try { $data = if ($raw.Trim()) { $raw | ConvertFrom-Json } else { @{} } } catch { $data = @{} }
360
+
361
+ $segments = @()
362
+
363
+ $cwd = $data.cwd
364
+ if ($cwd) { $segments += "$C_CWD$(ShortenHome $cwd)$RESET" }
365
+
366
+ $branch = GetBranch $cwd
367
+ if ($branch) { $segments += "$C_BRANCH($branch)$RESET" }
368
+
369
+ $model = $data.model.display_name
370
+ $effort = $data.effort.level
371
+ if ($model) {
372
+ if ($effort) { $segments += "$C_MODEL$model ($effort)$RESET" }
373
+ else { $segments += "$C_MODEL$model$RESET" }
374
+ }
375
+
376
+ $ctx = $data.context_window.used_percentage
377
+ if ($null -ne $ctx) { $segments += "$C_CTX" + "ctx " + [int][math]::Round([double]$ctx) + "%$RESET" }
378
+
379
+ $tokens = SumTokens $data.transcript_path
380
+ if ($null -ne $tokens) {
381
+ $segments += "$C_TOK↑$(FmtTokens $tokens[0]) ↓$(FmtTokens $tokens[1])$RESET"
382
+ }
383
+
384
+ $five = $data.rate_limits.five_hour
385
+ $seven = $data.rate_limits.seven_day
386
+ if ($null -ne $five.used_percentage) {
387
+ $u = [double]$five.used_percentage
388
+ $segments += "$(QuotaColor $u)5h:$([int][math]::Round($u))%($(FmtEta $five.resets_at))$RESET"
389
+ }
390
+ if ($null -ne $seven.used_percentage) {
391
+ $u = [double]$seven.used_percentage
392
+ $segments += "$(QuotaColor $u)7d:$([int][math]::Round($u))%($(FmtEta $seven.resets_at))$RESET"
393
+ }
394
+
395
+ $sep = " $C_PIPE|$RESET "
396
+ [Console]::Out.Write([string]::Join($sep, $segments))
397
+ ```
398
+
399
+ Wire into `%USERPROFILE%\.claude\settings.json`:
400
+
401
+ ```json
402
+ {
403
+ "statusLine": {
404
+ "type": "command",
405
+ "command": "powershell.exe -NoProfile -ExecutionPolicy Bypass -File \"%USERPROFILE%\\.claude\\statusline-command.ps1\""
406
+ }
407
+ }
408
+ ```
409
+
410
+ ## Smoke Test
411
+
412
+ After installing, generate a fake JSON payload with 5h at 80% used, 7d at 50% used, both with future `resets_at`, plus a `transcript_path` pointing at a small fixture JSONL containing two assistant messages with `usage` blocks. Pipe it into the script and confirm:
413
+
414
+ - 5h renders **orange**, 7d renders **amber**.
415
+ - cwd, model, and context % are present.
416
+ - Tokens segment renders `↑<sum> ↓<sum>` matching the fixture, in `#937bda`, as its own pipe-separated segment immediately after `ctx`.
417
+ - Omitting `transcript_path` hides the tokens segment cleanly (no stray separator).
418
+
419
+ ### Linux/macOS smoke test
420
+
421
+ ```bash
422
+ mkdir -p /tmp/statusline-smoke
423
+ cat >/tmp/statusline-smoke/transcript.jsonl <<'EOF'
424
+ {"type":"assistant","message":{"role":"assistant","usage":{"input_tokens":100,"cache_creation_input_tokens":200,"cache_read_input_tokens":300,"output_tokens":50}}}
425
+ {"type":"assistant","message":{"role":"assistant","usage":{"input_tokens":10,"cache_creation_input_tokens":20,"cache_read_input_tokens":30,"output_tokens":5}}}
426
+ EOF
427
+ FUTURE=$(($(date +%s) + 7200))
428
+ FUTURE7=$(($(date +%s) + 3*86400))
429
+ printf '{"cwd":"%s","transcript_path":"/tmp/statusline-smoke/transcript.jsonl","model":{"display_name":"Opus 4.7"},"effort":{"level":"medium"},"context_window":{"used_percentage":42},"rate_limits":{"five_hour":{"used_percentage":80,"resets_at":%d},"seven_day":{"used_percentage":50,"resets_at":%d}}}' "$HOME" "$FUTURE" "$FUTURE7" | bash ~/.claude/statusline-command.sh; echo
430
+ ```
431
+
432
+ ### Windows smoke test (PowerShell)
433
+
434
+ ```powershell
435
+ $tmp = "$env:TEMP\statusline-smoke"
436
+ New-Item -ItemType Directory -Force -Path $tmp | Out-Null
437
+ @'
438
+ {"type":"assistant","message":{"role":"assistant","usage":{"input_tokens":100,"cache_creation_input_tokens":200,"cache_read_input_tokens":300,"output_tokens":50}}}
439
+ {"type":"assistant","message":{"role":"assistant","usage":{"input_tokens":10,"cache_creation_input_tokens":20,"cache_read_input_tokens":30,"output_tokens":5}}}
440
+ '@ | Set-Content -LiteralPath "$tmp\transcript.jsonl" -Encoding UTF8
441
+ $future = [int64](Get-Date -UFormat %s) + 7200
442
+ $future7 = [int64](Get-Date -UFormat %s) + 3*86400
443
+ $payload = @{
444
+ cwd = $env:USERPROFILE
445
+ transcript_path = "$tmp\transcript.jsonl"
446
+ model = @{ display_name = "Opus 4.7" }
447
+ effort = @{ level = "medium" }
448
+ context_window = @{ used_percentage = 42 }
449
+ rate_limits = @{
450
+ five_hour = @{ used_percentage = 80; resets_at = $future }
451
+ seven_day = @{ used_percentage = 50; resets_at = $future7 }
452
+ }
453
+ } | ConvertTo-Json -Depth 5 -Compress
454
+ $payload | powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$env:USERPROFILE\.claude\statusline-command.ps1"
455
+ ```
456
+
457
+ ## Color Preview One-Liner
458
+
459
+ After install, output a one-liner the user can paste to preview all four quota tiers, with both 5h and 7d on each line, separated by ` | `, one tier per line.
460
+
461
+ ### Bash form
462
+
463
+ ```bash
464
+ printf '\033[38;2;78;186;101m5h:20%%(2h30m)\033[0m \033[0;37m|\033[0m \033[38;2;78;186;101m7d:25%%(3d12h)\033[0m\n\033[38;2;222;142;62m5h:55%%(2h30m)\033[0m \033[0;37m|\033[0m \033[38;2;222;142;62m7d:45%%(3d12h)\033[0m\n\033[38;2;234;88;12m5h:80%%(1h15m)\033[0m \033[0;37m|\033[0m \033[38;2;234;88;12m7d:75%%(2d3h)\033[0m\n\033[38;2;183;68;38m5h:95%%(0h30m)\033[0m \033[0;37m|\033[0m \033[38;2;183;68;38m7d:92%%(1d2h)\033[0m\n'
465
+ ```
466
+
467
+ ### PowerShell form
468
+
469
+ ```powershell
470
+ $e=[char]27; Write-Host "$e[38;2;78;186;101m5h:20%(2h30m)$e[0m $e[0;37m|$e[0m $e[38;2;78;186;101m7d:25%(3d12h)$e[0m`n$e[38;2;222;142;62m5h:55%(2h30m)$e[0m $e[0;37m|$e[0m $e[38;2;222;142;62m7d:45%(3d12h)$e[0m`n$e[38;2;234;88;12m5h:80%(1h15m)$e[0m $e[0;37m|$e[0m $e[38;2;234;88;12m7d:75%(2d3h)$e[0m`n$e[38;2;183;68;38m5h:95%(0h30m)$e[0m $e[0;37m|$e[0m $e[38;2;183;68;38m7d:92%(1d2h)$e[0m"
471
+ ```
472
+
473
+ ## Other Rules
474
+
475
+ - Do not add features beyond this spec.
476
+ - Do not auto-commit any changes.
477
+ - Do not add Co-Authored-By trailers.
478
+ - Read git branch with `git -C "$cwd" rev-parse --abbrev-ref HEAD`, never from `PWD`.
479
+ - When merging into existing `settings.json`, preserve all other keys; only set/replace `statusLine`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kurokeita/add-skill",
3
- "version": "1.18.0",
3
+ "version": "1.19.0",
4
4
  "description": "CLI to install AI agent skills to various platforms",
5
5
  "type": "module",
6
6
  "bin": {