@kurokeita/add-skill 1.21.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -8
- package/dist/bin/cli.js +576 -792
- package/dist/skills/git-commit/SKILL.md +101 -37
- package/dist/skills/handoff/SKILL.md +16 -0
- package/dist/skills/init-agents-md/SKILL.md +51 -0
- package/dist/skills/init-agents-md/references/agents_template.md +42 -0
- package/dist/skills/init-agents-md/references/patterns_template.md +19 -0
- package/dist/skills/statusline-setup/SKILL.md +211 -66
- package/dist/skills/universalize-agents/SKILL.md +245 -0
- package/dist/skills/universalize-agents/reference/hook-templates/README.md +30 -0
- package/dist/skills/universalize-agents/reference/hook-templates/claude-code.json +15 -0
- package/dist/skills/universalize-agents/reference/hook-templates/codex.toml +10 -0
- package/dist/skills/universalize-agents/reference/hook-templates/copilot.json +9 -0
- package/dist/skills/universalize-agents/reference/hook-templates/gemini.json +11 -0
- package/dist/skills/universalize-agents/reference/mapping.md +100 -0
- package/dist/skills/universalize-agents/scripts/agent-setup.ps1 +83 -0
- package/dist/skills/universalize-agents/scripts/agent-setup.sh +98 -0
- package/dist/skills/update-agents-md/SKILL.md +43 -0
- package/package.json +2 -5
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: statusline-setup
|
|
3
|
-
description: 'Set up the Claude Code statusline with cwd, git branch, model, context %, session token totals, and 5h/7d quota usage with ETA. Use when asked to "set up my statusline", "install statusline", "configure
|
|
3
|
+
description: 'Set up the Claude Code or Gemini/Antigravity CLI statusline with cwd, git branch, model, context %, session token totals, and 5h/7d quota usage with ETA. Use when asked to "set up my statusline", "install statusline", "configure statusline", or to add a colored status line showing rate limits and token usage. Detects OS and installs the appropriate variant (bash + jq on Linux/macOS, PowerShell on Windows).'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Statusline Setup
|
|
7
7
|
|
|
8
|
-
Install a Claude Code statusline that renders, left to right, pipe-separated:
|
|
8
|
+
Install a Claude Code or Gemini/Antigravity statusline that renders, left to right, pipe-separated:
|
|
9
9
|
|
|
10
10
|
1. **cwd** — 24-bit ANSI `#5EFFFF` (`38;2;94;255;255`), with `$HOME` / `%USERPROFILE%` shortened to `~`.
|
|
11
11
|
2. **git branch** in parentheses — 24-bit ANSI `#C24870` (`38;2;194;72;112`), only when inside a repo.
|
|
@@ -18,18 +18,25 @@ Segments are joined with ANSI `0;37` pipes (` | `).
|
|
|
18
18
|
|
|
19
19
|
## When to Use This Skill
|
|
20
20
|
|
|
21
|
-
- "Set up my Claude Code statusline"
|
|
21
|
+
- "Set up my Claude Code statusline" or "Set up my Antigravity statusline"
|
|
22
22
|
- "Install the statusline"
|
|
23
23
|
- "Configure my statusline with token counts and quota"
|
|
24
24
|
- Any request to add a colored status line showing context %, rate limits, or session tokens.
|
|
25
25
|
|
|
26
|
-
## OS Detection
|
|
26
|
+
## OS & Platform Detection
|
|
27
27
|
|
|
28
|
-
Detect the platform first and
|
|
28
|
+
Detect the operating system and targeted platform first, then write and wire the corresponding configuration:
|
|
29
|
+
|
|
30
|
+
### Claude Code
|
|
29
31
|
|
|
30
32
|
- **Linux / macOS** → write `~/.claude/statusline-command.sh` (pure bash + `jq`), wire into `~/.claude/settings.json`.
|
|
31
33
|
- **Windows** → write `%USERPROFILE%\.claude\statusline-command.ps1`, wire into `%USERPROFILE%\.claude\settings.json`.
|
|
32
34
|
|
|
35
|
+
### Antigravity / Gemini CLI
|
|
36
|
+
|
|
37
|
+
- **Linux / macOS** → write `~/.gemini/antigravity-cli/statusline-command.sh` (pure bash + `jq`), wire into `~/.gemini/antigravity-cli/settings.json`.
|
|
38
|
+
- **Windows** → write `%USERPROFILE%\.gemini\antigravity-cli\statusline-command.ps1`, wire into `%USERPROFILE%\.gemini\antigravity-cli\settings.json`.
|
|
39
|
+
|
|
33
40
|
## Token Formatting
|
|
34
41
|
|
|
35
42
|
- `< 1000` → raw integer (e.g. `↑850 ↓120`)
|
|
@@ -39,12 +46,14 @@ Detect the platform first and install only the matching variant:
|
|
|
39
46
|
|
|
40
47
|
## Token Source
|
|
41
48
|
|
|
42
|
-
|
|
49
|
+
Tokens are calculated dynamically:
|
|
43
50
|
|
|
44
|
-
|
|
45
|
-
- `
|
|
46
|
-
|
|
47
|
-
|
|
51
|
+
1. **Transcript Parsing (Claude Code / general)**: Sum across assistant messages in the JSONL `transcript_path` file:
|
|
52
|
+
- `input` = `message.usage.input_tokens` + `message.usage.cache_creation_input_tokens` + `message.usage.cache_read_input_tokens`
|
|
53
|
+
- `output` = `message.usage.output_tokens`
|
|
54
|
+
2. **Payload Fallback (Antigravity / Gemini CLI)**: If transcript parsing yields `0` tokens (or the transcript doesn't store step token metrics), fallback to:
|
|
55
|
+
- `input` = `context_window.total_input_tokens`
|
|
56
|
+
- `output` = `context_window.total_output_tokens`
|
|
48
57
|
|
|
49
58
|
## Quota Colors (by REMAINING quota = 100 - used%)
|
|
50
59
|
|
|
@@ -57,7 +66,7 @@ Skip lines that fail JSON parsing or lack `message.usage`. If `transcript_path`
|
|
|
57
66
|
|
|
58
67
|
The 5h and 7d windows must be colored **independently**.
|
|
59
68
|
|
|
60
|
-
## ETA Format (from epoch seconds in
|
|
69
|
+
## ETA Format (from epoch seconds or reset duration in seconds)
|
|
61
70
|
|
|
62
71
|
- `> 1 day` → `Xd Yh`
|
|
63
72
|
- `> 1 hour` → `Xh Ym`
|
|
@@ -66,34 +75,57 @@ The 5h and 7d windows must be colored **independently**.
|
|
|
66
75
|
|
|
67
76
|
## Input Schema (stdin JSON)
|
|
68
77
|
|
|
69
|
-
|
|
78
|
+
The input schema depends on the platform:
|
|
79
|
+
|
|
80
|
+
### Claude Code Schema
|
|
70
81
|
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"cwd": "/path/to/cwd",
|
|
85
|
+
"transcript_path": "/path/to/transcript.jsonl",
|
|
86
|
+
"model": { "display_name": "Opus 4.7" },
|
|
87
|
+
"effort": { "level": "medium" },
|
|
88
|
+
"context_window": { "used_percentage": 42 },
|
|
89
|
+
"rate_limits": {
|
|
90
|
+
"five_hour": { "used_percentage": 80, "resets_at": 1719213265 },
|
|
91
|
+
"seven_day": { "used_percentage": 50, "resets_at": 1719213265 }
|
|
92
|
+
}
|
|
93
|
+
}
|
|
71
94
|
```
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
95
|
+
|
|
96
|
+
### Antigravity Schema
|
|
97
|
+
|
|
98
|
+
```json
|
|
99
|
+
{
|
|
100
|
+
"cwd": "/path/to/cwd",
|
|
101
|
+
"transcript_path": "/path/to/transcript.jsonl",
|
|
102
|
+
"model": { "display_name": "Gemini 3.5 Flash" },
|
|
103
|
+
"context_window": {
|
|
104
|
+
"used_percentage": 6.7,
|
|
105
|
+
"total_input_tokens": 70419,
|
|
106
|
+
"total_output_tokens": 16667
|
|
107
|
+
},
|
|
108
|
+
"quota": {
|
|
109
|
+
"gemini-5h": { "remaining_fraction": 0.87, "reset_in_seconds": 2515 },
|
|
110
|
+
"gemini-weekly": { "remaining_fraction": 0.97, "reset_in_seconds": 589315 }
|
|
111
|
+
}
|
|
112
|
+
}
|
|
81
113
|
```
|
|
82
114
|
|
|
83
115
|
## Linux / macOS Implementation
|
|
84
116
|
|
|
85
|
-
Write
|
|
117
|
+
Write `statusline-command.sh` (chmod +x) to the targeted platform directory. Pure bash + `jq` — no Python. `jq` must be installed; if missing, the script prints a one-line hint and exits 0.
|
|
86
118
|
|
|
87
119
|
JSON parsing rules:
|
|
88
120
|
|
|
89
121
|
- Read the full stdin payload **once** with `cat`, parse all fields with a single `jq` call.
|
|
90
|
-
-
|
|
122
|
+
- Use `fromjson?` to process JSONL files line-by-line resiliently to ignore malformed/plain-text entries.
|
|
91
123
|
|
|
92
|
-
|
|
124
|
+
`statusline-command.sh`:
|
|
93
125
|
|
|
94
126
|
```bash
|
|
95
127
|
#!/usr/bin/env bash
|
|
96
|
-
#
|
|
128
|
+
# Statusline — pure bash + jq. Supports Claude Code & Antigravity payload formats.
|
|
97
129
|
|
|
98
130
|
set -u
|
|
99
131
|
|
|
@@ -127,10 +159,14 @@ quota_color() {
|
|
|
127
159
|
|
|
128
160
|
fmt_eta() {
|
|
129
161
|
local resets="$1"
|
|
130
|
-
[[ -z "$resets" || "$resets" == "null" ]] && { printf ''; return; }
|
|
162
|
+
[[ -z "$resets" || "$resets" == "null" || "$resets" == "" ]] && { printf ''; return; }
|
|
131
163
|
local now delta days hours mins
|
|
132
|
-
|
|
133
|
-
|
|
164
|
+
if (( resets < 10000000 )); then
|
|
165
|
+
delta=$resets
|
|
166
|
+
else
|
|
167
|
+
now=$(date +%s)
|
|
168
|
+
delta=$(( resets - now ))
|
|
169
|
+
fi
|
|
134
170
|
if (( delta <= 0 )); then printf 'now'
|
|
135
171
|
elif (( delta >= 86400 )); then
|
|
136
172
|
days=$(( delta / 86400 )); hours=$(( (delta % 86400) / 3600 ))
|
|
@@ -173,15 +209,41 @@ read_fields() {
|
|
|
173
209
|
.model.display_name // "",
|
|
174
210
|
.effort.level // "",
|
|
175
211
|
(.context_window.used_percentage // "" | tostring),
|
|
176
|
-
(
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
212
|
+
(
|
|
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)
|
|
216
|
+
else "" end)
|
|
217
|
+
| tostring
|
|
218
|
+
),
|
|
219
|
+
(
|
|
220
|
+
.rate_limits.five_hour.resets_at //
|
|
221
|
+
.quota."gemini-5h".reset_in_seconds //
|
|
222
|
+
.quota."3p-5h".reset_in_seconds //
|
|
223
|
+
""
|
|
224
|
+
| tostring
|
|
225
|
+
),
|
|
226
|
+
(
|
|
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)
|
|
230
|
+
else "" end)
|
|
231
|
+
| tostring
|
|
232
|
+
),
|
|
233
|
+
(
|
|
234
|
+
.rate_limits.seven_day.resets_at //
|
|
235
|
+
.quota."gemini-weekly".reset_in_seconds //
|
|
236
|
+
.quota."3p-weekly".reset_in_seconds //
|
|
237
|
+
""
|
|
238
|
+
| tostring
|
|
239
|
+
),
|
|
240
|
+
(.context_window.total_input_tokens // "" | tostring),
|
|
241
|
+
(.context_window.total_output_tokens // "" | tostring)
|
|
180
242
|
] | join("\u001f")
|
|
181
243
|
' 2>/dev/null <<<"$payload"
|
|
182
244
|
}
|
|
183
245
|
|
|
184
|
-
IFS=$'\x1f' read -r cwd transcript model effort ctx five_used five_reset seven_used seven_reset < <(read_fields)
|
|
246
|
+
IFS=$'\x1f' read -r cwd transcript model effort ctx five_used five_reset seven_used seven_reset payload_in payload_out < <(read_fields)
|
|
185
247
|
|
|
186
248
|
segments=()
|
|
187
249
|
|
|
@@ -211,10 +273,23 @@ if [[ -n "$ctx" ]]; then
|
|
|
211
273
|
segments+=("${C_CTX}ctx ${ctx_int}%${RESET}")
|
|
212
274
|
fi
|
|
213
275
|
|
|
214
|
-
|
|
276
|
+
ti=0
|
|
277
|
+
to=0
|
|
278
|
+
|
|
279
|
+
# --- tokens: sum transcripts, fallback to context_window if needed ---
|
|
280
|
+
if [[ -n "$transcript" ]]; then
|
|
281
|
+
# Resolve path mismatch (if it has .gemini/antigravity/ but it actually is at .gemini/antigravity-cli/)
|
|
282
|
+
if [[ ! -f "$transcript" && "$transcript" == *"/antigravity/"* ]]; then
|
|
283
|
+
corrected_transcript="${transcript//\/antigravity\//\/antigravity-cli\/}"
|
|
284
|
+
if [[ -f "$corrected_transcript" ]]; then
|
|
285
|
+
transcript="$corrected_transcript"
|
|
286
|
+
fi
|
|
287
|
+
fi
|
|
288
|
+
fi
|
|
289
|
+
|
|
215
290
|
if [[ -n "$transcript" && -f "$transcript" ]]; then
|
|
216
|
-
tok=$(jq -
|
|
217
|
-
reduce
|
|
291
|
+
tok=$(jq -Rn '
|
|
292
|
+
reduce (inputs | fromjson?) as $m ({i:0,o:0};
|
|
218
293
|
($m.message.usage // null) as $u
|
|
219
294
|
| if $u == null then .
|
|
220
295
|
else
|
|
@@ -226,12 +301,21 @@ if [[ -n "$transcript" && -f "$transcript" ]]; then
|
|
|
226
301
|
' "$transcript" 2>/dev/null) || tok=""
|
|
227
302
|
if [[ -n "$tok" ]]; then
|
|
228
303
|
IFS=$'\t' read -r ti to <<<"$tok"
|
|
229
|
-
if (( ti > 0 || to > 0 )); then
|
|
230
|
-
segments+=("${C_TOK}↑$(fmt_tokens "$ti") ↓$(fmt_tokens "$to")${RESET}")
|
|
231
|
-
fi
|
|
232
304
|
fi
|
|
233
305
|
fi
|
|
234
306
|
|
|
307
|
+
# Fallback to payload context_window tokens if transcript sum is 0
|
|
308
|
+
if (( ti == 0 && to == 0 )); then
|
|
309
|
+
if [[ -n "$payload_in" && -n "$payload_out" ]]; then
|
|
310
|
+
ti=$payload_in
|
|
311
|
+
to=$payload_out
|
|
312
|
+
fi
|
|
313
|
+
fi
|
|
314
|
+
|
|
315
|
+
if (( ti > 0 || to > 0 )); then
|
|
316
|
+
segments+=("${C_TOK}↑$(fmt_tokens "$ti") ↓$(fmt_tokens "$to")${RESET}")
|
|
317
|
+
fi
|
|
318
|
+
|
|
235
319
|
if [[ -n "$five_used" ]]; then
|
|
236
320
|
fu=$(awk -v n="$five_used" 'BEGIN{printf "%d", (n+0.5)}')
|
|
237
321
|
eta=$(fmt_eta "$five_reset")
|
|
@@ -253,22 +337,11 @@ done
|
|
|
253
337
|
printf '%s' "$out"
|
|
254
338
|
```
|
|
255
339
|
|
|
256
|
-
Wire into `~/.claude/settings.json` (merge into existing JSON, don't clobber other keys):
|
|
257
|
-
|
|
258
|
-
```json
|
|
259
|
-
{
|
|
260
|
-
"statusLine": {
|
|
261
|
-
"type": "command",
|
|
262
|
-
"command": "bash ~/.claude/statusline-command.sh"
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
```
|
|
266
|
-
|
|
267
340
|
## Windows Implementation
|
|
268
341
|
|
|
269
|
-
Write
|
|
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.
|
|
270
343
|
|
|
271
|
-
|
|
344
|
+
`statusline-command.ps1`:
|
|
272
345
|
|
|
273
346
|
```powershell
|
|
274
347
|
$ErrorActionPreference = 'SilentlyContinue'
|
|
@@ -298,8 +371,13 @@ function QuotaColor([double]$used) {
|
|
|
298
371
|
}
|
|
299
372
|
|
|
300
373
|
function FmtEta($resetsAt) {
|
|
301
|
-
if ($null -eq $resetsAt) { return "" }
|
|
302
|
-
$
|
|
374
|
+
if ($null -eq $resetsAt -or $resetsAt -eq "") { return "" }
|
|
375
|
+
$val = [int64]$resetsAt
|
|
376
|
+
if ($val -lt 10000000) {
|
|
377
|
+
$delta = $val
|
|
378
|
+
} else {
|
|
379
|
+
$delta = $val - [int64](Get-Date -UFormat %s)
|
|
380
|
+
}
|
|
303
381
|
if ($delta -le 0) { return "now" }
|
|
304
382
|
$days = [math]::Floor($delta / 86400); $delta = $delta % 86400
|
|
305
383
|
$hours = [math]::Floor($delta / 3600); $delta = $delta % 3600
|
|
@@ -334,6 +412,13 @@ function GetBranch($cwd) {
|
|
|
334
412
|
|
|
335
413
|
function SumTokens($path) {
|
|
336
414
|
if (-not $path) { return $null }
|
|
415
|
+
# Resolve path mismatch (if it has .gemini/antigravity/ but it actually is at .gemini/antigravity-cli/)
|
|
416
|
+
if (-not (Test-Path -LiteralPath $path) -and $path.Contains("/.gemini/antigravity/")) {
|
|
417
|
+
$corrected = $path.Replace(".gemini/antigravity/", ".gemini/antigravity-cli/").Replace(".gemini\antigravity\", ".gemini\antigravity-cli\")
|
|
418
|
+
if (Test-Path -LiteralPath $corrected) {
|
|
419
|
+
$path = $corrected
|
|
420
|
+
}
|
|
421
|
+
}
|
|
337
422
|
if (-not (Test-Path -LiteralPath $path)) { return $null }
|
|
338
423
|
$sumIn = 0; $sumOut = 0; $found = $false
|
|
339
424
|
$lines = Get-Content -LiteralPath $path -ReadCount 0 -ErrorAction SilentlyContinue
|
|
@@ -377,39 +462,99 @@ $ctx = $data.context_window.used_percentage
|
|
|
377
462
|
if ($null -ne $ctx) { $segments += "$C_CTX" + "ctx " + [int][math]::Round([double]$ctx) + "%$RESET" }
|
|
378
463
|
|
|
379
464
|
$tokens = SumTokens $data.transcript_path
|
|
465
|
+
$ti = 0
|
|
466
|
+
$to = 0
|
|
380
467
|
if ($null -ne $tokens) {
|
|
381
|
-
$
|
|
468
|
+
$ti = $tokens[0]
|
|
469
|
+
$to = $tokens[1]
|
|
382
470
|
}
|
|
383
471
|
|
|
384
|
-
|
|
385
|
-
$
|
|
386
|
-
if ($null -ne $
|
|
387
|
-
|
|
388
|
-
|
|
472
|
+
# Fallback to payload context_window tokens if transcript sum is 0
|
|
473
|
+
if ($ti -eq 0 -and $to -eq 0) {
|
|
474
|
+
if ($null -ne $data.context_window.total_input_tokens -and $null -ne $data.context_window.total_output_tokens) {
|
|
475
|
+
$ti = [int64]$data.context_window.total_input_tokens
|
|
476
|
+
$to = [int64]$data.context_window.total_output_tokens
|
|
477
|
+
}
|
|
389
478
|
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
$segments += "$(
|
|
479
|
+
|
|
480
|
+
if ($ti -gt 0 -or $to -gt 0) {
|
|
481
|
+
$segments += "$C_TOK↑$(FmtTokens $ti) ↓$(FmtTokens $to)$RESET"
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
$five_used = $null
|
|
485
|
+
$five_reset = $null
|
|
486
|
+
$seven_used = $null
|
|
487
|
+
$seven_reset = $null
|
|
488
|
+
|
|
489
|
+
if ($null -ne $data.rate_limits.five_hour.used_percentage) {
|
|
490
|
+
$five_used = $data.rate_limits.five_hour.used_percentage
|
|
491
|
+
$five_reset = $data.rate_limits.five_hour.resets_at
|
|
492
|
+
} elseif ($null -ne $data.quota) {
|
|
493
|
+
$q5 = if ($null -ne $data.quota."gemini-5h") { $data.quota."gemini-5h" } else { $data.quota."3p-5h" }
|
|
494
|
+
if ($null -ne $q5) {
|
|
495
|
+
$five_used = (1.0 - $q5.remaining_fraction) * 100
|
|
496
|
+
$five_reset = $q5.reset_in_seconds
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
if ($null -ne $data.rate_limits.seven_day.used_percentage) {
|
|
501
|
+
$seven_used = $data.rate_limits.seven_day.used_percentage
|
|
502
|
+
$seven_reset = $data.rate_limits.seven_day.resets_at
|
|
503
|
+
} elseif ($null -ne $data.quota) {
|
|
504
|
+
$qw = if ($null -ne $data.quota."gemini-weekly") { $data.quota."gemini-weekly" } else { $data.quota."3p-weekly" }
|
|
505
|
+
if ($null -ne $qw) {
|
|
506
|
+
$seven_used = (1.0 - $qw.remaining_fraction) * 100
|
|
507
|
+
$seven_reset = $qw.reset_in_seconds
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
if ($null -ne $five_used) {
|
|
512
|
+
$segments += "$(QuotaColor $five_used)5h:$([int][math]::Round($five_used))%($(FmtEta $five_reset))$RESET"
|
|
513
|
+
}
|
|
514
|
+
if ($null -ne $seven_used) {
|
|
515
|
+
$segments += "$(QuotaColor $seven_used)7d:$([int][math]::Round($seven_used))%($(FmtEta $seven_reset))$RESET"
|
|
393
516
|
}
|
|
394
517
|
|
|
395
518
|
$sep = " $C_PIPE|$RESET "
|
|
396
519
|
[Console]::Out.Write([string]::Join($sep, $segments))
|
|
397
520
|
```
|
|
398
521
|
|
|
399
|
-
|
|
522
|
+
## Settings Wiring
|
|
523
|
+
|
|
524
|
+
### Claude Code
|
|
525
|
+
|
|
526
|
+
Wire into `~/.claude/settings.json` (or `%USERPROFILE%\.claude\settings.json` on Windows):
|
|
527
|
+
|
|
528
|
+
```json
|
|
529
|
+
{
|
|
530
|
+
"statusLine": {
|
|
531
|
+
"type": "command",
|
|
532
|
+
"command": "bash ~/.claude/statusline-command.sh"
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
(Or the `powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%USERPROFILE%\.claude\statusline-command.ps1"` variant on Windows).
|
|
538
|
+
|
|
539
|
+
### Antigravity CLI
|
|
540
|
+
|
|
541
|
+
Wire into `~/.gemini/antigravity-cli/settings.json` (or `%USERPROFILE%\.gemini\antigravity-cli\settings.json` on Windows):
|
|
400
542
|
|
|
401
543
|
```json
|
|
402
544
|
{
|
|
403
545
|
"statusLine": {
|
|
404
546
|
"type": "command",
|
|
405
|
-
"command": "
|
|
547
|
+
"command": "bash ~/.gemini/antigravity-cli/statusline-command.sh",
|
|
548
|
+
"enabled": true
|
|
406
549
|
}
|
|
407
550
|
}
|
|
408
551
|
```
|
|
409
552
|
|
|
553
|
+
(Or the `powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%USERPROFILE%\.gemini\antigravity-cli\statusline-command.ps1"` variant on Windows).
|
|
554
|
+
|
|
410
555
|
## Smoke Test
|
|
411
556
|
|
|
412
|
-
After installing, generate a fake JSON payload with 5h at 80% used, 7d at 50% used, both with future `resets_at`, plus a `transcript_path` pointing at a small fixture JSONL containing two assistant messages with `usage` blocks. Pipe it into the script and confirm:
|
|
557
|
+
After installing, generate a fake JSON payload with 5h at 80% used, 7d at 50% used, both with future `resets_at`/`reset_in_seconds`, plus a `transcript_path` pointing at a small fixture JSONL containing two assistant messages with `usage` blocks. Pipe it into the script and confirm:
|
|
413
558
|
|
|
414
559
|
- 5h renders **orange**, 7d renders **amber**.
|
|
415
560
|
- cwd, model, and context % are present.
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: universalize-agents
|
|
3
|
+
description: Use when the user wants to consolidate or "universalize" their AI agent setup across platforms (Claude Code, Codex, Windsurf, GitHub Copilot, Gemini CLI, Antigravity). Discovers each platform's installed skills/agents/commands and master instructions, copies them into a single `.agents/` directory, migrates master instructions into a canonical `AGENTS.md`, and installs a per-platform session-start hook that symlinks `.agents/` back into each platform's directories.
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Universalize Agents
|
|
8
|
+
|
|
9
|
+
Consolidate a multi-platform AI agent setup into one source of truth
|
|
10
|
+
(`.agents/` + `AGENTS.md`) and wire every platform to re-materialize it via
|
|
11
|
+
symlinks on each session start.
|
|
12
|
+
|
|
13
|
+
This skill is the first step of a larger "universalize agent setup" feature.
|
|
14
|
+
It performs discovery, consolidation, and hook installation. It does **not**
|
|
15
|
+
modify this repository's `src/` or CLI.
|
|
16
|
+
|
|
17
|
+
## Canonical references
|
|
18
|
+
|
|
19
|
+
Read these before acting — they are the source of truth and must not drift
|
|
20
|
+
from the codebase:
|
|
21
|
+
|
|
22
|
+
- `reference/mapping.md` — reverse map of every platform's
|
|
23
|
+
skills/agents/commands directories (derived from `src/utils/paths.ts`),
|
|
24
|
+
the per-platform hook-config file locations, and the master-instruction
|
|
25
|
+
file names.
|
|
26
|
+
- `scripts/agent-setup.sh` (macOS/Linux) and `scripts/agent-setup.ps1`
|
|
27
|
+
(Windows) — the idempotent symlink templates that get installed.
|
|
28
|
+
- `reference/hook-templates/` — per-platform session-start hook snippets.
|
|
29
|
+
|
|
30
|
+
## When to Use
|
|
31
|
+
|
|
32
|
+
Invoke when the user asks to "universalize", "consolidate", "unify", or
|
|
33
|
+
"centralize" their agent setup, skills, commands, or instructions across more
|
|
34
|
+
than one AI coding platform.
|
|
35
|
+
|
|
36
|
+
## Workflow
|
|
37
|
+
|
|
38
|
+
Follow these phases in order. Stop and ask whenever a decision is the user's
|
|
39
|
+
to make.
|
|
40
|
+
|
|
41
|
+
### Phase 1 — Choose scope
|
|
42
|
+
|
|
43
|
+
Ask the user: **universalize the setup for the current project, or globally?**
|
|
44
|
+
|
|
45
|
+
- `project` → `.agents/` lives at the project root; platform targets are the
|
|
46
|
+
project-relative directories in `reference/mapping.md`.
|
|
47
|
+
- `global` → `.agents/` lives at `$HOME/.agents`; platform targets are the
|
|
48
|
+
`~/...` directories in `reference/mapping.md`.
|
|
49
|
+
|
|
50
|
+
Record the chosen scope and the resolved base directory (`BASE`). Everything
|
|
51
|
+
downstream uses `BASE`.
|
|
52
|
+
|
|
53
|
+
### Phase 2 — Discover existing setups
|
|
54
|
+
|
|
55
|
+
For each of the six platforms, check whether its directories exist under
|
|
56
|
+
`BASE` (per `reference/mapping.md`). Treat a platform as "in use" if any of
|
|
57
|
+
its skills/agents/commands dirs or its master-instruction file exists.
|
|
58
|
+
|
|
59
|
+
For each in-use platform, enumerate items in its `skills`, `agents`,
|
|
60
|
+
`workflows`, and `rules` source dirs. Some platforms collapse multiple types into one
|
|
61
|
+
directory (Codex puts skills + agents + workflows in `.codex/skills`; Claude
|
|
62
|
+
Code puts workflows in `.claude/skills`). Disambiguate an item's real type by
|
|
63
|
+
reading its frontmatter / the `x-ai-agents-type` metadata this toolchain
|
|
64
|
+
writes when wrapping items — never by directory alone.
|
|
65
|
+
|
|
66
|
+
Report a discovery summary (platforms in use, item counts per type) before
|
|
67
|
+
continuing.
|
|
68
|
+
|
|
69
|
+
### Phase 3 — Build `.agents/`
|
|
70
|
+
|
|
71
|
+
Create the unified layout under `BASE`:
|
|
72
|
+
|
|
73
|
+
```text
|
|
74
|
+
.agents/
|
|
75
|
+
skills/ agents/ commands/ rules/ hooks/
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Copy discovered items into `.agents/{skills,agents,commands,rules}`. When the
|
|
79
|
+
same-named item is found on more than one platform, copy it once: prefer the
|
|
80
|
+
first platform discovered and warn about the collision rather than silently
|
|
81
|
+
overwriting. Never wipe an existing `.agents/` — merge into it.
|
|
82
|
+
|
|
83
|
+
#### Format normalization
|
|
84
|
+
|
|
85
|
+
The unified store is markdown-only. Normalize as you copy:
|
|
86
|
+
|
|
87
|
+
- **Rules.** Collect standard markdown rule files from the platforms that
|
|
88
|
+
support them — Claude, Gemini, and Antigravity use `<agent-dir>/rules`;
|
|
89
|
+
GitHub Copilot uses `.github/instructions` (see `reference/mapping.md`) —
|
|
90
|
+
into `.agents/rules/`. Codex and Windsurf have none. These are per-platform
|
|
91
|
+
rule files, distinct from the master instruction files consolidated in
|
|
92
|
+
Phase 4. Rules are **not** symlinked back (not in `__LINK_MAP__`); Phase 4
|
|
93
|
+
wires the whole dir into `AGENTS.md`.
|
|
94
|
+
- **Workflows → commands.** Treat every platform's "workflow" items as the
|
|
95
|
+
universal `commands` type and place them in `.agents/commands/`. If a prior
|
|
96
|
+
run left a `.agents/workflows/` directory, move its contents into
|
|
97
|
+
`.agents/commands/` and remove the now-empty `workflows/` dir.
|
|
98
|
+
- **Gemini commands → skills.** Gemini stores commands as TOML
|
|
99
|
+
(`.gemini/commands/*.toml`), which cannot be symlinked back as markdown.
|
|
100
|
+
Convert each Gemini `*.toml` into a skill: parse its `description` and
|
|
101
|
+
`prompt = """..."""` fields, then write `.agents/skills/<name>/SKILL.md`
|
|
102
|
+
with the prompt as the body and this frontmatter:
|
|
103
|
+
|
|
104
|
+
```markdown
|
|
105
|
+
---
|
|
106
|
+
name: <toml file name without extension>
|
|
107
|
+
description: <value of the TOML `description` field>
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
<contents of the TOML `prompt` field>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
This is the reverse of `convertToGeminiCommandTOML` in
|
|
114
|
+
`src/utils/toml.ts`. Gemini workflows therefore land in `.agents/skills/`,
|
|
115
|
+
not `.agents/commands/`.
|
|
116
|
+
|
|
117
|
+
### Phase 4 — Migrate master instructions
|
|
118
|
+
|
|
119
|
+
Detect every master-instruction file present in scope:
|
|
120
|
+
|
|
121
|
+
| Platform | File |
|
|
122
|
+
|---|---|
|
|
123
|
+
| Claude Code | `CLAUDE.md` |
|
|
124
|
+
| Codex / Antigravity / Windsurf | `AGENTS.md` |
|
|
125
|
+
| Gemini CLI | `GEMINI.md` |
|
|
126
|
+
| GitHub Copilot | `.github/copilot-instructions.md` |
|
|
127
|
+
|
|
128
|
+
The canonical target is root `AGENTS.md` (already the native master file for
|
|
129
|
+
Codex, Antigravity, and Windsurf).
|
|
130
|
+
|
|
131
|
+
- **None found** → create a minimal `AGENTS.md`.
|
|
132
|
+
- **Exactly one found** → use it as `AGENTS.md` directly, no prompt.
|
|
133
|
+
- **Two or more distinct files found** → STOP and ask the user how to
|
|
134
|
+
consolidate:
|
|
135
|
+
1. **Keep one** — show each detected file with a short preview / line
|
|
136
|
+
count; the user picks which becomes `AGENTS.md`.
|
|
137
|
+
2. **Merge all + trim** — concatenate every source, then de-duplicate
|
|
138
|
+
overlapping instructions into one coherent `AGENTS.md` (semantic
|
|
139
|
+
de-duplication, preserving every unique directive). Surface any
|
|
140
|
+
conflicting directives to the user instead of dropping them silently.
|
|
141
|
+
|
|
142
|
+
After producing `AGENTS.md` (at `BASE`), **replace the entire contents** of
|
|
143
|
+
each non-`AGENTS.md` master file with a single import line — its original
|
|
144
|
+
content now lives in `AGENTS.md`:
|
|
145
|
+
|
|
146
|
+
```text
|
|
147
|
+
@AGENTS.md
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Always create `AGENTS.md` (with the migrated content) before overwriting any
|
|
151
|
+
master file, so nothing is lost. Platforms that natively read `AGENTS.md`
|
|
152
|
+
(Codex, Antigravity, Windsurf) need no such file.
|
|
153
|
+
|
|
154
|
+
**Wiring rules into masters.** Rules are not symlinked into any platform dir.
|
|
155
|
+
Instead, import the whole `.agents/rules/` directory into `AGENTS.md` with a
|
|
156
|
+
single directory import, so every platform that reads `AGENTS.md` (directly, or
|
|
157
|
+
via its `@AGENTS.md` master import) picks them up:
|
|
158
|
+
|
|
159
|
+
```text
|
|
160
|
+
@.agents/rules/
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Add this import only when `.agents/rules/` is non-empty.
|
|
164
|
+
|
|
165
|
+
### Phase 5 — Install and run the setup script
|
|
166
|
+
|
|
167
|
+
Pick the script for the host OS — `scripts/agent-setup.sh` on macOS/Linux,
|
|
168
|
+
`scripts/agent-setup.ps1` on Windows — copy it into `BASE/.agents/hooks/`
|
|
169
|
+
(keeping its extension), make it executable, and fill its two placeholders:
|
|
170
|
+
|
|
171
|
+
- `__AGENTS_DIR__` → path to `BASE/.agents`: **relative** (`.agents`) for
|
|
172
|
+
project scope, **absolute** for global scope.
|
|
173
|
+
- `__LINK_MAP__` → one `SRC_SUBDIR|DEST_DIR` line per (in-use platform × type),
|
|
174
|
+
using the scope-correct destination dirs from `reference/mapping.md`.
|
|
175
|
+
`SRC_SUBDIR` is `skills`, `agents`, or `commands`. Use **relative** dest
|
|
176
|
+
dirs (e.g. `.claude/skills`) for project scope and **absolute** dirs for
|
|
177
|
+
global scope. The script detects the mode from `__AGENTS_DIR__` and emits
|
|
178
|
+
relative symlinks for project scope, absolute for global.
|
|
179
|
+
|
|
180
|
+
The script symlinks each item individually (not whole directories) so that
|
|
181
|
+
collapsed targets like `.codex/skills` can receive skills + agents + commands
|
|
182
|
+
merged together without conflict. It is idempotent and refuses to clobber a
|
|
183
|
+
real (non-symlink) path.
|
|
184
|
+
|
|
185
|
+
In **project scope** the script keeps the generated symlinks out of git by
|
|
186
|
+
writing a single `.gitignore` per platform at its base dir (the parent of the
|
|
187
|
+
target dirs, e.g. `.claude/.gitignore`) that lists each symlink subdir
|
|
188
|
+
(`skills/`, `commands/`, …). Only the canonical `.agents/` content (which you
|
|
189
|
+
commit) is version-controlled. Global scope skips this (it is not inside a
|
|
190
|
+
repo).
|
|
191
|
+
|
|
192
|
+
Once `.agents/` holds every item and the script is installed, **delete the
|
|
193
|
+
original item files/directories at each platform source dir**. The canonical
|
|
194
|
+
copy now lives in `.agents/`, so the originals are redundant — and because the
|
|
195
|
+
symlink step refuses to overwrite a real path, it cannot link until the
|
|
196
|
+
original is gone. Then run the setup script once to create the symlinks; every
|
|
197
|
+
later session-start run re-applies them idempotently.
|
|
198
|
+
|
|
199
|
+
### Phase 6 — Install session-start hooks
|
|
200
|
+
|
|
201
|
+
For each in-use platform, register a session-start hook that runs the
|
|
202
|
+
installed setup script in `BASE/.agents/hooks/` (`agent-setup.sh` on
|
|
203
|
+
macOS/Linux, `agent-setup.ps1` on Windows), writing to the config file in
|
|
204
|
+
`reference/mapping.md`:
|
|
205
|
+
|
|
206
|
+
| Platform | Project file | Global file | Format |
|
|
207
|
+
|---|---|---|---|
|
|
208
|
+
| Claude Code | `.claude/settings.json` | `~/.claude/settings.json` | JSON |
|
|
209
|
+
| Gemini / Antigravity | `.gemini/settings.json` | `~/.gemini/settings.json` | JSON |
|
|
210
|
+
| Codex | `.codex/config.toml` | `~/.codex/config.toml` | TOML |
|
|
211
|
+
| Windsurf | — | — | no session-start hook (skip) |
|
|
212
|
+
| GitHub Copilot | `.github/hooks/agent-setup.json` | `~/.copilot/hooks/agent-setup.json` | JSON |
|
|
213
|
+
|
|
214
|
+
Rules for every write:
|
|
215
|
+
|
|
216
|
+
- **Append, never overwrite.** Parse the existing file, inject the hook entry
|
|
217
|
+
only if an equivalent one is absent, then write back. Preserve all other
|
|
218
|
+
keys and formatting as much as the format allows.
|
|
219
|
+
- **Path style follows scope.** The hook command points at the setup script:
|
|
220
|
+
use a **relative** path (`.agents/hooks/agent-setup.sh`) for project scope
|
|
221
|
+
and an **absolute** path for global scope — matching how `__AGENTS_DIR__`
|
|
222
|
+
was filled.
|
|
223
|
+
- Back up the file (`<file>.bak`) before the first modification.
|
|
224
|
+
- Use the verified template for each platform in `reference/hook-templates/`
|
|
225
|
+
(`claude-code.json`, `gemini.json`, `codex.toml`, `copilot.json`).
|
|
226
|
+
**Windsurf has no session-start hook**, so it gets no recurring hook. Keep
|
|
227
|
+
its dirs in the Phase 5 `__LINK_MAP__` so the one-time setup run symlinks
|
|
228
|
+
them, but tell the user the shared `.agents/` will not auto-refresh on
|
|
229
|
+
Windsurf — they re-run the setup script manually to pick up later changes.
|
|
230
|
+
Codex prompts for a trust review of project hooks on first use.
|
|
231
|
+
|
|
232
|
+
### Phase 7 — Report
|
|
233
|
+
|
|
234
|
+
Summarize: scope, platforms processed, items consolidated per type, how
|
|
235
|
+
master instructions were resolved, which hooks were installed (and which were
|
|
236
|
+
skipped/unverified), and any collisions or backups created.
|
|
237
|
+
|
|
238
|
+
## Constraints
|
|
239
|
+
|
|
240
|
+
- Append-only for existing config (settings/hook) files; back up first.
|
|
241
|
+
Master instruction files are the exception: replace their contents with the
|
|
242
|
+
`@AGENTS.md` import once the content is migrated into `AGENTS.md`.
|
|
243
|
+
- Per-item symlinks, never whole-directory, never clobber real paths.
|
|
244
|
+
- Do not fabricate hook schemas; verify unverified platforms at runtime.
|
|
245
|
+
- Do not modify this repo's `src/`, CLI, or `paths.ts`.
|