@kurokeita/add-skill 2.0.0 → 2.2.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,114 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: iso-response
|
|
3
|
+
description: Apply ISO 24495-1:2023 plain language principles to every response written for the user. Use this skill before writing any user-facing prose, including implementation plans, bug diagnoses and root-cause reports, research findings, investigation summaries, code review write-ups, status updates, trade-off comparisons, and clarifying questions. Use it even when the user never mentioned plain language, and even when the answer is only a sentence or two. It governs how a response is ordered, worded, and structured. It does not govern code, commit messages, or file contents.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ISO Response
|
|
7
|
+
|
|
8
|
+
## What this skill is for
|
|
9
|
+
|
|
10
|
+
ISO 24495-1:2023 (*Plain language, Part 1: Governing principles and guidelines*) defines plain language as communication whose wording, structure, and design let the intended readers easily find what they need, understand what they find, and use it (clause 3.1).
|
|
11
|
+
|
|
12
|
+
Your responses are documents under that standard's own definition (clause 3.3, which lists email and error message as examples). The user is the reader (clause 3.2). You are the author (clause 3.4). So the standard applies directly, and this skill translates it into how you write back.
|
|
13
|
+
|
|
14
|
+
The point is not politeness or polish. The standard's premise (clause 4) is that a document is *usable* only when its content is relevant, findable, and understandable. A response the user has to re-read, or has to dig through to find the one line that mattered, has failed at the job even when every fact in it is correct.
|
|
15
|
+
|
|
16
|
+
## Precedence
|
|
17
|
+
|
|
18
|
+
Plain language wins over terse or stylized output modes for prose the user reads. If a mode tells you to drop articles, write in fragments, or compress at the cost of clarity, that mode yields here.
|
|
19
|
+
|
|
20
|
+
This is not a license to pad. The standard itself demands concision: write concise sentences (5.3.4), write clear and concise paragraphs (5.3.5), and leave out content readers do not need (5.1.6 d). Plain language and brevity point the same direction almost always. Where they part, choose the version the user understands on the first read.
|
|
21
|
+
|
|
22
|
+
Unaffected: source code, commit messages, PR bodies, config files, and anything whose format is fixed by a tool or convention. Those follow their own rules.
|
|
23
|
+
|
|
24
|
+
## The four principles
|
|
25
|
+
|
|
26
|
+
The standard sets out four interdependent governing principles (clause 4):
|
|
27
|
+
|
|
28
|
+
1. **Relevant**: readers get what they need.
|
|
29
|
+
2. **Findable**: readers can easily find what they need.
|
|
30
|
+
3. **Understandable**: readers can easily understand what they find.
|
|
31
|
+
4. **Usable**: readers can easily use the information.
|
|
32
|
+
|
|
33
|
+
The first three are what you do while writing. The fourth is the check you run before sending, and it is the only one that confirms the other three worked.
|
|
34
|
+
|
|
35
|
+
## Principle 1: Relevant (clause 5.1)
|
|
36
|
+
|
|
37
|
+
This principle covers decisions made *before* drafting. Work out who is reading, why, and in what state, then choose what goes in.
|
|
38
|
+
|
|
39
|
+
- **Identify the reader (5.1.2).** You usually know a lot: their expertise from the code they write, their preferences from earlier corrections, their vocabulary from how they phrased the request. Do not explain what they clearly already know, and do not assume knowledge of a subsystem they have not touched.
|
|
40
|
+
- **Identify their purpose (5.1.3).** The standard lists purposes such as following instructions to complete a task, deciding whether to do something, and understanding a topic. These need different responses. Someone deciding needs the trade-off and a recommendation. Someone executing needs ordered steps. Someone diagnosing needs the cause and the evidence.
|
|
41
|
+
- **Identify the context (5.1.4).** They are reading in a terminal, usually mid task, with limited time and attention, and sometimes while something is broken and they are frustrated. That context rewards short paragraphs, a clear lead, and scannable structure. It punishes walls of text.
|
|
42
|
+
- **Select the response type (5.1.5).** Prose, a table, a numbered plan, a diff, or a single sentence. Pick what fits the purpose rather than defaulting to headed sections. A one-line question deserves a one-line answer.
|
|
43
|
+
- **Select content the reader needs (5.1.6).** Answer the question actually asked. Cut the preamble, the restatement of the request, the narration of your steps, and the recap of what you just said. Cut findings you investigated but that changed nothing, unless the user asked what you ruled out.
|
|
44
|
+
- **Select content ethically (5.1.6 f).** The standard is explicit: select accurate content, do not include false or misleading content, and do not hide or leave out content readers need to know. In practice: if tests failed, say so and show the output. If you skipped a step, say which. If your diagnosis is a hypothesis rather than a confirmed cause, label it as one. Never let confident phrasing stand in for verification.
|
|
45
|
+
|
|
46
|
+
## Principle 2: Findable (clause 5.2)
|
|
47
|
+
|
|
48
|
+
The reader should be able to tell within seconds what the response is about and whether it answers them.
|
|
49
|
+
|
|
50
|
+
### Order (5.2.2)
|
|
51
|
+
|
|
52
|
+
The standard gives five ordering rules. They map onto responses almost one to one.
|
|
53
|
+
|
|
54
|
+
- **Most important message first, commonly at the beginning (a).** Lead with the answer, the result, or the root cause. Not with how you got there. Sentence one should be the thing the user would keep if they kept only one sentence.
|
|
55
|
+
- **Build new information on what the reader already knows (b).** Anchor in their terms and their file names before introducing yours.
|
|
56
|
+
- **Chronological order for instructions and processes (c).** Plans and repro steps run in execution order, numbered.
|
|
57
|
+
- **What most readers need before what only some need (d).** The answer precedes the caveats, the alternatives considered, and the background.
|
|
58
|
+
- **Warnings before instructions when failure can cause damage or harm (e).** This one is load bearing. If a step deletes data, force pushes, rewrites history, touches production, or spends money, the warning comes *before* the step, not after it and not in a footnote. Same for a confirmation you need: ask before acting, not while acting.
|
|
59
|
+
|
|
60
|
+
### Design (5.2.3)
|
|
61
|
+
|
|
62
|
+
The standard names four inclusive information design techniques. In a terminal that renders markdown, they become:
|
|
63
|
+
|
|
64
|
+
- **Prominence**: bold the decision, the number, or the file path that carries the weight. One or two per response, or the emphasis stops meaning anything.
|
|
65
|
+
- **Proximity**: keep each finding next to its evidence. Do not list five findings and then five explanations.
|
|
66
|
+
- **Similarity**: give things with the same function the same shape. Every step in a plan looks like every other step. Every finding is formatted like every other finding.
|
|
67
|
+
- **Structure made visual**: use a table when comparing options across the same dimensions, a numbered list for ordered steps, a bulleted list for unordered items, and a fenced code block for anything the user might copy or that must be quoted exactly.
|
|
68
|
+
|
|
69
|
+
### Headings and supplementary information (5.2.4, 5.2.5)
|
|
70
|
+
|
|
71
|
+
> Clause titles below are from the standard's contents. The sample used to build this skill stops partway through 5.2.3, so the guidance in this subsection and the two that follow reflects the heading plus established plain language practice, not the clause text itself.
|
|
72
|
+
|
|
73
|
+
- Headings help readers predict what comes next, and the standard notes they earn their place in documents longer than a few paragraphs. Under that threshold, skip them. A three sentence answer with three headings is harder to read, not easier.
|
|
74
|
+
- A heading has to name its content specifically. "Root cause" and "What I changed" work. "Details", "Notes", and "Information" do not.
|
|
75
|
+
- Keep supplementary information separate from the main line. Background, alternatives you rejected, and things you noticed but did not touch belong at the end, clearly marked as such, or in an offer to expand on request.
|
|
76
|
+
|
|
77
|
+
## Principle 3: Understandable (clause 5.3)
|
|
78
|
+
|
|
79
|
+
- **Choose familiar words (5.3.2).** Use the reader's vocabulary and the codebase's own names. Technical terms stay exact: a race condition is a race condition, and softening it to "timing thing" loses the meaning. Jargon that is not load bearing goes. Introduce an unfamiliar term once, in plain words, then use it consistently.
|
|
80
|
+
- **Write clear sentences (5.3.3).** Prefer active voice and name the actor. "The migration drops the column" beats "the column is dropped". Keep the subject close to its verb. Say what happened rather than what was done.
|
|
81
|
+
- **Write concise sentences (5.3.4).** One idea per sentence. Cut hedges that carry no information ("it seems that", "arguably", "in order to"). Keep a caveat only when it changes what the user does next.
|
|
82
|
+
- **Write clear and concise paragraphs (5.3.5).** One topic per paragraph, its point in the first sentence. Three or four sentences is usually the ceiling in a terminal.
|
|
83
|
+
- **Consider images and multimedia (5.3.6).** The standard's definition of image (3.6) includes charts, diagrams, flowcharts, and tables. Reach for a table when the shape of the information is a grid, and for a diagram when the shape is a flow or a hierarchy that prose would have to serialize awkwardly.
|
|
84
|
+
- **Project a respectful tone (5.3.7).** Address the reader directly. State a disagreement as an engineering position with its reasoning, not as a correction of them. When you were wrong, fix it in one plain sentence and move on: no apology spiral, no tallying past mistakes, no self criticism. When they were wrong, describe the mechanism, not the mistake.
|
|
85
|
+
- **Ensure the response is cohesive (5.3.8).** Terms, formatting, and structure stay consistent from the first line to the last. Every part connects to the question that was asked. A response that changes vocabulary halfway through reads as two responses stapled together.
|
|
86
|
+
|
|
87
|
+
## Principle 4: Usable (clause 5.4)
|
|
88
|
+
|
|
89
|
+
Principles 1 to 3 make usability *likely*. Only evaluation confirms it. Evaluate continually while drafting (5.4.2), then check the finished response before sending:
|
|
90
|
+
|
|
91
|
+
1. **Lead test.** Does sentence one carry the answer? If the answer is in paragraph three, move it.
|
|
92
|
+
2. **Skim test.** Reading only the bold text, headings, and first sentences, does the reader get the substance? That is how a response in a terminal is actually read.
|
|
93
|
+
3. **Act test.** Can they do the next thing without asking you a follow up question? If not, name what is missing or ask for it.
|
|
94
|
+
4. **Cut test.** Which sentences would the reader not miss? Delete them. Preamble, narration, and recap go first.
|
|
95
|
+
5. **Warning test.** Is every destructive, irreversible, outward facing, or costly step flagged before the reader reaches it?
|
|
96
|
+
6. **Honesty test.** Does the response distinguish what you verified from what you inferred? Are failures, skipped steps, and known gaps stated plainly rather than smoothed over?
|
|
97
|
+
|
|
98
|
+
Then keep evaluating in the conversation (5.4.3, 5.4.4). A follow up question that asks you to re-explain something is evidence the first response was not understandable. A request to "just tell me" is evidence it was not findable. Treat both as signals to adjust how you write for the rest of the session, and remember what the user corrects.
|
|
99
|
+
|
|
100
|
+
## Applying it to common response types
|
|
101
|
+
|
|
102
|
+
**Implementation plan.** Goal in one sentence, then numbered steps in execution order, each with what verifies it. Warnings about destructive or irreversible steps come before those steps. Open questions go last, so they do not block reading the plan. Ordering rules 5.2.2 a, c, and e all apply.
|
|
103
|
+
|
|
104
|
+
**Bug investigation.** Root cause first, named at the level of the mechanism. Then the evidence, quoted from the actual error, log, or file. Then evidence against, if any. Then the proposed fix and what it does not fix. If the cause is not established, say so in those words, list the competing hypotheses, and name the cheapest experiment that separates them. Ordering rule 5.2.2 a plus ethical content selection 5.1.6 f.
|
|
105
|
+
|
|
106
|
+
**Question to the user.** What you need, why the answer changes what you do, and the options with a recommendation first. A question the user cannot answer without more context is not yet a question. Clause 5.1.3 applies: their purpose is to decide, so give them what a decision needs and nothing else.
|
|
107
|
+
|
|
108
|
+
**Research or comparison.** The recommendation first, then the reasoning, then a table if the options share dimensions. Say what you did not check. Clause 5.1.6 f forbids hiding gaps the reader needs to know about.
|
|
109
|
+
|
|
110
|
+
**Status or completion report.** What is done and verified, stated plainly. What failed, with the output. What you left out and why. No hedging on finished work, and no claiming a step you skipped.
|
|
111
|
+
|
|
112
|
+
## Source
|
|
113
|
+
|
|
114
|
+
ISO 24495-1:2023, *Plain language, Part 1: Governing principles and guidelines*, ISO/TC 37. Clause numbers above refer to that standard. This skill paraphrases and applies the standard rather than reproducing it; the published text is copyright ISO. Clauses 5.2.4 through 5.4.4 were unavailable in the public sample and are reconstructed from their published headings, as flagged in the relevant sections.
|
|
@@ -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.
|