@kurokeita/add-skill 2.6.2 → 3.0.1

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.
Files changed (140) hide show
  1. package/README.md +43 -64
  2. package/dist/bin/cli.js +270 -478
  3. package/dist/skills/statusline-setup/.claude-plugin/plugin.json +1 -1
  4. package/dist/skills/statusline-setup/SKILL.md +21 -436
  5. package/dist/skills/statusline-setup/mod/hooks/register.tsx +93 -103
  6. package/dist/skills/statusline-setup/mod/tests/work.test.ts +28 -10
  7. package/dist/skills/statusline-setup/mod/types/index.d.ts +2 -1
  8. package/dist/skills/universalize-agents/reference/mapping.md +3 -2
  9. package/package.json +3 -3
  10. package/dist/agents/backend-specialist.md +0 -277
  11. package/dist/agents/code-archaeologist.md +0 -114
  12. package/dist/agents/database-architect.md +0 -238
  13. package/dist/agents/debugger.md +0 -231
  14. package/dist/agents/devops-engineer.md +0 -242
  15. package/dist/agents/documentation-writer.md +0 -104
  16. package/dist/agents/explorer-agent.md +0 -79
  17. package/dist/agents/frontend-specialist.md +0 -594
  18. package/dist/agents/game-developer.md +0 -162
  19. package/dist/agents/mobile-developer.md +0 -383
  20. package/dist/agents/orchestrator.md +0 -435
  21. package/dist/agents/penetration-tester.md +0 -188
  22. package/dist/agents/performance-optimizer.md +0 -191
  23. package/dist/agents/product-manager.md +0 -122
  24. package/dist/agents/product-owner.md +0 -106
  25. package/dist/agents/project-planner.md +0 -408
  26. package/dist/agents/qa-automation-engineer.md +0 -109
  27. package/dist/agents/security-auditor.md +0 -171
  28. package/dist/agents/test-engineer.md +0 -158
  29. package/dist/skills/agent-identifier/SKILL.md +0 -431
  30. package/dist/skills/agent-identifier/examples/agent-creation-prompt.md +0 -247
  31. package/dist/skills/agent-identifier/examples/complete-agent-examples.md +0 -432
  32. package/dist/skills/agent-identifier/references/agent-creation-system-prompt.md +0 -215
  33. package/dist/skills/agent-identifier/references/system-prompt-design.md +0 -419
  34. package/dist/skills/agent-identifier/references/triggering-examples.md +0 -508
  35. package/dist/skills/agent-identifier/scripts/validate-agent.sh +0 -217
  36. package/dist/skills/code-review-excellence/SKILL.md +0 -525
  37. package/dist/skills/component-refactoring/SKILL.md +0 -492
  38. package/dist/skills/component-refactoring/references/complexity-patterns.md +0 -493
  39. package/dist/skills/component-refactoring/references/component-splitting.md +0 -477
  40. package/dist/skills/component-refactoring/references/hook-extraction.md +0 -317
  41. package/dist/skills/context7-guidelines/SKILL.md +0 -31
  42. package/dist/skills/feature-design-assistant/SKILL.md +0 -371
  43. package/dist/skills/find-bugs/SKILL.md +0 -75
  44. package/dist/skills/frontend-design/SKILL.md +0 -45
  45. package/dist/skills/go-concurrency-patterns/SKILL.md +0 -657
  46. package/dist/skills/grill-me/SKILL.md +0 -9
  47. package/dist/skills/handoff/SKILL.md +0 -16
  48. package/dist/skills/koreader-plugin-development/SKILL.md +0 -141
  49. package/dist/skills/koreader-plugin-development/examples/2-example-patch.lua +0 -17
  50. package/dist/skills/koreader-plugin-development/examples/_meta.lua +0 -6
  51. package/dist/skills/koreader-plugin-development/examples/helloworld.lua +0 -36
  52. package/dist/skills/koreader-plugin-development/references/debugging.md +0 -92
  53. package/dist/skills/koreader-plugin-development/references/events-and-widgets.md +0 -102
  54. package/dist/skills/koreader-plugin-development/references/patches.md +0 -118
  55. package/dist/skills/koreader-plugin-development/references/plugin-anatomy.md +0 -97
  56. package/dist/skills/make-instruction/SKILL.md +0 -68
  57. package/dist/skills/make-prompt/SKILL.md +0 -70
  58. package/dist/skills/make-skill/SKILL.md +0 -147
  59. package/dist/skills/prd-to-tasks/SKILL.md +0 -164
  60. package/dist/skills/react-native-async-patterns/SKILL.md +0 -256
  61. package/dist/skills/react-native-performance/SKILL.md +0 -425
  62. package/dist/skills/react-native-testing/SKILL.md +0 -458
  63. package/dist/skills/react-native-zustand-patterns/SKILL.md +0 -343
  64. package/dist/skills/skill-creator/SKILL.md +0 -385
  65. package/dist/skills/skill-creator/scripts/init_skill.cjs +0 -235
  66. package/dist/skills/skill-creator/scripts/package_skill.cjs +0 -84
  67. package/dist/skills/skill-creator/scripts/validate_skill.cjs +0 -127
  68. package/dist/skills/skill-development/SKILL.md +0 -676
  69. package/dist/skills/skill-development/references/skill-creator-original.md +0 -210
  70. package/dist/skills/sql-optimization-patterns/SKILL.md +0 -509
  71. package/dist/skills/test-driven-development/SKILL.md +0 -114
  72. package/dist/skills/test-driven-development/deep-modules.md +0 -33
  73. package/dist/skills/test-driven-development/interface-design.md +0 -35
  74. package/dist/skills/test-driven-development/mocking.md +0 -68
  75. package/dist/skills/test-driven-development/refactoring.md +0 -10
  76. package/dist/skills/test-driven-development/tests.md +0 -61
  77. package/dist/skills/vercel-react-best-practices/AGENTS.md +0 -2410
  78. package/dist/skills/vercel-react-best-practices/SKILL.md +0 -127
  79. package/dist/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +0 -55
  80. package/dist/skills/vercel-react-best-practices/rules/advanced-use-latest.md +0 -49
  81. package/dist/skills/vercel-react-best-practices/rules/async-api-routes.md +0 -38
  82. package/dist/skills/vercel-react-best-practices/rules/async-defer-await.md +0 -80
  83. package/dist/skills/vercel-react-best-practices/rules/async-dependencies.md +0 -36
  84. package/dist/skills/vercel-react-best-practices/rules/async-parallel.md +0 -28
  85. package/dist/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +0 -99
  86. package/dist/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +0 -59
  87. package/dist/skills/vercel-react-best-practices/rules/bundle-conditional.md +0 -31
  88. package/dist/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +0 -49
  89. package/dist/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +0 -35
  90. package/dist/skills/vercel-react-best-practices/rules/bundle-preload.md +0 -50
  91. package/dist/skills/vercel-react-best-practices/rules/client-event-listeners.md +0 -74
  92. package/dist/skills/vercel-react-best-practices/rules/client-localstorage-schema.md +0 -71
  93. package/dist/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +0 -48
  94. package/dist/skills/vercel-react-best-practices/rules/client-swr-dedup.md +0 -56
  95. package/dist/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +0 -82
  96. package/dist/skills/vercel-react-best-practices/rules/js-cache-function-results.md +0 -80
  97. package/dist/skills/vercel-react-best-practices/rules/js-cache-property-access.md +0 -28
  98. package/dist/skills/vercel-react-best-practices/rules/js-cache-storage.md +0 -70
  99. package/dist/skills/vercel-react-best-practices/rules/js-combine-iterations.md +0 -32
  100. package/dist/skills/vercel-react-best-practices/rules/js-early-exit.md +0 -50
  101. package/dist/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +0 -45
  102. package/dist/skills/vercel-react-best-practices/rules/js-index-maps.md +0 -37
  103. package/dist/skills/vercel-react-best-practices/rules/js-length-check-first.md +0 -50
  104. package/dist/skills/vercel-react-best-practices/rules/js-min-max-loop.md +0 -82
  105. package/dist/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +0 -24
  106. package/dist/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +0 -57
  107. package/dist/skills/vercel-react-best-practices/rules/rendering-activity.md +0 -26
  108. package/dist/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +0 -47
  109. package/dist/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +0 -40
  110. package/dist/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +0 -38
  111. package/dist/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +0 -46
  112. package/dist/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +0 -82
  113. package/dist/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +0 -28
  114. package/dist/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +0 -39
  115. package/dist/skills/vercel-react-best-practices/rules/rerender-dependencies.md +0 -45
  116. package/dist/skills/vercel-react-best-practices/rules/rerender-derived-state.md +0 -29
  117. package/dist/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +0 -74
  118. package/dist/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +0 -58
  119. package/dist/skills/vercel-react-best-practices/rules/rerender-memo.md +0 -44
  120. package/dist/skills/vercel-react-best-practices/rules/rerender-transitions.md +0 -40
  121. package/dist/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +0 -73
  122. package/dist/skills/vercel-react-best-practices/rules/server-cache-lru.md +0 -41
  123. package/dist/skills/vercel-react-best-practices/rules/server-cache-react.md +0 -76
  124. package/dist/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +0 -83
  125. package/dist/skills/vercel-react-best-practices/rules/server-serialization.md +0 -38
  126. package/dist/skills/web-design-guidelines/SKILL.md +0 -40
  127. package/dist/skills/workflow-patterns/SKILL.md +0 -623
  128. package/dist/skills/write-prd/SKILL.md +0 -162
  129. package/dist/workflows/brainstorm.md +0 -113
  130. package/dist/workflows/create.md +0 -60
  131. package/dist/workflows/debug.md +0 -107
  132. package/dist/workflows/deploy.md +0 -178
  133. package/dist/workflows/enhance.md +0 -64
  134. package/dist/workflows/github-pull-request-create.md +0 -134
  135. package/dist/workflows/orchestrate.md +0 -246
  136. package/dist/workflows/plan.md +0 -90
  137. package/dist/workflows/preview.md +0 -83
  138. package/dist/workflows/status.md +0 -87
  139. package/dist/workflows/test.md +0 -144
  140. package/dist/workflows/ui-ux-pro-max.md +0 -315
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "work-items",
2
+ "name": "work-pane",
3
3
  "version": "0.1.0",
4
4
  "description": "Work pane: this session's PRs, work items, summary and steps, fetched with gh or az",
5
5
  "hooks": "./mod/hooks/hooks.json",
@@ -1,22 +1,22 @@
1
1
  ---
2
2
  name: statusline-setup
3
- description: 'Set up the Claude Code or Gemini/Antigravity CLI statusline as side-by-side bordered boxes: a repo box (cwd, git branch, working-tree state, last commit), a session box (model, context % + token totals, 5h/7d quota with ETA), and a work box (the current branch''s pull request and its linked issues or work items, fetched from GitHub with gh or Azure DevOps with az). Use when asked to "set up my statusline", "install statusline", "configure statusline", or to add a colored multi-line status line showing rate limits, token usage, or the PR and work items for the current branch. Detects OS and installs the appropriate variant (bash + jq on Linux/macOS, PowerShell on Windows).'
3
+ description: 'Set up the Claude Code or Gemini/Antigravity CLI statusline as side-by-side bordered boxes: a repo box (cwd, git branch, working-tree state, last commit), a session box (model, context % + token totals, 5h/7d quota with ETA). Also ships the Claude Code work pane (`/work`), which shows the session''s pull requests and work items. Use when asked to "set up my statusline", "install statusline", "configure statusline", or to add a colored multi-line status line showing rate limits or 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 up to three side-by-side bordered boxes:
8
+ Install a Claude Code or Gemini/Antigravity statusline that renders two side-by-side bordered boxes:
9
9
 
10
10
  ```text
11
- ╭─ repo ──────────────────────────────────────────────────────╮ ╭─ session ─────────────╮ ╭─ work ───────────────────────╮
12
- │ ~/dev/ai_agents │ │ Opus 5 (high) │ │ PR #46 open approved │
13
- │ feat/statusline-boxed-layout │ │ ctx 42% ↑1.2M ↓18.4k │ │ #45 Show PRs in statusline │
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
- ╰─────────────────────────────────────────────────────────────╯ ╰───────────────────────╯ ╰──────────────────────────────╯
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
17
  ```
18
18
 
19
- The work box appears when the current branch has a pull request or related work items. Its rows are clickable links. In Claude Code, `/work` opens the same data in a scrollable pane (see Work Pane).
19
+ In Claude Code, `/work` shows the session's pull requests and work items in a side pane (see Work Pane).
20
20
 
21
21
  ## Box Contents
22
22
 
@@ -36,20 +36,11 @@ Rows 2-4 appear only inside a git repo.
36
36
  3. **`5h N% resets <eta>`** — colored by remaining quota (see below).
37
37
  4. **`7d N% resets <eta>`** — colored independently of the 5h row.
38
38
 
39
- ### work box
40
-
41
- 1. **pull request** in `#009AFB` (`38;2;0;154;251`): `PR #<n> <state>` on GitHub, `PR !<n> <state>` on Azure DevOps, followed by a review word when there is one. See [Work Box Data](#work-box-data) for the state and review words.
42
- 2. **related items**, one per row, in dim `#787e8a`: `#<number> <title>`, truncated to 50 characters with a trailing `…`. With a PR, GitHub lists the issues the PR closes and Azure DevOps lists the work items linked to the PR. Without a PR, the items come from the branch name and the branch's commits (see [Branch refs](#branch-refs)). At most 3 rows; any more collapse into one `+N more` row.
43
-
44
- Every row with a URL is wrapped in an OSC 8 hyperlink (`ESC ]8;;<url> BEL` … `ESC ]8;; BEL`), so Cmd+click (macOS) or Ctrl+click opens the PR or item. The escape goes in the colored copy only, never the plain copy used for width. Terminal.app shows the text but cannot click it.
45
-
46
- The box is left out entirely when it has no rows: detached HEAD, a remote that is neither GitHub nor Azure DevOps, `gh`/`az` not installed, no PR and no related items, or no data fetched yet.
47
-
48
39
  ## Box Rendering Rules
49
40
 
50
41
  - Border glyphs `╭ ╮ ╰ ╯ ─ │` in `#5a5f69` (`38;2;90;95;105`); the box title in `#8c929e` (`38;2;140;146;158`).
51
42
  - Each box's inner width is the longest of its content rows, or its title + 2, whichever is larger.
52
- - All rendered boxes are padded to the **same row count** with blank rows, so the frames start and end on the same terminal lines.
43
+ - Both boxes are padded to the **same row count** with blank rows, so the frames start and end on the same terminal lines.
53
44
  - The boxes are joined by a single space on every line.
54
45
  - Rows are emitted newline-separated; the statusline host renders them as multiple lines.
55
46
  - When the repo and session boxes both have zero content rows the script prints nothing and exits 0.
@@ -136,68 +127,6 @@ The last-commit row needs one further call: `git -C "$cwd" log -1 --format='%h %
136
127
 
137
128
  Every git call must skip optional locks: export `GIT_OPTIONAL_LOCKS=0` once at the top of the script. A plain `git status` takes `.git/index.lock` to refresh stat info, and the statusline re-runs on every turn, so it races the user's own `git add`/`commit` and makes them fail with `Unable to create '.git/index.lock': File exists`.
138
129
 
139
- ## Work Box Data
140
-
141
- The statusline payload says nothing about pull requests, so the current branch stands in for the session.
142
-
143
- ### Platform detection
144
-
145
- Read the remote with `git -C "$cwd" remote get-url origin`. This is the one git call outside the single status call.
146
-
147
- | Remote matches | Platform | CLI |
148
- |----------------|----------|-----|
149
- | `github.com:` or `github.com/` | GitHub | `gh` |
150
- | `dev.azure.com:`, `dev.azure.com/` or `.visualstudio.com/` | Azure DevOps | `az` with the `azure-devops` extension |
151
-
152
- Anything else, or a missing CLI, renders no work box. Both CLIs read the repository from the current directory, so every fetch runs inside `cd "$cwd"` in the background job.
153
-
154
- ### Branch refs
155
-
156
- Without a PR, the related items are the item numbers found locally, in this order, with duplicates and leading zeros removed and at most 10 kept:
157
-
158
- 1. A 3 to 9 digit segment of the branch name, splitting on anything that is not a letter or digit (`user/minhle/43553/feat/x` → `43553`). On GitHub, also the leading number of a `gh issue develop` name (`41-fix-version` → `41`).
159
- 2. Refs in the messages of commits on the branch but not on the default branch (`git log --format=%B <origin/HEAD>..HEAD`): `AB#<n>` on Azure DevOps, `#<n>` on GitHub. No `refs/remotes/origin/HEAD` means no commit scan.
160
-
161
- Ids are capped at 9 digits because both APIs take 32-bit ids, and one out-of-range literal fails the whole query. A number that is not an item is dropped silently.
162
-
163
- ### Fetch
164
-
165
- - **GitHub**: one `gh api graphql` call. `-F owner='{owner}' -F name='{repo}'` lets `gh` fill in the repository; pass the branch with `-f` so a numeric branch name stays a string. Query the newest PR whose head is the branch (`states: [OPEN, MERGED, CLOSED]`) with `number state isDraft reviewDecision url` and `closingIssuesReferences(first: 10) { nodes { number title url } }`. The same query carries one `i<n>: issueOrPullRequest(number: <n>)` alias per branch ref, used only when there is no PR. `gh` exits 1 when a ref does not exist but still prints the rest, so judge success by `.data.repository`, not the exit code.
166
- - **Azure DevOps**: `az repos pr list --source-branch <branch> --status all --top 1`, then `az repos pr work-item list --id <n>` for the linked work items. Without a PR, one `az boards query --wiql "SELECT [System.Id], [System.Title] FROM WorkItems WHERE [System.Id] IN (<refs>)"` instead. Pass `--only-show-errors -o json` to every call. `--detect` (on by default) scopes them to the repository of the current directory.
167
-
168
- ### Links
169
-
170
- | Row | GitHub | Azure DevOps |
171
- |-----|--------|--------------|
172
- | PR | `url` | API `url` with `/_apis/git/repositories/` → `/_git/` and `/pullRequests/` → `/pullrequest/` |
173
- | item | `url` | API `url` with `/_apis/wit/workItems/` → `/_workitems/edit/` |
174
-
175
- Only `https://` URLs without spaces become links; anything else renders as plain text.
176
-
177
- ### Row vocabulary
178
-
179
- | Field | GitHub | Azure DevOps |
180
- |-------|--------|--------------|
181
- | state | `draft` when an open PR is a draft, else `open`, `merged`, `closed` | `draft` when an active PR is a draft, else `active` → `open`, `completed` → `merged`, `abandoned` → `closed` |
182
- | review | `reviewDecision`: `APPROVED` → `approved`, `CHANGES_REQUESTED` → `changes requested`, `REVIEW_REQUIRED` → `review required`, null → nothing | reviewer votes: any `-10` → `rejected`, else any `-5` → `waiting for author`, else any `5` or `10` → `approved`, else nothing |
183
- | item | `#<issue number> <issue title>` | `#<work item id> <System.Title>` |
184
-
185
- Tabs and newlines in titles become spaces, so each row stays one line and the tab before the URL stays unique.
186
-
187
- ### Cache and background refresh
188
-
189
- `gh` takes about 0.7s and the two `az` calls about 5s, and the statusline re-runs on every turn. The render path therefore **never calls `gh` or `az`**:
190
-
191
- 1. The cache lives in a per-user directory: `${TMPDIR:-/tmp}/statusline-work-$UID` (mode 700) or `%TEMP%\statusline-work`. The file name is a hash of `<remote URL>|<branch>`, so a token embedded in the remote URL never lands in a file name.
192
- 2. `<cache>.at` holds the epoch of the last fetch attempt. When it is missing or older than 60 seconds, the script writes the current epoch to it, starts a detached refresh, and carries on rendering.
193
- 3. The refresh writes the rows to a temporary file, then renames it over `<cache>`, so a half-written cache never renders. A failed fetch (offline, logged out) leaves the previous rows in place; the `.at` stamp stops a retry until the next 60-second window.
194
- 4. The render reads `<cache>`: one `<text><TAB><url>` row per line, the `PR` row first, then the item rows. It strips control characters from the text and the URL first, because titles come from other people and could otherwise inject terminal escape sequences.
195
-
196
- Detaching rules, or the host waits for the fetch and the statusline stalls:
197
-
198
- - **bash**: run the refresh as `( ... ) </dev/null >/dev/null 2>&1 &` followed by `disown`. No file descriptor may stay attached to the statusline's stdout.
199
- - **PowerShell**: `Start-Job` dies with its parent, so re-invoke the same script with `Start-Process -WindowStyle Hidden` and a `-Refresh` argument.
200
-
201
130
  ## Quota Colors (by REMAINING quota = 100 - used%)
202
131
 
203
132
  | Remaining | Color | 24-bit ANSI |
@@ -562,163 +491,17 @@ if [[ -n "$seven_used" ]]; then
562
491
  sess_plain+=("$s"); sess_color+=("$(quota_color "$su")${s}${RESET}")
563
492
  fi
564
493
 
565
- # --- work box: PR + linked items for the current branch ---
566
- # gh takes ~0.7s and az ~5s, so rendering only reads a cache; a stale cache
567
- # starts a detached refresh and this turn shows the previous rows.
568
- WORK_TTL=60
569
-
570
- # Item numbers related to the branch without a PR: a 3-9 digit segment of the
571
- # branch name (plus gh issue develop's "<n>-slug"), then AB#N (Azure DevOps) or
572
- # #N (GitHub) in commits not yet on the default branch. Capped at 9 digits
573
- # because both APIs take 32-bit ids, and one bad literal fails the whole query.
574
- item_refs() {
575
- local platform="$1" branch="$2" base pat
576
- {
577
- [[ "$platform" == github && "$branch" =~ ^([0-9]{1,9})- ]] && echo "${BASH_REMATCH[1]}"
578
- printf '%s\n' "$branch" | tr -c 'A-Za-z0-9' '\n' | grep -Ex '[0-9]{3,9}'
579
- base=$(git symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null)
580
- if [[ -n "$base" ]]; then
581
- if [[ "$platform" == github ]]; then pat='#[0-9]{1,9}'; else pat='AB#[0-9]{1,9}'; fi
582
- git log --format=%B "$base..HEAD" 2>/dev/null | grep -Eo "$pat" | tr -cd '0-9\n'
583
- fi
584
- } | awk 'NF { n = $0 + 0; if (!seen[n]++) print n }' | head -n 10
585
- }
586
-
587
- # Prints the PR row, then one row per linked item, each as "<text>\t<url>".
588
- # Without a PR, the items come from item_refs instead. Fails on a CLI error so
589
- # the caller keeps the previous cache. Runs inside the repo (gh/az detect from PWD).
590
- fetch_work() {
591
- local platform="$1" branch="$2" refs out pr id n q=""
592
- local title='gsub("[\t\r\n]"; " ")'
593
- refs=$(item_refs "$platform" "$branch")
594
- if [[ "$platform" == github ]]; then
595
- for n in $refs; do
596
- q+=" i$n: issueOrPullRequest(number: $n) {
597
- ... on Issue { number title url } ... on PullRequest { number title url } }"
598
- done
599
- # gh exits 1 when a ref is not an issue but still prints the rest, so
600
- # success is judged by .data.repository instead of the exit code.
601
- out=$(gh api graphql -F owner='{owner}' -F name='{repo}' -f branch="$branch" -f query='
602
- query($owner: String!, $name: String!, $branch: String!) {
603
- repository(owner: $owner, name: $name) {
604
- pullRequests(headRefName: $branch, first: 1, states: [OPEN, MERGED, CLOSED],
605
- orderBy: {field: CREATED_AT, direction: DESC}) {
606
- nodes {
607
- number state isDraft reviewDecision url
608
- closingIssuesReferences(first: 10) { nodes { number title url } }
609
- }
610
- }
611
- '"$q"'
612
- }
613
- }')
614
- jq -r '.data.repository // error("no repository")
615
- | if .pullRequests.nodes[0] then
616
- .pullRequests.nodes[0]
617
- | "PR #\(.number) \(if .isDraft and .state == "OPEN" then "draft" else (.state | ascii_downcase) end)"
618
- + ({"APPROVED": " approved", "CHANGES_REQUESTED": " changes requested",
619
- "REVIEW_REQUIRED": " review required"}[.reviewDecision // ""] // "")
620
- + "\t\(.url)",
621
- (.closingIssuesReferences.nodes[] | "#\(.number) \(.title | '"$title"')\t\(.url)")
622
- else
623
- to_entries[] | select(.key | startswith("i")) | .value // empty
624
- | "#\(.number) \(.title | '"$title"')\t\(.url)"
625
- end' <<<"$out"
626
- else
627
- local rows='.[] | "#\(.id) \(.fields."System.Title" | '"$title"')\t\(.url | sub("/_apis/wit/workItems/"; "/_workitems/edit/"))"'
628
- pr=$(az repos pr list --source-branch "$branch" --status all --top 1 --only-show-errors -o json) || return 1
629
- id=$(jq -r '.[0].pullRequestId // empty' <<<"$pr")
630
- if [[ -n "$id" ]]; then
631
- jq -r '.[0]
632
- | "PR !\(.pullRequestId) \(if .isDraft and .status == "active" then "draft"
633
- else ({"active": "open", "completed": "merged", "abandoned": "closed"}[.status] // .status) end)"
634
- + ([.reviewers[]?.vote] | if any(. == -10) then " rejected"
635
- elif any(. == -5) then " waiting for author"
636
- elif any(. >= 5) then " approved" else "" end)
637
- + "\t\(.url | sub("/_apis/git/repositories/"; "/_git/") | sub("/pullRequests/"; "/pullrequest/"))"' <<<"$pr"
638
- az repos pr work-item list --id "$id" --only-show-errors -o json | jq -r "$rows"
639
- elif [[ -n "$refs" ]]; then
640
- az boards query --only-show-errors -o json \
641
- --wiql "SELECT [System.Id], [System.Title] FROM WorkItems WHERE [System.Id] IN (${refs//$'\n'/, })" \
642
- | jq -r "$rows"
643
- fi
644
- fi
645
- }
646
-
647
- work_plain=(); work_color=()
648
- platform=""
649
- if [[ -n "$branch" && "$branch" != detached@* ]]; then
650
- remote=$(git -C "$cwd" remote get-url origin 2>/dev/null)
651
- case "$remote" in
652
- *github.com[:/]*)
653
- command -v gh >/dev/null 2>&1 && platform=github ;;
654
- *dev.azure.com[:/]*|*.visualstudio.com/*)
655
- command -v az >/dev/null 2>&1 && platform=azure ;;
656
- esac
657
- fi
658
-
659
- if [[ -n "$platform" ]]; then
660
- work_dir="${TMPDIR:-/tmp}/statusline-work-${UID}"
661
- mkdir -p -m 700 "$work_dir"
662
- # Hashed so a token embedded in the remote URL never lands in a file name.
663
- read -r key _ < <(printf '%s|%s' "$remote" "$branch" | cksum)
664
- cache="$work_dir/$key"
665
- now=$(date +%s); at=0
666
- [[ -f "$cache.at" ]] && read -r at < "$cache.at"
667
- if (( now - ${at:-0} > WORK_TTL )); then
668
- printf '%s\n' "$now" > "$cache.at"
669
- # Every fd is detached, or the host waits for the fetch to finish.
670
- (
671
- set -o pipefail
672
- cd "$cwd" && rows=$(fetch_work "$platform" "$branch") \
673
- && printf '%s\n' "$rows" > "$cache.$$" && mv -f "$cache.$$" "$cache"
674
- ) </dev/null >/dev/null 2>&1 &
675
- disown
676
- fi
677
-
678
- items=0
679
- if [[ -f "$cache" ]]; then
680
- while IFS=$'\t' read -r line url; do
681
- # Titles come from other people; drop escapes before they reach the terminal.
682
- line=${line//[[:cntrl:]]/}; url=${url//[[:cntrl:]]/}
683
- [[ -z "$line" ]] && continue
684
- [[ "$url" == https://* && "$url" != *' '* ]] || url=""
685
- if [[ "$line" == 'PR '* ]]; then
686
- color="$C_CTX"
687
- elif (( ++items <= 3 )); then
688
- color="$C_DIM"; line=$(truncate_to "$line" 50)
689
- else
690
- continue
691
- fi
692
- # OSC 8 makes the row clickable; it lives only in the colored copy.
693
- colored="${color}${line}${RESET}"
694
- [[ -n "$url" ]] && colored=$'\033]8;;'"${url}"$'\a'"${colored}"$'\033]8;;\a'
695
- work_plain+=("$line"); work_color+=("$colored")
696
- done < "$cache"
697
- fi
698
- if (( items > 3 )); then
699
- more="+$(( items - 3 )) more"
700
- work_plain+=("$more"); work_color+=("${C_DIM}${more}${RESET}")
701
- fi
702
- fi
703
- work_rows=${#work_plain[@]}
704
-
705
494
  rows=${#repo_plain[@]}
706
495
  (( ${#sess_plain[@]} > rows )) && rows=${#sess_plain[@]}
707
496
  if (( rows == 0 )); then printf ''; exit 0; fi
708
- (( work_rows > rows )) && rows=$work_rows
709
497
  for (( i = ${#repo_plain[@]}; i < rows; i++ )); do repo_plain+=(""); repo_color+=(""); done
710
498
  for (( i = ${#sess_plain[@]}; i < rows; i++ )); do sess_plain+=(""); sess_color+=(""); done
711
- if (( work_rows )); then
712
- for (( i = work_rows; i < rows; i++ )); do work_plain+=(""); work_color+=(""); done
713
- fi
714
499
 
715
500
  # Width comes from the plain rows; the parallel colored rows carry ANSI escapes
716
501
  # that would otherwise be counted as visible characters. ${#s} counts characters,
717
502
  # but emoji and CJK take two terminal columns, so one perl call measures every
718
503
  # row by grapheme. Each box holds `rows` rows, so box N's widths start at N*rows.
719
504
  all=("${repo_plain[@]}" "${sess_plain[@]}")
720
- # An empty array trips `set -u` on bash 3.2, so only expand it when it has rows.
721
- (( work_rows )) && all+=("${work_plain[@]}")
722
505
  cols=()
723
506
  while IFS= read -r n; do cols+=("$n"); done < <(printf '%s\n' "${all[@]}" \
724
507
  | perl -CS -ne 'chomp; my $w = 0; $w += /[\p{Ea=W}\p{Ea=F}\x{FE0F}]/ ? 2 : 1 for /\X/g; print "$w\n"' 2>/dev/null)
@@ -736,7 +519,6 @@ box_width() {
736
519
  }
737
520
  rw=$(box_width "repo" "${cols[@]:0:rows}")
738
521
  sw=$(box_width "session" "${cols[@]:rows:rows}")
739
- (( work_rows )) && ww=$(box_width "work" "${cols[@]:2*rows:rows}")
740
522
 
741
523
  box_top() {
742
524
  local title="$1" w="$2"
@@ -753,15 +535,12 @@ box_row() {
753
535
  }
754
536
 
755
537
  out="$(box_top repo "$rw") $(box_top session "$sw")"
756
- (( work_rows )) && out+=" $(box_top work "$ww")"
757
538
  for (( i = 0; i < rows; i++ )); do
758
539
  out+=$'\n'
759
540
  out+="$(box_row "${repo_color[$i]}" "$rw" "${cols[$i]}")"
760
541
  out+=" $(box_row "${sess_color[$i]}" "$sw" "${cols[rows + i]}")"
761
- (( work_rows )) && out+=" $(box_row "${work_color[$i]}" "$ww" "${cols[2 * rows + i]}")"
762
542
  done
763
543
  out+=$'\n'"$(box_bottom "$rw") $(box_bottom "$sw")"
764
- (( work_rows )) && out+=" $(box_bottom "$ww")"
765
544
 
766
545
  printf '%s' "$out"
767
546
  ```
@@ -773,9 +552,6 @@ Write `statusline-command.ps1` to the targeted platform directory. Use `ConvertF
773
552
  `statusline-command.ps1`:
774
553
 
775
554
  ```powershell
776
- # -Refresh is only passed by the script itself, to fetch the work box in the background.
777
- param([string]$Refresh, [string]$Branch, [string]$Cache)
778
-
779
555
  $ErrorActionPreference = 'SilentlyContinue'
780
556
  # The statusline re-runs on every Claude turn. A plain `git status` grabs
781
557
  # .git/index.lock to refresh stat info, racing the user's own git writes.
@@ -783,7 +559,6 @@ $env:GIT_OPTIONAL_LOCKS = '0'
783
559
  [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
784
560
  $OutputEncoding = [System.Text.Encoding]::UTF8
785
561
  $ESC = [char]27
786
- $BEL = [char]7
787
562
 
788
563
  $C_CWD = "$ESC[38;2;94;255;255m"
789
564
  $C_BRANCH = "$ESC[38;2;194;72;112m"
@@ -879,96 +654,6 @@ function SumTokens($path) {
879
654
  return ,@($sumIn, $sumOut)
880
655
  }
881
656
 
882
- $WORK_TTL = 60
883
-
884
- # Item numbers related to the branch without a PR: a 3-9 digit segment of the
885
- # branch name (plus gh issue develop's "<n>-slug"), then AB#N (Azure DevOps) or
886
- # #N (GitHub) in commits not yet on the default branch. Capped at 9 digits
887
- # because both APIs take 32-bit ids, and one bad literal fails the whole query.
888
- function ItemRefs($platform, $branch) {
889
- $refs = @()
890
- if ($platform -eq "github" -and $branch -match '^(\d{1,9})-') { $refs += $Matches[1] }
891
- $refs += @($branch -split '[^A-Za-z0-9]' | Where-Object { $_ -match '^\d{3,9}$' })
892
- $base = & git symbolic-ref --short refs/remotes/origin/HEAD 2>$null
893
- if ($base) {
894
- if ($platform -eq "github") { $pat = '#(\d{1,9})' } else { $pat = 'AB#(\d{1,9})' }
895
- $log = (& git log --format=%B "$base..HEAD" 2>$null) -join "`n"
896
- foreach ($m in [regex]::Matches($log, $pat)) { $refs += $m.Groups[1].Value }
897
- }
898
- return @($refs | ForEach-Object { [int]$_ } | Select-Object -Unique | Select-Object -First 10)
899
- }
900
-
901
- function OneLine($s) { return ([string]$s) -replace '[\t\r\n]', ' ' }
902
-
903
- # Returns the PR row, then one row per linked item, each as "<text>`t<url>",
904
- # or $null on a CLI error so the caller keeps the previous cache. Without a PR,
905
- # the items come from ItemRefs instead. Runs inside the repo (gh/az detect from PWD).
906
- function FetchWork($platform, $branch) {
907
- $rows = @()
908
- $refs = ItemRefs $platform $branch
909
- if ($platform -eq "github") {
910
- $q = ''
911
- foreach ($n in $refs) { $q += " i$($n): issueOrPullRequest(number: $n) { ... on Issue { number title url } ... on PullRequest { number title url } }" }
912
- # No double quotes: Windows PowerShell 5.1 strips them from native arguments.
913
- $query = 'query($owner: String!, $name: String!, $branch: String!) { repository(owner: $owner, name: $name) { pullRequests(headRefName: $branch, first: 1, states: [OPEN, MERGED, CLOSED], orderBy: {field: CREATED_AT, direction: DESC}) { nodes { number state isDraft reviewDecision url closingIssuesReferences(first: 10) { nodes { number title url } } } }' + $q + ' } }'
914
- # gh exits 1 when a ref is not an issue but still prints the rest, so
915
- # success is judged by .data.repository instead of the exit code.
916
- $raw = & gh api graphql -F 'owner={owner}' -F 'name={repo}' -f "branch=$branch" -f "query=$query"
917
- $repo = (($raw -join "`n") | ConvertFrom-Json).data.repository
918
- if (-not $repo) { return $null }
919
- $pr = $repo.pullRequests.nodes | Select-Object -First 1
920
- if ($pr) {
921
- if ($pr.isDraft -and $pr.state -eq "OPEN") { $state = "draft" } else { $state = $pr.state.ToLower() }
922
- $review = @{ APPROVED = " approved"; CHANGES_REQUESTED = " changes requested"; REVIEW_REQUIRED = " review required" }[[string]$pr.reviewDecision]
923
- $rows += "PR #$($pr.number) $state$review`t$($pr.url)"
924
- foreach ($i in $pr.closingIssuesReferences.nodes) { $rows += "#$($i.number) $(OneLine $i.title)`t$($i.url)" }
925
- } else {
926
- foreach ($n in $refs) {
927
- $i = $repo."i$n"
928
- if ($i) { $rows += "#$($i.number) $(OneLine $i.title)`t$($i.url)" }
929
- }
930
- }
931
- } else {
932
- $raw = & az repos pr list --source-branch $branch --status all --top 1 --only-show-errors -o json
933
- if ($LASTEXITCODE -ne 0) { return $null }
934
- $pr = @(($raw -join "`n") | ConvertFrom-Json) | Select-Object -First 1
935
- if ($pr) {
936
- if ($pr.isDraft -and $pr.status -eq "active") {
937
- $state = "draft"
938
- } else {
939
- $state = @{ active = "open"; completed = "merged"; abandoned = "closed" }[[string]$pr.status]
940
- if (-not $state) { $state = $pr.status }
941
- }
942
- $votes = @($pr.reviewers | ForEach-Object { $_.vote })
943
- $review = ""
944
- if ($votes -contains -10) { $review = " rejected" }
945
- elseif ($votes -contains -5) { $review = " waiting for author" }
946
- elseif (@($votes | Where-Object { $_ -ge 5 }).Count -gt 0) { $review = " approved" }
947
- $url = $pr.url -replace '/_apis/git/repositories/', '/_git/' -replace '/pullRequests/', '/pullrequest/'
948
- $rows += "PR !$($pr.pullRequestId) $state$review`t$url"
949
- $raw = & az repos pr work-item list --id $pr.pullRequestId --only-show-errors -o json
950
- } elseif ($refs.Count -gt 0) {
951
- $raw = & az boards query --only-show-errors -o json --wiql "SELECT [System.Id], [System.Title] FROM WorkItems WHERE [System.Id] IN ($($refs -join ', '))"
952
- } else {
953
- return ,$rows
954
- }
955
- if ($LASTEXITCODE -ne 0) { return $null }
956
- foreach ($w in @(($raw -join "`n") | ConvertFrom-Json)) {
957
- $rows += "#$($w.id) $(OneLine $w.fields.'System.Title')`t$($w.url -replace '/_apis/wit/workItems/', '/_workitems/edit/')"
958
- }
959
- }
960
- return ,$rows
961
- }
962
-
963
- if ($Refresh) {
964
- $rows = FetchWork $Refresh $Branch
965
- if ($null -ne $rows) {
966
- [System.IO.File]::WriteAllLines("$Cache.$PID", [string[]]$rows)
967
- Move-Item -LiteralPath "$Cache.$PID" -Destination $Cache -Force
968
- }
969
- exit 0
970
- }
971
-
972
657
  $raw = [Console]::In.ReadToEnd()
973
658
  try { $data = if ($raw.Trim()) { $raw | ConvertFrom-Json } else { @{} } } catch { $data = @{} }
974
659
 
@@ -1118,76 +803,10 @@ if ($null -ne $seven_used) {
1118
803
  $sessPlain += $s; $sessColor += "$(QuotaColor $seven_used)$s$RESET"
1119
804
  }
1120
805
 
1121
- # --- work box: PR + linked items for the current branch ---
1122
- # gh takes ~0.7s and az ~5s, so rendering only reads a cache; a stale cache
1123
- # starts a detached refresh and this turn shows the previous rows.
1124
- $workPlain = @(); $workColor = @()
1125
- $platform = ""
1126
- if ($branch -and -not $branch.StartsWith("detached@")) {
1127
- $remote = & git -C "$cwd" remote get-url origin 2>$null
1128
- if ($remote -match 'github\.com[:/]') {
1129
- if (Get-Command gh) { $platform = "github" }
1130
- } elseif ($remote -match 'dev\.azure\.com[:/]|\.visualstudio\.com/') {
1131
- if (Get-Command az) { $platform = "azure" }
1132
- }
1133
- }
1134
-
1135
- if ($platform) {
1136
- $workDir = Join-Path $env:TEMP "statusline-work"
1137
- New-Item -ItemType Directory -Force -Path $workDir | Out-Null
1138
- # Hashed so a token embedded in the remote URL never lands in a file name.
1139
- $hash = [System.Security.Cryptography.MD5]::Create().ComputeHash([System.Text.Encoding]::UTF8.GetBytes("$remote|$branch"))
1140
- $cache = Join-Path $workDir ([BitConverter]::ToString($hash) -replace '-', '')
1141
- $now = [DateTimeOffset]::UtcNow.ToUnixTimeSeconds()
1142
- $at = 0
1143
- if (Test-Path -LiteralPath "$cache.at") { $at = [int64](Get-Content -LiteralPath "$cache.at" -TotalCount 1) }
1144
- if ($now - $at -gt $WORK_TTL) {
1145
- Set-Content -LiteralPath "$cache.at" -Value $now
1146
- # Start-Job dies with this process, so re-run the script detached instead.
1147
- $self = (Get-Process -Id $PID).Path
1148
- Start-Process -FilePath $self -WindowStyle Hidden -WorkingDirectory $cwd -ArgumentList @(
1149
- '-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', "`"$PSCommandPath`"",
1150
- '-Refresh', $platform, '-Branch', $branch, '-Cache', "`"$cache`"")
1151
- }
1152
-
1153
- $items = 0
1154
- if (Test-Path -LiteralPath $cache) {
1155
- foreach ($row in [System.IO.File]::ReadAllLines($cache)) {
1156
- $parts = $row.Split("`t")
1157
- # Titles come from other people; drop escapes before they reach the terminal.
1158
- $line = $parts[0] -replace '[\x00-\x1f\x7f-\x9f]', ''
1159
- $url = ""
1160
- if ($parts.Count -gt 1) { $url = $parts[1] -replace '[\x00-\x1f\x7f-\x9f]', '' }
1161
- if (-not $line) { continue }
1162
- if (-not ($url.StartsWith("https://") -and -not $url.Contains(" "))) { $url = "" }
1163
- if ($line.StartsWith("PR ")) {
1164
- $color = $C_CTX
1165
- } else {
1166
- $items++
1167
- if ($items -gt 3) { continue }
1168
- $color = $C_DIM; $line = TruncateTo $line 50
1169
- }
1170
- # OSC 8 makes the row clickable; it lives only in the colored copy.
1171
- $colored = "$color$line$RESET"
1172
- if ($url) { $colored = "$ESC]8;;$url$BEL$colored$ESC]8;;$BEL" }
1173
- $workPlain += $line; $workColor += $colored
1174
- }
1175
- }
1176
- if ($items -gt 3) {
1177
- $more = "+$($items - 3) more"
1178
- $workPlain += $more; $workColor += "$C_DIM$more$RESET"
1179
- }
1180
- }
1181
- $workRows = $workPlain.Count
1182
-
1183
806
  $rows = [math]::Max($repoPlain.Count, $sessPlain.Count)
1184
807
  if ($rows -eq 0) { exit 0 }
1185
- $rows = [math]::Max($rows, $workRows)
1186
808
  while ($repoPlain.Count -lt $rows) { $repoPlain += ""; $repoColor += "" }
1187
809
  while ($sessPlain.Count -lt $rows) { $sessPlain += ""; $sessColor += "" }
1188
- if ($workRows -gt 0) {
1189
- while ($workPlain.Count -lt $rows) { $workPlain += ""; $workColor += "" }
1190
- }
1191
810
 
1192
811
  # Width comes from the plain rows; the parallel colored rows carry ANSI escapes
1193
812
  # that would otherwise be counted as visible characters.
@@ -1198,7 +817,6 @@ function BoxWidth($title, $plainRows) {
1198
817
  }
1199
818
  $rw = BoxWidth "repo" $repoPlain
1200
819
  $sw = BoxWidth "session" $sessPlain
1201
- $ww = BoxWidth "work" $workPlain
1202
820
 
1203
821
  function BoxTop($title, [int]$w) {
1204
822
  $dashes = [string]$HZ * ($w - $title.Length - 1)
@@ -1213,17 +831,11 @@ function BoxRow($colored, [int]$w, $plain) {
1213
831
  }
1214
832
 
1215
833
  $lines = @()
1216
- $line = (BoxTop "repo" $rw) + " " + (BoxTop "session" $sw)
1217
- if ($workRows -gt 0) { $line += " " + (BoxTop "work" $ww) }
1218
- $lines += $line
834
+ $lines += (BoxTop "repo" $rw) + " " + (BoxTop "session" $sw)
1219
835
  for ($i = 0; $i -lt $rows; $i++) {
1220
- $line = (BoxRow $repoColor[$i] $rw $repoPlain[$i]) + " " + (BoxRow $sessColor[$i] $sw $sessPlain[$i])
1221
- if ($workRows -gt 0) { $line += " " + (BoxRow $workColor[$i] $ww $workPlain[$i]) }
1222
- $lines += $line
836
+ $lines += (BoxRow $repoColor[$i] $rw $repoPlain[$i]) + " " + (BoxRow $sessColor[$i] $sw $sessPlain[$i])
1223
837
  }
1224
- $line = (BoxBottom $rw) + " " + (BoxBottom $sw)
1225
- if ($workRows -gt 0) { $line += " " + (BoxBottom $ww) }
1226
- $lines += $line
838
+ $lines += (BoxBottom $rw) + " " + (BoxBottom $sw)
1227
839
 
1228
840
  [Console]::Out.Write([string]::Join("`n", $lines))
1229
841
  ```
@@ -1238,16 +850,13 @@ Wire into `~/.claude/settings.json` (or `%USERPROFILE%\.claude\settings.json` on
1238
850
  {
1239
851
  "statusLine": {
1240
852
  "type": "command",
1241
- "command": "bash ~/.claude/statusline-command.sh",
1242
- "refreshInterval": 10
853
+ "command": "bash ~/.claude/statusline-command.sh"
1243
854
  }
1244
855
  }
1245
856
  ```
1246
857
 
1247
858
  (Or the `powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%USERPROFILE%\.claude\statusline-command.ps1"` variant on Windows).
1248
859
 
1249
- `refreshInterval` re-runs the script every 10 seconds on top of the event triggers. Claude Code renders the statusline once at startup, which only starts the background work-box fetch; without the timer the box would stay empty until the first assistant message. The cache stamp still limits `gh`/`az` to one call per 60 seconds.
1250
-
1251
860
  ### Antigravity CLI
1252
861
 
1253
862
  Wire into `~/.gemini/antigravity-cli/settings.json` (or `%USERPROFILE%\.gemini\antigravity-cli\settings.json` on Windows):
@@ -1266,20 +875,22 @@ Wire into `~/.gemini/antigravity-cli/settings.json` (or `%USERPROFILE%\.gemini\a
1266
875
 
1267
876
  ## Work Pane (Claude Code)
1268
877
 
1269
- The work box shows a few rows. The work pane is its scrollable companion: a Claude Code mod that ships inside this skill folder, in `.claude-plugin/plugin.json` and `mod/`. Claude Code loads a plugin from any folder under `~/.claude/skills/` (or a project's `.claude/skills/`), so installing this skill installs the pane. **Do not write any settings for it**, in particular not `CLAUDE_CODE_PLUGIN_DIRS`: that would load it a second time.
878
+ The work pane is a Claude Code mod that ships inside this skill folder, in `.claude-plugin/plugin.json` and `mod/`. Claude Code loads a plugin from any folder under `~/.claude/skills/` (or a project's `.claude/skills/`), so installing this skill installs the pane. **Do not write any settings for it**, in particular not `CLAUDE_CODE_PLUGIN_DIRS`: that would load it a second time.
1270
879
 
1271
880
  What it shows, in a side pane:
1272
881
 
1273
882
  - **Summary and Steps** of the session, written by a Haiku call. It runs when the pane first opens, at the end of each turn while the pane is open, and on **Refresh**.
1274
883
  - **Pull requests**: the current branch's, those of branches this session committed to, and any PR of this repository linked in the conversation. Each shows its state, review, checks and comment count.
1275
- - **Work items**: the PRs' linked issues or work items or, for a branch without a PR, the same branch refs the work box uses.
884
+ - **Work items**: the PRs' linked issues or work items. For a branch without a PR, the item numbers in the branch name (a 3 to 9 digit segment, or a `gh issue develop` `<n>-` prefix) and in commits not yet on the default branch (`#<n>` on GitHub, `AB#<n>` on Azure DevOps).
885
+
886
+ When `origin` is neither GitHub nor Azure DevOps, or there is no repo, the pane hides both sections and shows only the summary and steps.
1276
887
 
1277
888
  How it opens:
1278
889
 
1279
890
  - `/work` opens and focuses it. Up/Down or PgUp/PgDn scroll, `r` refreshes, Esc closes.
1280
- - A resumed session (`--resume`, `--continue`, the picker or `/resume`) opens it by itself, unfocused, once it has a PR or item to show. A new session never does.
891
+ - A resumed session (`--resume`, `--continue`, the picker or `/resume`) opens it by itself, unfocused, inside a repo or not, and with or without a PR or item. A new session and `/clear` never do.
1281
892
 
1282
- It needs the same `gh` or `az` login as the work box. It fetches by itself and does not read the statusline's cache. Its tests run with `claude plugin test <skill folder>`.
893
+ It fetches with `gh` (GitHub) or `az` with the `azure-devops` extension (Azure DevOps), so it needs that CLI logged in. Its tests run with `claude plugin test <skill folder>`.
1283
894
 
1284
895
  ## Smoke Test
1285
896
 
@@ -1294,10 +905,6 @@ After installing, pipe a fake payload into the script and confirm:
1294
905
  - The tokens sum matches the fixture and sits on the `ctx` row.
1295
906
  - Omitting `transcript_path` leaves the `ctx` row as a bare `ctx N%` — no `↑0 ↓0`, no stray spacing.
1296
907
  - Outside a repo the repo box collapses to the cwd row alone and the session box pads with blank rows.
1297
- - With a fixture work cache, a third `work` box shows the PR row and item rows, and every line still has the same width.
1298
- - Work rows with an `https://` URL carry an OSC 8 link; strip `\e]8;;[^\a]*\a` along with the color codes before measuring widths.
1299
- - With an empty work cache, only the repo and session boxes render.
1300
- - With no work cache at all, the script still returns in well under a second, and the refresh writes the cache a few seconds later.
1301
908
  - In a new Claude Code session inside a repo, `/work` opens the work pane; `claude plugin validate` and `claude plugin test` on the installed skill folder both pass.
1302
909
 
1303
910
  ### Linux/macOS smoke test
@@ -1324,22 +931,13 @@ Assert equal line widths mechanically:
1324
931
  ```bash
1325
932
  printf '{"cwd":"%s","transcript_path":"/tmp/statusline-smoke/transcript.jsonl","model":{"display_name":"Opus 4.7"},"context_window":{"used_percentage":42}}' "$PWD" \
1326
933
  | bash ~/.claude/statusline-command.sh \
1327
- | perl -CSD -pe 's/\e\[[0-9;]*m//g; s/\e\]8;;[^\a]*\a//g' \
934
+ | perl -CSD -pe 's/\e\[[0-9;]*m//g' \
1328
935
  | perl -CSD -ne 'chomp; my $w = 0; $w += /[\p{Ea=W}\p{Ea=F}\x{FE0F}]/ ? 2 : 1 for /\X/g; print "$w\n"' \
1329
936
  | sort -u
1330
937
  ```
1331
938
 
1332
939
  That must print exactly one number. Counting with `length` instead would pass on a row an emoji pushes out of line. 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.
1333
940
 
1334
- Work box from a fixture cache, run inside a GitHub or Azure DevOps repo. The fresh `.at` stamp stops a network refresh, so this stays offline:
1335
-
1336
- ```bash
1337
- d="${TMPDIR:-/tmp}/statusline-work-$UID"; mkdir -p -m 700 "$d"
1338
- read -r key _ < <(printf '%s|%s' "$(git remote get-url origin)" "$(git branch --show-current)" | cksum)
1339
- printf 'PR #46 open approved\thttps://github.com/o/r/pull/46\n#45 Show PRs in statusline\thttps://github.com/o/r/issues/45\n' > "$d/$key"; date +%s > "$d/$key.at"
1340
- printf '{"cwd":"%s","model":{"display_name":"Opus 4.7"}}' "$PWD" | bash ~/.claude/statusline-command.sh; echo
1341
- ```
1342
-
1343
941
  ### Windows smoke test (PowerShell)
1344
942
 
1345
943
  ```powershell
@@ -1369,18 +967,6 @@ $out
1369
967
 
1370
968
  The last line must print exactly one number.
1371
969
 
1372
- Work box from a fixture cache, run inside a GitHub or Azure DevOps repo. The fresh `.at` stamp stops a network refresh, so this stays offline:
1373
-
1374
- ```powershell
1375
- $d = "$env:TEMP\statusline-work"; New-Item -ItemType Directory -Force -Path $d | Out-Null
1376
- $md5 = [System.Security.Cryptography.MD5]::Create()
1377
- $key = [BitConverter]::ToString($md5.ComputeHash([System.Text.Encoding]::UTF8.GetBytes("$(git remote get-url origin)|$(git branch --show-current)"))) -replace '-', ''
1378
- "PR #46 open approved`thttps://github.com/o/r/pull/46`n#45 Show PRs in statusline`thttps://github.com/o/r/issues/45" | Set-Content -LiteralPath "$d\$key" -Encoding UTF8
1379
- [DateTimeOffset]::UtcNow.ToUnixTimeSeconds() | Set-Content -LiteralPath "$d\$key.at"
1380
- $payload = @{ cwd = $PWD.Path; model = @{ display_name = "Opus 4.7" } } | ConvertTo-Json -Compress
1381
- $payload | powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$env:USERPROFILE\.claude\statusline-command.ps1"
1382
- ```
1383
-
1384
970
  ## Color Preview One-Liner
1385
971
 
1386
972
  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.
@@ -1403,6 +989,5 @@ $e=[char]27; Write-Host "$e[38;2;78;186;101m5h 20% resets 2h 30m$e[0m`n$e[38;2;
1403
989
  - Do not auto-commit any changes.
1404
990
  - Do not add Co-Authored-By trailers.
1405
991
  - Read git state with `git -C "$cwd" status -b --porcelain`, never from `PWD`.
1406
- - Never call `gh` or `az` in the render path; only the detached refresh calls them.
1407
992
  - Never measure padding from a string containing ANSI escapes.
1408
993
  - When merging into existing `settings.json`, preserve all other keys; only set/replace `statusLine`.