@kurokeita/add-skill 2.0.0 → 2.1.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.
|
@@ -1,20 +1,67 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: statusline-setup
|
|
3
|
-
description: 'Set up the Claude Code or Gemini/Antigravity CLI statusline
|
|
3
|
+
description: 'Set up the Claude Code or Gemini/Antigravity CLI statusline as two side-by-side bordered boxes — a repo box (cwd, git branch, working-tree state, last commit) and a session box (model, context % + token totals, 5h/7d quota with ETA). Use when asked to "set up my statusline", "install statusline", "configure statusline", or to add a colored multi-line status line showing rate limits and token usage. Detects OS and installs the appropriate variant (bash + jq on Linux/macOS, PowerShell on Windows).'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Statusline Setup
|
|
7
7
|
|
|
8
|
-
Install a Claude Code or Gemini/Antigravity statusline that renders
|
|
8
|
+
Install a Claude Code or Gemini/Antigravity statusline that renders two side-by-side bordered boxes:
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
```text
|
|
11
|
+
╭─ repo ──────────────────────────────────────────────────────╮ ╭─ session ─────────────╮
|
|
12
|
+
│ ~/dev/ai_agents │ │ Opus 5 (high) │
|
|
13
|
+
│ feat/statusline-boxed-layout │ │ ctx 42% ↑1.2M ↓18.4k │
|
|
14
|
+
│ +2 ~5 ?1 ↑3 │ │ 5h 38% resets 2h 14m │
|
|
15
|
+
│ ffd30ce feat(skills): add iso-response plain language skill │ │ 7d 61% resets 3d 4h │
|
|
16
|
+
╰─────────────────────────────────────────────────────────────╯ ╰───────────────────────╯
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Box Contents
|
|
20
|
+
|
|
21
|
+
### repo box
|
|
22
|
+
|
|
23
|
+
1. **cwd** — 24-bit ANSI `#5EFFFF` (`38;2;94;255;255`), shown at **full length**; only `$HOME` / `%USERPROFILE%` is replaced with `~`. No component abbreviation.
|
|
24
|
+
2. **git branch** — 24-bit ANSI `#C24870` (`38;2;194;72;112`), bare name with no parentheses. Detached HEAD renders `detached@<short-sha>`.
|
|
25
|
+
3. **working-tree state** — `+<staged> ~<modified> -<deleted> ?<untracked>` in bright green `#4eba65`, followed by `↑<ahead> ↓<behind>` in dim `#787e8a`. A clean tree renders a dim `clean`. Each counter is omitted when zero.
|
|
26
|
+
4. **last commit** — `%h %s` in dim `#787e8a`, truncated to 80 characters with a trailing `…`.
|
|
27
|
+
|
|
28
|
+
Rows 2-4 appear only inside a git repo.
|
|
29
|
+
|
|
30
|
+
### session box
|
|
31
|
+
|
|
32
|
+
1. **model display name + effort** in parens — 24-bit ANSI `#E89440` (`38;2;232;148;64`), effort only when present.
|
|
33
|
+
2. **`ctx N%`** — 24-bit ANSI `#009AFB` (`38;2;0;154;251`) — followed on the same row, after two spaces, by **tokens** `↑<in> ↓<out>` in `#937bda` (`38;2;147;123;218`). When no token data exists the row is just `ctx N%`.
|
|
34
|
+
3. **`5h N% resets <eta>`** — colored by remaining quota (see below).
|
|
35
|
+
4. **`7d N% resets <eta>`** — colored independently of the 5h row.
|
|
36
|
+
|
|
37
|
+
## Box Rendering Rules
|
|
38
|
+
|
|
39
|
+
- Border glyphs `╭ ╮ ╰ ╯ ─ │` in `#5a5f69` (`38;2;90;95;105`); the box title in `#8c929e` (`38;2;140;146;158`).
|
|
40
|
+
- Each box's inner width is the longest of its content rows, or its title + 2, whichever is larger.
|
|
41
|
+
- Both boxes are padded to the **same row count** with blank rows, so the two frames start and end on the same terminal lines.
|
|
42
|
+
- The two boxes are joined by a single space on every line.
|
|
43
|
+
- Rows are emitted newline-separated; the statusline host renders them as multiple lines.
|
|
44
|
+
- When both boxes have zero content rows the script prints nothing and exits 0.
|
|
45
|
+
|
|
46
|
+
### Measuring width with ANSI escapes present
|
|
47
|
+
|
|
48
|
+
Build **two parallel arrays** per box: one holding the plain text, one holding the same text wrapped in color escapes. Measure padding from the plain array only — measuring the colored string counts the escape bytes and destroys alignment.
|
|
49
|
+
|
|
50
|
+
### UTF-8 locale guard (bash only)
|
|
16
51
|
|
|
17
|
-
|
|
52
|
+
`${#str}` counts **bytes**, not characters, unless the shell is in a UTF-8 locale. The rows contain `↑`, `↓` and `…`, so under `LC_ALL=C` every such row is padded 2 characters short. Guard at the top of the script with a subprocess-free probe:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
probe='↑'
|
|
56
|
+
if (( ${#probe} != 1 )); then
|
|
57
|
+
for loc in C.UTF-8 en_US.UTF-8 en_US.utf8; do
|
|
58
|
+
export LC_ALL="$loc"
|
|
59
|
+
(( ${#probe} == 1 )) && break
|
|
60
|
+
done
|
|
61
|
+
fi
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Assigning `LC_ALL` makes bash call `setlocale` immediately, so `${#probe}` re-evaluates inside the loop; a locale the system lacks leaves the previous value and the loop moves on.
|
|
18
65
|
|
|
19
66
|
## When to Use This Skill
|
|
20
67
|
|
|
@@ -37,12 +84,20 @@ Detect the operating system and targeted platform first, then write and wire the
|
|
|
37
84
|
- **Linux / macOS** → write `~/.gemini/antigravity-cli/statusline-command.sh` (pure bash + `jq`), wire into `~/.gemini/antigravity-cli/settings.json`.
|
|
38
85
|
- **Windows** → write `%USERPROFILE%\.gemini\antigravity-cli\statusline-command.ps1`, wire into `%USERPROFILE%\.gemini\antigravity-cli\settings.json`.
|
|
39
86
|
|
|
87
|
+
Multi-line rendering is verified on Claude Code. Antigravity's handling of multi-line statusline output is untested.
|
|
88
|
+
|
|
89
|
+
## Shell Compatibility
|
|
90
|
+
|
|
91
|
+
macOS ships bash 3.2, so the bash variant must avoid: `local -n` namerefs, `mapfile`/`readarray`, associative arrays, and negative array indices. Pass arrays into helpers as positional parameters instead.
|
|
92
|
+
|
|
93
|
+
The PowerShell variant targets Windows PowerShell 5.1, so avoid PowerShell 7 syntax: null-coalescing `??`, ternary `? :`, and chain operators `&&`/`||`.
|
|
94
|
+
|
|
40
95
|
## Token Formatting
|
|
41
96
|
|
|
42
97
|
- `< 1000` → raw integer (e.g. `↑850 ↓120`)
|
|
43
98
|
- `>= 1_000` → one decimal + `k` (e.g. `↑1.2k ↓34.5k`)
|
|
44
99
|
- `>= 1_000_000` → one decimal + `M` (e.g. `↑1.2M ↓3.4M`)
|
|
45
|
-
- Missing/unparseable transcript → omit the
|
|
100
|
+
- Missing/unparseable transcript → omit the tokens from the `ctx` row entirely, never render `↑0 ↓0`.
|
|
46
101
|
|
|
47
102
|
## Token Source
|
|
48
103
|
|
|
@@ -55,6 +110,17 @@ Tokens are calculated dynamically:
|
|
|
55
110
|
- `input` = `context_window.total_input_tokens`
|
|
56
111
|
- `output` = `context_window.total_output_tokens`
|
|
57
112
|
|
|
113
|
+
## Git State
|
|
114
|
+
|
|
115
|
+
Read **all** git state from a single `git -C "$cwd" status -b --porcelain` call, never from `PWD`. That one call yields branch name, upstream ahead/behind, and the staged/modified/untracked counts:
|
|
116
|
+
|
|
117
|
+
- The `##` header line carries `branch...upstream [ahead N, behind M]`, or the literal `HEAD (no branch)` when detached — fall back to `git -C "$cwd" rev-parse --short HEAD` for that case.
|
|
118
|
+
- `??` entries are untracked.
|
|
119
|
+
- For every other entry, column 1 is the index and column 2 the working tree. A `D` in either column counts as deleted; any other non-space counts as staged (column 1) or modified (column 2). One file can count in several buckets.
|
|
120
|
+
- Deletions get their own `-N` counter so a staged removal does not read as `+N`, which looks like an addition.
|
|
121
|
+
|
|
122
|
+
The last-commit row needs one further call: `git -C "$cwd" log -1 --format='%h %s'`.
|
|
123
|
+
|
|
58
124
|
## Quota Colors (by REMAINING quota = 100 - used%)
|
|
59
125
|
|
|
60
126
|
| Remaining | Color | 24-bit ANSI |
|
|
@@ -134,12 +200,25 @@ if ! command -v jq >/dev/null 2>&1; then
|
|
|
134
200
|
exit 0
|
|
135
201
|
fi
|
|
136
202
|
|
|
203
|
+
# ${#str} counts bytes outside a UTF-8 locale, which mis-pads every row holding
|
|
204
|
+
# an arrow or ellipsis. Assigning LC_ALL re-runs setlocale, so the probe below
|
|
205
|
+
# re-evaluates; an unavailable locale leaves the previous value untouched.
|
|
206
|
+
probe='↑'
|
|
207
|
+
if (( ${#probe} != 1 )); then
|
|
208
|
+
for loc in C.UTF-8 en_US.UTF-8 en_US.utf8; do
|
|
209
|
+
export LC_ALL="$loc"
|
|
210
|
+
(( ${#probe} == 1 )) && break
|
|
211
|
+
done
|
|
212
|
+
fi
|
|
213
|
+
|
|
137
214
|
C_CWD=$'\033[38;2;94;255;255m'
|
|
138
215
|
C_BRANCH=$'\033[38;2;194;72;112m'
|
|
139
|
-
|
|
216
|
+
C_BOX=$'\033[38;2;90;95;105m'
|
|
217
|
+
C_TITLE=$'\033[38;2;140;146;158m'
|
|
140
218
|
C_MODEL=$'\033[38;2;232;148;64m'
|
|
141
219
|
C_CTX=$'\033[38;2;0;154;251m'
|
|
142
220
|
C_TOK=$'\033[38;2;147;123;218m'
|
|
221
|
+
C_DIM=$'\033[38;2;120;126;138m'
|
|
143
222
|
RESET=$'\033[0m'
|
|
144
223
|
|
|
145
224
|
GREEN=$'\033[38;2;78;186;101m'
|
|
@@ -159,8 +238,9 @@ quota_color() {
|
|
|
159
238
|
|
|
160
239
|
fmt_eta() {
|
|
161
240
|
local resets="$1"
|
|
162
|
-
[[ -z "$resets" || "$resets" == "null"
|
|
241
|
+
[[ -z "$resets" || "$resets" == "null" ]] && { printf ''; return; }
|
|
163
242
|
local now delta days hours mins
|
|
243
|
+
# Claude Code sends an absolute epoch; Antigravity sends a duration.
|
|
164
244
|
if (( resets < 10000000 )); then
|
|
165
245
|
delta=$resets
|
|
166
246
|
else
|
|
@@ -188,13 +268,26 @@ fmt_tokens() {
|
|
|
188
268
|
fi
|
|
189
269
|
}
|
|
190
270
|
|
|
271
|
+
truncate_to() {
|
|
272
|
+
local s="$1" max="$2"
|
|
273
|
+
if (( ${#s} > max )); then printf '%s…' "${s:0:$(( max - 1 ))}"
|
|
274
|
+
else printf '%s' "$s"
|
|
275
|
+
fi
|
|
276
|
+
}
|
|
277
|
+
|
|
191
278
|
shorten_home() {
|
|
192
279
|
local p="$1"
|
|
193
280
|
[[ -z "$p" ]] && return
|
|
194
|
-
if [[ "$p" == "$HOME" ]]; then
|
|
195
|
-
elif [[ "$p" == "$HOME"/* ]]; then
|
|
196
|
-
else printf '%s' "$p"
|
|
281
|
+
if [[ "$p" == "$HOME" ]]; then p='~'
|
|
282
|
+
elif [[ "$p" == "$HOME"/* ]]; then p="~${p#$HOME}"
|
|
197
283
|
fi
|
|
284
|
+
printf '%s' "$p"
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
repeat_char() {
|
|
288
|
+
local ch="$1" n="$2" out=""
|
|
289
|
+
while (( n-- > 0 )); do out+="$ch"; done
|
|
290
|
+
printf '%s' "$out"
|
|
198
291
|
}
|
|
199
292
|
|
|
200
293
|
# --- read & parse stdin payload in a single jq call ---
|
|
@@ -210,67 +303,113 @@ read_fields() {
|
|
|
210
303
|
.effort.level // "",
|
|
211
304
|
(.context_window.used_percentage // "" | tostring),
|
|
212
305
|
(
|
|
213
|
-
.rate_limits.five_hour.used_percentage //
|
|
214
|
-
(if .quota."gemini-5h" != null then ((1.0 - .quota."gemini-5h".remaining_fraction) * 100)
|
|
215
|
-
elif .quota."3p-5h" != null then ((1.0 - .quota."3p-5h".remaining_fraction) * 100)
|
|
306
|
+
.rate_limits.five_hour.used_percentage //
|
|
307
|
+
(if .quota."gemini-5h" != null then ((1.0 - .quota."gemini-5h".remaining_fraction) * 100)
|
|
308
|
+
elif .quota."3p-5h" != null then ((1.0 - .quota."3p-5h".remaining_fraction) * 100)
|
|
216
309
|
else "" end)
|
|
217
310
|
| tostring
|
|
218
311
|
),
|
|
219
312
|
(
|
|
220
|
-
.rate_limits.five_hour.resets_at //
|
|
221
|
-
.quota."gemini-5h".reset_in_seconds //
|
|
222
|
-
.quota."3p-5h".reset_in_seconds //
|
|
313
|
+
.rate_limits.five_hour.resets_at //
|
|
314
|
+
.quota."gemini-5h".reset_in_seconds //
|
|
315
|
+
.quota."3p-5h".reset_in_seconds //
|
|
223
316
|
""
|
|
224
317
|
| tostring
|
|
225
318
|
),
|
|
226
319
|
(
|
|
227
|
-
.rate_limits.seven_day.used_percentage //
|
|
228
|
-
(if .quota."gemini-weekly" != null then ((1.0 - .quota."gemini-weekly".remaining_fraction) * 100)
|
|
229
|
-
elif .quota."3p-weekly" != null then ((1.0 - .quota."3p-weekly".remaining_fraction) * 100)
|
|
320
|
+
.rate_limits.seven_day.used_percentage //
|
|
321
|
+
(if .quota."gemini-weekly" != null then ((1.0 - .quota."gemini-weekly".remaining_fraction) * 100)
|
|
322
|
+
elif .quota."3p-weekly" != null then ((1.0 - .quota."3p-weekly".remaining_fraction) * 100)
|
|
230
323
|
else "" end)
|
|
231
324
|
| tostring
|
|
232
325
|
),
|
|
233
326
|
(
|
|
234
|
-
.rate_limits.seven_day.resets_at //
|
|
235
|
-
.quota."gemini-weekly".reset_in_seconds //
|
|
236
|
-
.quota."3p-weekly".reset_in_seconds //
|
|
327
|
+
.rate_limits.seven_day.resets_at //
|
|
328
|
+
.quota."gemini-weekly".reset_in_seconds //
|
|
329
|
+
.quota."3p-weekly".reset_in_seconds //
|
|
237
330
|
""
|
|
238
331
|
| tostring
|
|
239
332
|
),
|
|
240
333
|
(.context_window.total_input_tokens // "" | tostring),
|
|
241
334
|
(.context_window.total_output_tokens // "" | tostring)
|
|
242
|
-
] | join("
|
|
335
|
+
] | join("")
|
|
243
336
|
' 2>/dev/null <<<"$payload"
|
|
244
337
|
}
|
|
245
338
|
|
|
246
339
|
IFS=$'\x1f' read -r cwd transcript model effort ctx five_used five_reset seven_used seven_reset payload_in payload_out < <(read_fields)
|
|
247
340
|
|
|
248
|
-
|
|
341
|
+
repo_plain=(); repo_color=()
|
|
249
342
|
|
|
250
343
|
if [[ -n "$cwd" ]]; then
|
|
251
|
-
|
|
344
|
+
p=$(shorten_home "$cwd")
|
|
345
|
+
repo_plain+=("$p"); repo_color+=("${C_CWD}${p}${RESET}")
|
|
252
346
|
fi
|
|
253
347
|
|
|
254
|
-
#
|
|
255
|
-
|
|
348
|
+
# One `status -b --porcelain` call yields branch, dirty counts and ahead/behind.
|
|
349
|
+
# Must use git -C "$cwd", never PWD.
|
|
350
|
+
branch=""; staged=0; modified=0; deleted=0; untracked=0; ahead=0; behind=0
|
|
256
351
|
if [[ -n "$cwd" ]]; then
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
352
|
+
while IFS= read -r line; do
|
|
353
|
+
if [[ "$line" == '## '* ]]; then
|
|
354
|
+
head=${line#'## '}
|
|
355
|
+
if [[ "$head" == 'HEAD (no branch)' ]]; then
|
|
356
|
+
branch=$(git -C "$cwd" rev-parse --short HEAD 2>/dev/null)
|
|
357
|
+
[[ -n "$branch" ]] && branch="detached@${branch}"
|
|
358
|
+
else
|
|
359
|
+
branch=${head%%...*}
|
|
360
|
+
if [[ "$head" == *'[ahead '* ]]; then t=${head#*'[ahead '}; ahead=${t%%[,\]]*}; fi
|
|
361
|
+
if [[ "$head" == *'behind '* ]]; then t=${head#*'behind '}; behind=${t%%[,\]]*}; fi
|
|
362
|
+
fi
|
|
363
|
+
elif [[ "$line" == '??'* ]]; then
|
|
364
|
+
(( ++untracked ))
|
|
365
|
+
else
|
|
366
|
+
case "${line:0:1}" in
|
|
367
|
+
' ') ;;
|
|
368
|
+
'D') (( ++deleted )) ;;
|
|
369
|
+
*) (( ++staged )) ;;
|
|
370
|
+
esac
|
|
371
|
+
case "${line:1:1}" in
|
|
372
|
+
' ') ;;
|
|
373
|
+
'D') (( ++deleted )) ;;
|
|
374
|
+
*) (( ++modified )) ;;
|
|
375
|
+
esac
|
|
376
|
+
fi
|
|
377
|
+
done < <(git -C "$cwd" status -b --porcelain 2>/dev/null)
|
|
261
378
|
fi
|
|
262
379
|
|
|
263
|
-
if [[ -n "$
|
|
264
|
-
|
|
265
|
-
|
|
380
|
+
if [[ -n "$branch" ]]; then
|
|
381
|
+
repo_plain+=("$branch"); repo_color+=("${C_BRANCH}${branch}${RESET}")
|
|
382
|
+
|
|
383
|
+
if (( staged || modified || deleted || untracked )); then
|
|
384
|
+
dirty=""
|
|
385
|
+
(( staged )) && dirty+="+${staged} "
|
|
386
|
+
(( modified )) && dirty+="~${modified} "
|
|
387
|
+
(( deleted )) && dirty+="-${deleted} "
|
|
388
|
+
(( untracked )) && dirty+="?${untracked} "
|
|
389
|
+
dirty=${dirty% }
|
|
390
|
+
dirty_c="${GREEN}${dirty}${RESET}"
|
|
266
391
|
else
|
|
267
|
-
|
|
392
|
+
dirty="clean"; dirty_c="${C_DIM}clean${RESET}"
|
|
393
|
+
fi
|
|
394
|
+
sync=""
|
|
395
|
+
(( ahead )) && sync+=" ↑${ahead}"
|
|
396
|
+
(( behind )) && sync+=" ↓${behind}"
|
|
397
|
+
repo_plain+=("${dirty}${sync}")
|
|
398
|
+
repo_color+=("${dirty_c}${C_DIM}${sync}${RESET}")
|
|
399
|
+
|
|
400
|
+
last=$(git -C "$cwd" log -1 --format='%h %s' 2>/dev/null)
|
|
401
|
+
if [[ -n "$last" ]]; then
|
|
402
|
+
last=$(truncate_to "$last" 80)
|
|
403
|
+
repo_plain+=("$last"); repo_color+=("${C_DIM}${last}${RESET}")
|
|
268
404
|
fi
|
|
269
405
|
fi
|
|
270
406
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
407
|
+
sess_plain=(); sess_color=()
|
|
408
|
+
|
|
409
|
+
if [[ -n "$model" ]]; then
|
|
410
|
+
m="$model"
|
|
411
|
+
[[ -n "$effort" ]] && m="${model} (${effort})"
|
|
412
|
+
sess_plain+=("$m"); sess_color+=("${C_MODEL}${m}${RESET}")
|
|
274
413
|
fi
|
|
275
414
|
|
|
276
415
|
ti=0
|
|
@@ -288,7 +427,7 @@ if [[ -n "$transcript" ]]; then
|
|
|
288
427
|
fi
|
|
289
428
|
|
|
290
429
|
if [[ -n "$transcript" && -f "$transcript" ]]; then
|
|
291
|
-
tok=$(jq -
|
|
430
|
+
tok=$(jq -Rrn '
|
|
292
431
|
reduce (inputs | fromjson?) as $m ({i:0,o:0};
|
|
293
432
|
($m.message.usage // null) as $u
|
|
294
433
|
| if $u == null then .
|
|
@@ -312,34 +451,82 @@ if (( ti == 0 && to == 0 )); then
|
|
|
312
451
|
fi
|
|
313
452
|
fi
|
|
314
453
|
|
|
454
|
+
tokens=""
|
|
315
455
|
if (( ti > 0 || to > 0 )); then
|
|
316
|
-
|
|
456
|
+
tokens="↑$(fmt_tokens "$ti") ↓$(fmt_tokens "$to")"
|
|
457
|
+
fi
|
|
458
|
+
|
|
459
|
+
if [[ -n "$ctx" ]]; then
|
|
460
|
+
ctx_int=$(awk -v n="$ctx" 'BEGIN{printf "%d", (n+0.5)}')
|
|
461
|
+
c="ctx ${ctx_int}%"
|
|
462
|
+
c_col="${C_CTX}${c}${RESET}"
|
|
463
|
+
if [[ -n "$tokens" ]]; then
|
|
464
|
+
c+=" ${tokens}"; c_col+=" ${C_TOK}${tokens}${RESET}"
|
|
465
|
+
fi
|
|
466
|
+
sess_plain+=("$c"); sess_color+=("$c_col")
|
|
467
|
+
elif [[ -n "$tokens" ]]; then
|
|
468
|
+
sess_plain+=("$tokens"); sess_color+=("${C_TOK}${tokens}${RESET}")
|
|
317
469
|
fi
|
|
318
470
|
|
|
319
471
|
if [[ -n "$five_used" ]]; then
|
|
320
472
|
fu=$(awk -v n="$five_used" 'BEGIN{printf "%d", (n+0.5)}')
|
|
321
473
|
eta=$(fmt_eta "$five_reset")
|
|
322
|
-
|
|
474
|
+
s="5h ${fu}%"; [[ -n "$eta" ]] && s+=" resets ${eta}"
|
|
475
|
+
sess_plain+=("$s"); sess_color+=("$(quota_color "$fu")${s}${RESET}")
|
|
323
476
|
fi
|
|
324
477
|
if [[ -n "$seven_used" ]]; then
|
|
325
478
|
su=$(awk -v n="$seven_used" 'BEGIN{printf "%d", (n+0.5)}')
|
|
326
479
|
eta=$(fmt_eta "$seven_reset")
|
|
327
|
-
|
|
480
|
+
s="7d ${su}%"; [[ -n "$eta" ]] && s+=" resets ${eta}"
|
|
481
|
+
sess_plain+=("$s"); sess_color+=("$(quota_color "$su")${s}${RESET}")
|
|
328
482
|
fi
|
|
329
483
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
484
|
+
rows=${#repo_plain[@]}
|
|
485
|
+
(( ${#sess_plain[@]} > rows )) && rows=${#sess_plain[@]}
|
|
486
|
+
if (( rows == 0 )); then printf ''; exit 0; fi
|
|
487
|
+
for (( i = ${#repo_plain[@]}; i < rows; i++ )); do repo_plain+=(""); repo_color+=(""); done
|
|
488
|
+
for (( i = ${#sess_plain[@]}; i < rows; i++ )); do sess_plain+=(""); sess_color+=(""); done
|
|
489
|
+
|
|
490
|
+
# Width comes from the plain rows; the parallel colored rows carry ANSI escapes
|
|
491
|
+
# that would otherwise be counted as visible characters. Arrays are passed
|
|
492
|
+
# positionally because bash 3.2 (macOS) has no `local -n` nameref.
|
|
493
|
+
box_width() {
|
|
494
|
+
local title="$1"; shift
|
|
495
|
+
local w=$(( ${#title} + 2 )) s
|
|
496
|
+
for s in "$@"; do (( ${#s} > w )) && w=${#s}; done
|
|
497
|
+
printf '%d' "$w"
|
|
498
|
+
}
|
|
499
|
+
rw=$(box_width "repo" "${repo_plain[@]}")
|
|
500
|
+
sw=$(box_width "session" "${sess_plain[@]}")
|
|
501
|
+
|
|
502
|
+
box_top() {
|
|
503
|
+
local title="$1" w="$2"
|
|
504
|
+
printf '%s╭─ %s%s%s %s╮%s' "$C_BOX" "$C_TITLE" "$title" "$C_BOX" \
|
|
505
|
+
"$(repeat_char '─' $(( w - ${#title} - 1 )))" "$RESET"
|
|
506
|
+
}
|
|
507
|
+
box_bottom() {
|
|
508
|
+
printf '%s╰%s╯%s' "$C_BOX" "$(repeat_char '─' $(( $1 + 2 )))" "$RESET"
|
|
509
|
+
}
|
|
510
|
+
box_row() {
|
|
511
|
+
local colored="$1" w="$2" plain="$3"
|
|
512
|
+
printf '%s│%s %s%s %s│%s' "$C_BOX" "$RESET" "$colored" \
|
|
513
|
+
"$(repeat_char ' ' $(( w - ${#plain} )))" "$C_BOX" "$RESET"
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
out="$(box_top repo "$rw") $(box_top session "$sw")"
|
|
517
|
+
for (( i = 0; i < rows; i++ )); do
|
|
518
|
+
out+=$'\n'
|
|
519
|
+
out+="$(box_row "${repo_color[$i]}" "$rw" "${repo_plain[$i]}")"
|
|
520
|
+
out+=" $(box_row "${sess_color[$i]}" "$sw" "${sess_plain[$i]}")"
|
|
336
521
|
done
|
|
522
|
+
out+=$'\n'"$(box_bottom "$rw") $(box_bottom "$sw")"
|
|
523
|
+
|
|
337
524
|
printf '%s' "$out"
|
|
338
525
|
```
|
|
339
526
|
|
|
340
527
|
## Windows Implementation
|
|
341
528
|
|
|
342
|
-
Write `statusline-command.ps1` to the targeted platform directory. 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.
|
|
529
|
+
Write `statusline-command.ps1` to the targeted platform directory. 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, and set UTF-8 output encoding so the box-drawing glyphs survive.
|
|
343
530
|
|
|
344
531
|
`statusline-command.ps1`:
|
|
345
532
|
|
|
@@ -351,10 +538,12 @@ $ESC = [char]27
|
|
|
351
538
|
|
|
352
539
|
$C_CWD = "$ESC[38;2;94;255;255m"
|
|
353
540
|
$C_BRANCH = "$ESC[38;2;194;72;112m"
|
|
354
|
-
$
|
|
541
|
+
$C_BOX = "$ESC[38;2;90;95;105m"
|
|
542
|
+
$C_TITLE = "$ESC[38;2;140;146;158m"
|
|
355
543
|
$C_MODEL = "$ESC[38;2;232;148;64m"
|
|
356
544
|
$C_CTX = "$ESC[38;2;0;154;251m"
|
|
357
545
|
$C_TOK = "$ESC[38;2;147;123;218m"
|
|
546
|
+
$C_DIM = "$ESC[38;2;120;126;138m"
|
|
358
547
|
$RESET = "$ESC[0m"
|
|
359
548
|
|
|
360
549
|
$GREEN = "$ESC[38;2;78;186;101m"
|
|
@@ -362,6 +551,10 @@ $AMBER = "$ESC[38;2;222;142;62m"
|
|
|
362
551
|
$ORANGE = "$ESC[38;2;234;88;12m"
|
|
363
552
|
$REDORG = "$ESC[38;2;183;68;38m"
|
|
364
553
|
|
|
554
|
+
$TL = [char]0x256D; $TR = [char]0x256E; $BL = [char]0x2570; $BR = [char]0x256F
|
|
555
|
+
$HZ = [char]0x2500; $VT = [char]0x2502
|
|
556
|
+
$UP = [char]0x2191; $DN = [char]0x2193; $ELLIPSIS = [char]0x2026
|
|
557
|
+
|
|
365
558
|
function QuotaColor([double]$used) {
|
|
366
559
|
$remaining = 100 - $used
|
|
367
560
|
if ($remaining -gt 60) { return $GREEN }
|
|
@@ -373,6 +566,7 @@ function QuotaColor([double]$used) {
|
|
|
373
566
|
function FmtEta($resetsAt) {
|
|
374
567
|
if ($null -eq $resetsAt -or $resetsAt -eq "") { return "" }
|
|
375
568
|
$val = [int64]$resetsAt
|
|
569
|
+
# Claude Code sends an absolute epoch; Antigravity sends a duration.
|
|
376
570
|
if ($val -lt 10000000) {
|
|
377
571
|
$delta = $val
|
|
378
572
|
} else {
|
|
@@ -393,23 +587,19 @@ function FmtTokens([int64]$n) {
|
|
|
393
587
|
return ("{0:N1}M" -f ($n / 1000000.0))
|
|
394
588
|
}
|
|
395
589
|
|
|
590
|
+
function TruncateTo($s, [int]$max) {
|
|
591
|
+
if ($s.Length -gt $max) { return $s.Substring(0, $max - 1) + $ELLIPSIS }
|
|
592
|
+
return $s
|
|
593
|
+
}
|
|
594
|
+
|
|
396
595
|
function ShortenHome($p) {
|
|
397
596
|
if (-not $p) { return "" }
|
|
398
|
-
$
|
|
399
|
-
if ($p -eq $
|
|
400
|
-
if ($p.StartsWith("$
|
|
597
|
+
$userHome = $env:USERPROFILE
|
|
598
|
+
if ($p -eq $userHome) { return "~" }
|
|
599
|
+
if ($p.StartsWith("$userHome\")) { return "~" + $p.Substring($userHome.Length) }
|
|
401
600
|
return $p
|
|
402
601
|
}
|
|
403
602
|
|
|
404
|
-
function GetBranch($cwd) {
|
|
405
|
-
if (-not $cwd) { return $null }
|
|
406
|
-
try {
|
|
407
|
-
$b = & git -C "$cwd" rev-parse --abbrev-ref HEAD 2>$null
|
|
408
|
-
if ($LASTEXITCODE -eq 0 -and $b) { return $b.Trim() }
|
|
409
|
-
} catch {}
|
|
410
|
-
return $null
|
|
411
|
-
}
|
|
412
|
-
|
|
413
603
|
function SumTokens($path) {
|
|
414
604
|
if (-not $path) { return $null }
|
|
415
605
|
# Resolve path mismatch (if it has .gemini/antigravity/ but it actually is at .gemini/antigravity-cli/)
|
|
@@ -443,30 +633,84 @@ function SumTokens($path) {
|
|
|
443
633
|
$raw = [Console]::In.ReadToEnd()
|
|
444
634
|
try { $data = if ($raw.Trim()) { $raw | ConvertFrom-Json } else { @{} } } catch { $data = @{} }
|
|
445
635
|
|
|
446
|
-
$
|
|
636
|
+
$repoPlain = @(); $repoColor = @()
|
|
637
|
+
$sessPlain = @(); $sessColor = @()
|
|
447
638
|
|
|
448
639
|
$cwd = $data.cwd
|
|
449
|
-
if ($cwd) {
|
|
640
|
+
if ($cwd) {
|
|
641
|
+
$p = ShortenHome $cwd
|
|
642
|
+
$repoPlain += $p; $repoColor += "$C_CWD$p$RESET"
|
|
643
|
+
}
|
|
450
644
|
|
|
451
|
-
|
|
452
|
-
|
|
645
|
+
# One `status -b --porcelain` call yields branch, dirty counts and ahead/behind.
|
|
646
|
+
# Must use git -C "$cwd", never PWD.
|
|
647
|
+
$branch = ""; $staged = 0; $modified = 0; $deleted = 0; $untracked = 0; $ahead = 0; $behind = 0
|
|
648
|
+
if ($cwd) {
|
|
649
|
+
$statusLines = & git -C "$cwd" status -b --porcelain 2>$null
|
|
650
|
+
if ($LASTEXITCODE -eq 0 -and $statusLines) {
|
|
651
|
+
foreach ($line in @($statusLines)) {
|
|
652
|
+
if ($line.StartsWith("## ")) {
|
|
653
|
+
$head = $line.Substring(3)
|
|
654
|
+
if ($head -eq "HEAD (no branch)") {
|
|
655
|
+
$sha = & git -C "$cwd" rev-parse --short HEAD 2>$null
|
|
656
|
+
if ($sha) { $branch = "detached@" + $sha.Trim() }
|
|
657
|
+
} else {
|
|
658
|
+
$branch = ($head -split '\.\.\.')[0]
|
|
659
|
+
if ($head -match '\[ahead (\d+)') { $ahead = [int]$Matches[1] }
|
|
660
|
+
if ($head -match 'behind (\d+)') { $behind = [int]$Matches[1] }
|
|
661
|
+
}
|
|
662
|
+
} elseif ($line.StartsWith("??")) {
|
|
663
|
+
$untracked++
|
|
664
|
+
} else {
|
|
665
|
+
$idx = $line.Substring(0, 1)
|
|
666
|
+
if ($idx -eq "D") { $deleted++ } elseif ($idx -ne " ") { $staged++ }
|
|
667
|
+
$wt = $line.Substring(1, 1)
|
|
668
|
+
if ($wt -eq "D") { $deleted++ } elseif ($wt -ne " ") { $modified++ }
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
if ($branch) {
|
|
675
|
+
$repoPlain += $branch; $repoColor += "$C_BRANCH$branch$RESET"
|
|
676
|
+
|
|
677
|
+
if ($staged -gt 0 -or $modified -gt 0 -or $deleted -gt 0 -or $untracked -gt 0) {
|
|
678
|
+
$parts = @()
|
|
679
|
+
if ($staged -gt 0) { $parts += "+$staged" }
|
|
680
|
+
if ($modified -gt 0) { $parts += "~$modified" }
|
|
681
|
+
if ($deleted -gt 0) { $parts += "-$deleted" }
|
|
682
|
+
if ($untracked -gt 0) { $parts += "?$untracked" }
|
|
683
|
+
$dirty = [string]::Join(" ", $parts)
|
|
684
|
+
$dirtyColored = "$GREEN$dirty$RESET"
|
|
685
|
+
} else {
|
|
686
|
+
$dirty = "clean"; $dirtyColored = "$C_DIM" + "clean$RESET"
|
|
687
|
+
}
|
|
688
|
+
$sync = ""
|
|
689
|
+
if ($ahead -gt 0) { $sync += " $UP$ahead" }
|
|
690
|
+
if ($behind -gt 0) { $sync += " $DN$behind" }
|
|
691
|
+
$repoPlain += "$dirty$sync"
|
|
692
|
+
$repoColor += "$dirtyColored$C_DIM$sync$RESET"
|
|
693
|
+
|
|
694
|
+
$last = & git -C "$cwd" log -1 --format='%h %s' 2>$null
|
|
695
|
+
if ($last) {
|
|
696
|
+
$last = TruncateTo $last.Trim() 80
|
|
697
|
+
$repoPlain += $last; $repoColor += "$C_DIM$last$RESET"
|
|
698
|
+
}
|
|
699
|
+
}
|
|
453
700
|
|
|
454
701
|
$model = $data.model.display_name
|
|
455
702
|
$effort = $data.effort.level
|
|
456
703
|
if ($model) {
|
|
457
|
-
if ($effort) { $
|
|
458
|
-
|
|
704
|
+
if ($effort) { $m = "$model ($effort)" } else { $m = "$model" }
|
|
705
|
+
$sessPlain += $m; $sessColor += "$C_MODEL$m$RESET"
|
|
459
706
|
}
|
|
460
707
|
|
|
461
|
-
$
|
|
462
|
-
if ($null -ne $ctx) { $segments += "$C_CTX" + "ctx " + [int][math]::Round([double]$ctx) + "%$RESET" }
|
|
463
|
-
|
|
464
|
-
$tokens = SumTokens $data.transcript_path
|
|
708
|
+
$tokenSums = SumTokens $data.transcript_path
|
|
465
709
|
$ti = 0
|
|
466
710
|
$to = 0
|
|
467
|
-
if ($null -ne $
|
|
468
|
-
$ti = $
|
|
469
|
-
$to = $
|
|
711
|
+
if ($null -ne $tokenSums) {
|
|
712
|
+
$ti = $tokenSums[0]
|
|
713
|
+
$to = $tokenSums[1]
|
|
470
714
|
}
|
|
471
715
|
|
|
472
716
|
# Fallback to payload context_window tokens if transcript sum is 0
|
|
@@ -477,8 +721,22 @@ if ($ti -eq 0 -and $to -eq 0) {
|
|
|
477
721
|
}
|
|
478
722
|
}
|
|
479
723
|
|
|
724
|
+
$tokens = ""
|
|
480
725
|
if ($ti -gt 0 -or $to -gt 0) {
|
|
481
|
-
$
|
|
726
|
+
$tokens = "$UP" + (FmtTokens $ti) + " $DN" + (FmtTokens $to)
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
$ctx = $data.context_window.used_percentage
|
|
730
|
+
if ($null -ne $ctx) {
|
|
731
|
+
$c = "ctx " + [int][math]::Round([double]$ctx) + "%"
|
|
732
|
+
$cColored = "$C_CTX$c$RESET"
|
|
733
|
+
if ($tokens) {
|
|
734
|
+
$c += " $tokens"
|
|
735
|
+
$cColored += " $C_TOK$tokens$RESET"
|
|
736
|
+
}
|
|
737
|
+
$sessPlain += $c; $sessColor += $cColored
|
|
738
|
+
} elseif ($tokens) {
|
|
739
|
+
$sessPlain += $tokens; $sessColor += "$C_TOK$tokens$RESET"
|
|
482
740
|
}
|
|
483
741
|
|
|
484
742
|
$five_used = $null
|
|
@@ -509,14 +767,53 @@ if ($null -ne $data.rate_limits.seven_day.used_percentage) {
|
|
|
509
767
|
}
|
|
510
768
|
|
|
511
769
|
if ($null -ne $five_used) {
|
|
512
|
-
$
|
|
770
|
+
$s = "5h " + [int][math]::Round($five_used) + "%"
|
|
771
|
+
$eta = FmtEta $five_reset
|
|
772
|
+
if ($eta) { $s += " resets $eta" }
|
|
773
|
+
$sessPlain += $s; $sessColor += "$(QuotaColor $five_used)$s$RESET"
|
|
513
774
|
}
|
|
514
775
|
if ($null -ne $seven_used) {
|
|
515
|
-
$
|
|
776
|
+
$s = "7d " + [int][math]::Round($seven_used) + "%"
|
|
777
|
+
$eta = FmtEta $seven_reset
|
|
778
|
+
if ($eta) { $s += " resets $eta" }
|
|
779
|
+
$sessPlain += $s; $sessColor += "$(QuotaColor $seven_used)$s$RESET"
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
$rows = [math]::Max($repoPlain.Count, $sessPlain.Count)
|
|
783
|
+
if ($rows -eq 0) { exit 0 }
|
|
784
|
+
while ($repoPlain.Count -lt $rows) { $repoPlain += ""; $repoColor += "" }
|
|
785
|
+
while ($sessPlain.Count -lt $rows) { $sessPlain += ""; $sessColor += "" }
|
|
786
|
+
|
|
787
|
+
# Width comes from the plain rows; the parallel colored rows carry ANSI escapes
|
|
788
|
+
# that would otherwise be counted as visible characters.
|
|
789
|
+
function BoxWidth($title, $plainRows) {
|
|
790
|
+
$w = $title.Length + 2
|
|
791
|
+
foreach ($s in $plainRows) { if ($s.Length -gt $w) { $w = $s.Length } }
|
|
792
|
+
return $w
|
|
793
|
+
}
|
|
794
|
+
$rw = BoxWidth "repo" $repoPlain
|
|
795
|
+
$sw = BoxWidth "session" $sessPlain
|
|
796
|
+
|
|
797
|
+
function BoxTop($title, [int]$w) {
|
|
798
|
+
$dashes = [string]$HZ * ($w - $title.Length - 1)
|
|
799
|
+
return "$C_BOX$TL$HZ $C_TITLE$title$C_BOX $dashes$TR$RESET"
|
|
800
|
+
}
|
|
801
|
+
function BoxBottom([int]$w) {
|
|
802
|
+
return "$C_BOX$BL" + ([string]$HZ * ($w + 2)) + "$BR$RESET"
|
|
516
803
|
}
|
|
804
|
+
function BoxRow($colored, [int]$w, $plain) {
|
|
805
|
+
$pad = " " * ($w - $plain.Length)
|
|
806
|
+
return "$C_BOX$VT$RESET $colored$pad $C_BOX$VT$RESET"
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
$lines = @()
|
|
810
|
+
$lines += (BoxTop "repo" $rw) + " " + (BoxTop "session" $sw)
|
|
811
|
+
for ($i = 0; $i -lt $rows; $i++) {
|
|
812
|
+
$lines += (BoxRow $repoColor[$i] $rw $repoPlain[$i]) + " " + (BoxRow $sessColor[$i] $sw $sessPlain[$i])
|
|
813
|
+
}
|
|
814
|
+
$lines += (BoxBottom $rw) + " " + (BoxBottom $sw)
|
|
517
815
|
|
|
518
|
-
|
|
519
|
-
[Console]::Out.Write([string]::Join($sep, $segments))
|
|
816
|
+
[Console]::Out.Write([string]::Join("`n", $lines))
|
|
520
817
|
```
|
|
521
818
|
|
|
522
819
|
## Settings Wiring
|
|
@@ -554,12 +851,16 @@ Wire into `~/.gemini/antigravity-cli/settings.json` (or `%USERPROFILE%\.gemini\a
|
|
|
554
851
|
|
|
555
852
|
## Smoke Test
|
|
556
853
|
|
|
557
|
-
After installing,
|
|
854
|
+
After installing, pipe a fake payload into the script and confirm:
|
|
558
855
|
|
|
559
|
-
-
|
|
560
|
-
-
|
|
561
|
-
-
|
|
562
|
-
-
|
|
856
|
+
- **Every rendered line has the same character count** — this is the alignment check, and it fails first when width is measured off a colored string or the locale is not UTF-8.
|
|
857
|
+
- Both boxes show the same number of rows, framed top and bottom on the same lines.
|
|
858
|
+
- Inside a repo the repo box shows 4 rows: full cwd, branch, working-tree state, last commit.
|
|
859
|
+
- Staging a deletion (`git rm`) renders `-N`, not `+N`.
|
|
860
|
+
- 5h renders **orange** at 80% used, 7d renders **amber** at 50% used.
|
|
861
|
+
- The tokens sum matches the fixture and sits on the `ctx` row.
|
|
862
|
+
- Omitting `transcript_path` leaves the `ctx` row as a bare `ctx N%` — no `↑0 ↓0`, no stray spacing.
|
|
863
|
+
- Outside a repo the repo box collapses to the cwd row alone and the session box pads with blank rows.
|
|
563
864
|
|
|
564
865
|
### Linux/macOS smoke test
|
|
565
866
|
|
|
@@ -567,13 +868,31 @@ After installing, generate a fake JSON payload with 5h at 80% used, 7d at 50% us
|
|
|
567
868
|
mkdir -p /tmp/statusline-smoke
|
|
568
869
|
cat >/tmp/statusline-smoke/transcript.jsonl <<'EOF'
|
|
569
870
|
{"type":"assistant","message":{"role":"assistant","usage":{"input_tokens":100,"cache_creation_input_tokens":200,"cache_read_input_tokens":300,"output_tokens":50}}}
|
|
871
|
+
not json at all - must be skipped, not fatal
|
|
570
872
|
{"type":"assistant","message":{"role":"assistant","usage":{"input_tokens":10,"cache_creation_input_tokens":20,"cache_read_input_tokens":30,"output_tokens":5}}}
|
|
571
873
|
EOF
|
|
572
874
|
FUTURE=$(($(date +%s) + 7200))
|
|
573
875
|
FUTURE7=$(($(date +%s) + 3*86400))
|
|
574
|
-
|
|
876
|
+
|
|
877
|
+
# Claude Code payload
|
|
878
|
+
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}}}' "$PWD" "$FUTURE" "$FUTURE7" | bash ~/.claude/statusline-command.sh; echo
|
|
879
|
+
|
|
880
|
+
# Antigravity payload - exercises the quota/reset_in_seconds and token fallbacks
|
|
881
|
+
printf '{"cwd":"%s","model":{"display_name":"Gemini 3.5 Flash"},"context_window":{"used_percentage":6.7,"total_input_tokens":70419,"total_output_tokens":16667},"quota":{"gemini-5h":{"remaining_fraction":0.87,"reset_in_seconds":2515},"gemini-weekly":{"remaining_fraction":0.97,"reset_in_seconds":589315}}}' "$PWD" | bash ~/.claude/statusline-command.sh; echo
|
|
575
882
|
```
|
|
576
883
|
|
|
884
|
+
Assert equal line widths mechanically:
|
|
885
|
+
|
|
886
|
+
```bash
|
|
887
|
+
printf '{"cwd":"%s","transcript_path":"/tmp/statusline-smoke/transcript.jsonl","model":{"display_name":"Opus 4.7"},"context_window":{"used_percentage":42}}' "$PWD" \
|
|
888
|
+
| bash ~/.claude/statusline-command.sh \
|
|
889
|
+
| perl -CSD -pe 's/\e\[[0-9;]*m//g' \
|
|
890
|
+
| perl -CSD -ne 'chomp; print length($_), "\n"' \
|
|
891
|
+
| sort -u
|
|
892
|
+
```
|
|
893
|
+
|
|
894
|
+
That must print exactly one number. Re-run the same pipeline wrapped in `env -i PATH="$PATH" HOME="$HOME" bash -c '...'` to confirm the locale guard holds with no `LANG` set.
|
|
895
|
+
|
|
577
896
|
### Windows smoke test (PowerShell)
|
|
578
897
|
|
|
579
898
|
```powershell
|
|
@@ -586,7 +905,7 @@ New-Item -ItemType Directory -Force -Path $tmp | Out-Null
|
|
|
586
905
|
$future = [int64](Get-Date -UFormat %s) + 7200
|
|
587
906
|
$future7 = [int64](Get-Date -UFormat %s) + 3*86400
|
|
588
907
|
$payload = @{
|
|
589
|
-
cwd = $
|
|
908
|
+
cwd = $PWD.Path
|
|
590
909
|
transcript_path = "$tmp\transcript.jsonl"
|
|
591
910
|
model = @{ display_name = "Opus 4.7" }
|
|
592
911
|
effort = @{ level = "medium" }
|
|
@@ -596,23 +915,27 @@ $payload = @{
|
|
|
596
915
|
seven_day = @{ used_percentage = 50; resets_at = $future7 }
|
|
597
916
|
}
|
|
598
917
|
} | ConvertTo-Json -Depth 5 -Compress
|
|
599
|
-
$payload | powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$env:USERPROFILE\.claude\statusline-command.ps1"
|
|
918
|
+
$out = $payload | powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$env:USERPROFILE\.claude\statusline-command.ps1"
|
|
919
|
+
$out
|
|
920
|
+
($out -split "`n" | ForEach-Object { ($_ -replace "$([char]27)\[[0-9;]*m", "").Length } | Sort-Object -Unique)
|
|
600
921
|
```
|
|
601
922
|
|
|
923
|
+
The last line must print exactly one number.
|
|
924
|
+
|
|
602
925
|
## Color Preview One-Liner
|
|
603
926
|
|
|
604
|
-
After install, output a one-liner the user can paste to preview all four quota tiers,
|
|
927
|
+
After install, output a one-liner the user can paste to preview all four quota tiers, one tier per line, in the row format the session box uses.
|
|
605
928
|
|
|
606
929
|
### Bash form
|
|
607
930
|
|
|
608
931
|
```bash
|
|
609
|
-
printf '\033[38;2;78;186;101m5h
|
|
932
|
+
printf '\033[38;2;78;186;101m5h 20%% resets 2h 30m\033[0m\n\033[38;2;222;142;62m5h 55%% resets 2h 30m\033[0m\n\033[38;2;234;88;12m5h 80%% resets 1h 15m\033[0m\n\033[38;2;183;68;38m5h 95%% resets 30m\033[0m\n'
|
|
610
933
|
```
|
|
611
934
|
|
|
612
935
|
### PowerShell form
|
|
613
936
|
|
|
614
937
|
```powershell
|
|
615
|
-
$e=[char]27; Write-Host "$e[38;2;78;186;101m5h
|
|
938
|
+
$e=[char]27; Write-Host "$e[38;2;78;186;101m5h 20% resets 2h 30m$e[0m`n$e[38;2;222;142;62m5h 55% resets 2h 30m$e[0m`n$e[38;2;234;88;12m5h 80% resets 1h 15m$e[0m`n$e[38;2;183;68;38m5h 95% resets 30m$e[0m"
|
|
616
939
|
```
|
|
617
940
|
|
|
618
941
|
## Other Rules
|
|
@@ -620,5 +943,6 @@ $e=[char]27; Write-Host "$e[38;2;78;186;101m5h:20%(2h30m)$e[0m $e[0;37m|$e[0m $e
|
|
|
620
943
|
- Do not add features beyond this spec.
|
|
621
944
|
- Do not auto-commit any changes.
|
|
622
945
|
- Do not add Co-Authored-By trailers.
|
|
623
|
-
- Read git
|
|
946
|
+
- Read git state with `git -C "$cwd" status -b --porcelain`, never from `PWD`.
|
|
947
|
+
- Never measure padding from a string containing ANSI escapes.
|
|
624
948
|
- When merging into existing `settings.json`, preserve all other keys; only set/replace `statusLine`.
|
package/package.json
CHANGED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: serena-file-processing
|
|
3
|
-
description: Master instructions for using Serena MCP tools for code navigation, search, navigation, and editing. Includes rules for terminal usage and memory management.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Serena File Processing Skill
|
|
7
|
-
|
|
8
|
-
This skill defines the **authoritative rules** for using Serena tools (`view_file`, `replace_file_content`, `grep_search`, etc.) and interactions with other tools in Antigravity.
|
|
9
|
-
|
|
10
|
-
## Core Rules
|
|
11
|
-
|
|
12
|
-
### 1. Terminal Execution
|
|
13
|
-
|
|
14
|
-
- ❌ NEVER use `mcp_serena_execute_shell_command`.
|
|
15
|
-
- ✅ ALWAYS use `run_command` for terminal operations in Antigravity.
|
|
16
|
-
|
|
17
|
-
## Workflow Best Practices
|
|
18
|
-
|
|
19
|
-
### 2. Code Navigation
|
|
20
|
-
|
|
21
|
-
- **Symbolic First**: Use `view_file_outline` to understand file structure before reading content.
|
|
22
|
-
- **Search Smart**: Use `grep_search` to find usages. Constraint searches to relevant paths.
|
|
23
|
-
- **Jumping**: Use `view_code_item` to inspect specific functions/classes without reading the whole file.
|
|
24
|
-
|
|
25
|
-
### 3. Code Editing
|
|
26
|
-
|
|
27
|
-
- **Pattern Matching**: When using `replace_file_content` or `multi_replace_file_content`, ensure `TargetContent` is unique.
|
|
28
|
-
- **Context**: Use `view_file` around the target area first to ensure you have the exact string for replacement.
|
|
29
|
-
|
|
30
|
-
### 4. Memory Management
|
|
31
|
-
|
|
32
|
-
- **Limit Output**: When reading large files, read in chunks or use `view_child_nodes`.
|
|
33
|
-
- **Close Context**: You don't need to explicitly "close" files, but don't re-read files unnecessarily.
|
|
34
|
-
|
|
35
|
-
## Integration with Other Skills
|
|
36
|
-
|
|
37
|
-
- **Layering**: Use this skill as the foundational layer for file operations.
|
|
38
|
-
- **Specifics**: Higher-level skills (like `bliksund-pr-reviewer`) define *what* to look for; this skill defines *how* to look for it reliably.
|
|
39
|
-
|
|
40
|
-
## Troubleshooting
|
|
41
|
-
|
|
42
|
-
### 5. Connection Issues
|
|
43
|
-
|
|
44
|
-
If you encounter errors connecting to the Serena MCP server or tools are unavailable:
|
|
45
|
-
|
|
46
|
-
1. **Check Process Status**: Ask the user to verify if the Serena process has started.
|
|
47
|
-
2. **Check Workspace Config**: Ask the user to confirm if the Serena configuration has been initiated for the current workspace.
|