Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
1279 commits
Select commit Hold shift + click to select a range
7a70336
Add optional Copilot budget extras (#1273)
Quicksaver Jun 12, 2026
abaf4f7
docs: credit Copilot budget extras
steipete Jun 12, 2026
62b2d04
chore: prepare 0.34.0 release
steipete Jun 12, 2026
b1d3e40
style: apply release formatting
steipete Jun 12, 2026
7dc3d1c
build: use managed release signing keychain
steipete Jun 12, 2026
7717813
docs: update appcast for 0.34.0
steipete Jun 12, 2026
6ef1b44
build: stabilize widget package reference
steipete Jun 12, 2026
2080022
chore: start 0.34.1 development
steipete Jun 12, 2026
1dfd7d9
fix: restore native merged menu positioning
steipete Jun 12, 2026
9206642
Fix Launch at Login pending approval disable
AmrMohamad Jun 12, 2026
358f617
fix: complete launch at login state handling
steipete Jun 12, 2026
b553814
Merge pull request #1469 from AmrMohamad/codex/launch-login-requires-…
steipete Jun 12, 2026
1d7ae09
Memoize cookie cache lookups behind settings picker labels
ProspectOre Jun 12, 2026
5eaad53
Guard display-cache refreshes against newer store and clear snapshots
ProspectOre Jun 12, 2026
a1b874e
fix: guard cookie display cache invalidation
steipete Jun 12, 2026
1bfcc61
fix: harden cookie display cache failures
steipete Jun 12, 2026
a610c7a
fix: keep display cache migration race-safe
steipete Jun 13, 2026
c1f9e36
Merge pull request #1471 from ProspectOre/perf/settings-cookie-traili…
steipete Jun 13, 2026
5bfba2b
Fix package_app.sh preferring stale legacy build products
ProspectOre Jun 13, 2026
2d19528
fix: preserve universal package outputs
steipete Jun 13, 2026
0a16bb4
Merge pull request #1473 from ProspectOre/fix/package-app-stale-build…
steipete Jun 13, 2026
29f8fea
Keep switcher shortcut peeks from killing menu tracking sessions
ProspectOre Jun 12, 2026
f556704
Merge pull request #1472 from ProspectOre/fix/menu-tracking-zombie-ev…
steipete Jun 13, 2026
d57dabf
fix: prevent provider switch content flicker
steipete Jun 12, 2026
64f2201
Fix shortcut menu toggle
Zihao-Qi Jun 12, 2026
f997c3c
docs: credit shortcut menu fix
steipete Jun 13, 2026
060d19e
chore: document shortcut toggle fix
steipete Jun 13, 2026
70beaf0
chore: remove duplicate shortcut changelog
steipete Jun 13, 2026
bf7e7dc
Merge pull request #1470 from Zihao-Qi/shortcut-menu-toggle
steipete Jun 13, 2026
2c7283b
Add Kimi Code API usage source (#1424)
kiranmagic7 Jun 13, 2026
d37f9c0
fix: preserve menu bar text when pace is unavailable (#1474)
steipete Jun 13, 2026
76e684c
docs: correct Kimi release notes
steipete Jun 13, 2026
7a14e17
docs: require visible menu icon proof
steipete Jun 13, 2026
71c124e
ci: avoid duplicate pull request runs (#1475)
steipete Jun 13, 2026
a2b56ea
fix: enforce diagnostic probe timeouts (#1477)
steipete Jun 13, 2026
69831ca
fix(packaging): trust SwiftPM product paths (#1478)
steipete Jun 13, 2026
4bbe133
fix: rebuild provider switcher during menu tracking (#1479)
steipete Jun 13, 2026
581f8b0
fix: prevent tests from terminating user Codex processes (#1480)
steipete Jun 13, 2026
8adc2c7
fix: coalesce deferred menu refreshes (#1483)
steipete Jun 13, 2026
88d6360
test: avoid executable fixture spawn race (#1482)
steipete Jun 13, 2026
1d39e0c
fix: keep Settings toolbar in sync with dark mode switch (#1484)
hhh2210 Jun 13, 2026
ca94829
feat: show Xiaomi MiMo balance breakdown and token usage (#1309)
AdrianSimionov Jun 13, 2026
8e7df44
test: isolate browser cookie discovery (#1489)
steipete Jun 13, 2026
f5fa813
feat: add opt-in MiMo local usage fallback (#1284)
LeoLin990405 Jun 13, 2026
501cb51
fix: close browser cookie isolation bypass (#1491)
steipete Jun 13, 2026
2f92ee9
feat: use configured work days for weekly pace (#1451)
steipete Jun 13, 2026
532f43d
perf: remove redundant release prebuild (#1493)
ProspectOre Jun 13, 2026
87f67e3
fix: validate release dSYM paths and architectures (#1494)
ProspectOre Jun 13, 2026
620aa76
fix: verify release dSYM UUIDs (#1492)
ProspectOre Jun 14, 2026
0103e6e
Pin status-bar menus to the system appearance (fix dark menu in Light…
npapridonu Jun 14, 2026
cb934ff
fix: resolve and contain Sparkle signing paths (#1495)
ProspectOre Jun 14, 2026
7d12c4a
chore: prepare 0.35.0 release
steipete Jun 14, 2026
15c70c7
style: normalize MiMo usage tests
steipete Jun 14, 2026
9d3c354
docs: update appcast for 0.35.0
steipete Jun 14, 2026
158eafa
chore: start 0.35.1 development
steipete Jun 14, 2026
c7ab4e6
chore: update Ollama provider icon (#1497)
mattab178 Jun 14, 2026
a937443
fix(settings): make cost history days editable (#1503)
kiranmagic7 Jun 14, 2026
24ba34e
fix: prevent menu flicker and improve sidebar contrast
steipete Jun 14, 2026
83ca996
Merge remote-tracking branch 'origin/main'
steipete Jun 14, 2026
984c2d1
Improve Antigravity quota detection (#1509)
Zihao-Qi Jun 14, 2026
04a7870
fix: recognize Gemini API key auth (#1512)
steipete Jun 14, 2026
3a2b73e
fix: probe Antigravity HTTP ports on Linux (#1514)
steipete Jun 14, 2026
e6e84a6
test: cover local documentation links (#1506)
kiranmagic7 Jun 14, 2026
c818807
docs(config): sync provider id list (#1501)
kiranmagic7 Jun 14, 2026
9490a8d
Fix Antigravity quota edge handling (#1515)
steipete Jun 14, 2026
c33ecd6
test: cover provider descriptor icon resources (#1505)
kiranmagic7 Jun 14, 2026
b40c353
fix: refresh open menu usage values (#1518)
steipete Jun 14, 2026
6c409f7
fix: synchronize cookie cache test override (#1519)
steipete Jun 14, 2026
df4a5ba
docs(site): surface newer provider cards (#1504)
kiranmagic7 Jun 14, 2026
ab5ff4c
fix: fail MiMo refresh promptly (#1520)
steipete Jun 14, 2026
1033a6a
fix: retry transient Antigravity launch failures (#1522)
steipete Jun 14, 2026
2e4b355
fix: keep Command Code credits available (#1521)
steipete Jun 14, 2026
0677030
fix: shard local Swift tests (#1523)
steipete Jun 14, 2026
fe210ac
fix: bound DeepSeek optional summary wait (#1525)
steipete Jun 14, 2026
a3134da
fix: bound optional provider enrichment (#1526)
steipete Jun 14, 2026
ac8159f
fix: bound subprocess output draining (#1529)
steipete Jun 14, 2026
ae7455b
fix: keep token account menu data scoped (#1530)
steipete Jun 14, 2026
faab45e
test: characterize provider implementation registry
steipete Jun 14, 2026
30506ca
refactor: make provider registration explicit
steipete Jun 14, 2026
22a07ef
refactor: remove provider registration macros
steipete Jun 14, 2026
5c7c70e
fix: isolate runtime and test state
steipete Jun 14, 2026
bdb213f
refactor: centralize provider cookie settings
steipete Jun 14, 2026
43f7991
refactor: centralize provider environments
steipete Jun 14, 2026
6335f2a
refactor: share API token fetch strategy
steipete Jun 14, 2026
fa1a038
refactor: migrate commodity API providers
steipete Jun 14, 2026
7999774
fix: preserve usage snapshot payloads
steipete Jun 14, 2026
3cd3dd6
fix: unify icon usage projection
steipete Jun 14, 2026
0d3b09d
refactor: centralize menu session state
steipete Jun 14, 2026
bfcb395
refactor: share menu rebuild generations
steipete Jun 14, 2026
371c748
fix: dispose removed menu state
steipete Jun 14, 2026
5b8313f
refactor: extract provider refresh coordinator
steipete Jun 14, 2026
c9bf666
test: centralize app state fixtures
steipete Jun 14, 2026
605ab5f
test: finish status bar fixture migration
steipete Jun 15, 2026
b3f74ca
fix: bound CLI version output draining (#1532)
steipete Jun 15, 2026
3b5f523
Add native Italian localization support (#1248)
Yuxin-Qiao Jun 15, 2026
fc85132
Prove Claude cookie cache isolation (#1539)
ProspectOre Jun 15, 2026
6350ea1
Prove Claude OAuth ownership handoff (#1540)
ProspectOre Jun 15, 2026
2d9ddd0
Attach Copilot reset time (#1538)
Zihao-Qi Jun 15, 2026
a174d17
feat: add Indonesian (Bahasa Indonesia) localization (#1513)
StevanusPangau Jun 15, 2026
209b6c8
Fix Kiro usage command pipe hangs (#1535)
kiranmagic7 Jun 15, 2026
640cb8c
Fix Gemini fnm package discovery pipe hangs (#1536)
kiranmagic7 Jun 15, 2026
279821c
test: stabilize Antigravity readiness retry
steipete Jun 15, 2026
3263f04
test: stabilize provider readiness timing
steipete Jun 15, 2026
c303b67
Add native Polish localization support (#1253)
Yuxin-Qiao Jun 15, 2026
87b1195
Add LiteLLM provider (#1542)
hololee Jun 15, 2026
bd24350
Fix LiteLLM team budget handling (#1546)
steipete Jun 15, 2026
a71269d
Document LiteLLM on website (#1549)
steipete Jun 15, 2026
bc389cd
Localize website across 21 languages (#1550)
steipete Jun 15, 2026
2dd3ba4
docs: unify localization changelog entry
steipete Jun 15, 2026
6b0a0e5
fix(opencode): show Zen balance without subscription usage (#1498)
kiranmagic7 Jun 15, 2026
9d19703
Align app and website language catalogs (#1554)
steipete Jun 15, 2026
dd09666
fix: improve German website copy
steipete Jun 15, 2026
b1e5290
fix: normalize website provider logos
steipete Jun 15, 2026
3286934
fix: clean up Kiro CLI helper processes (#1552)
steipete Jun 15, 2026
99752f7
Fix usage pace risk label
kiranmagic7 Jun 15, 2026
74afdd0
Fix usage pace risk label (#1561)
steipete Jun 15, 2026
d1476cd
Codex/fix antigravity status shift (#1560)
Zihao-Qi Jun 15, 2026
40d9f07
test: wait for helper heartbeat progression
steipete Jun 15, 2026
75ecfd9
test: wait for DeepSeek cancellation propagation
steipete Jun 15, 2026
63aae70
Fix stale menu after refresh with hosted submenu open (#1556)
Yuxin-Qiao Jun 15, 2026
c4283ca
fix: keep localized website controls in mobile bounds
steipete Jun 15, 2026
faf112f
feat: show Antigravity quota reset time from resetTime field (#1553)
akunzai Jun 15, 2026
87b2666
Add XDG config path support (#1562)
kiranmagic7 Jun 15, 2026
ccb204f
chore: prepare 0.36.0 release
steipete Jun 15, 2026
38f0592
test: avoid generated executable fixtures
steipete Jun 15, 2026
ace529f
test: support injected Swift commands
steipete Jun 15, 2026
3dec0e8
chore: correct 0.36.0 release date
steipete Jun 15, 2026
6d906d3
Add Poe current balance provider (#1191)
Yuxin-Qiao Jun 15, 2026
f839629
docs: update appcast for 0.36.0
steipete Jun 15, 2026
7bced4b
chore: start 0.36.1 development
steipete Jun 15, 2026
de6cd3a
feat: add Chutes provider for usage/quota tracking (#1496)
mvanhorn Jun 16, 2026
ee9dc72
Add Zed provider using editor Keychain session (#1517)
enesteve0 Jun 16, 2026
0a17183
[codex] Fix provider switcher background (#1558)
Zihao-Qi Jun 16, 2026
de88c63
docs: add new provider website assets
steipete Jun 16, 2026
3ea7ed6
docs: correct 0.36.1 release notes
steipete Jun 16, 2026
5fcb7f6
style: preserve explicit sendable contracts
steipete Jun 16, 2026
06fea2c
docs: update appcast for 0.36.1
steipete Jun 16, 2026
d50643c
Reuse the precomputed store icon signature in updateIcons (#1555)
abe238 Jun 16, 2026
28a34b2
fix(i18n): improve Japanese localization quality (#1567)
tukuyomil032 Jun 16, 2026
b3474da
chore: merge post-release main updates
steipete Jun 16, 2026
ba38096
chore: start 0.36.2 development
steipete Jun 16, 2026
ac01d73
fix(i18n): localize hardcoded "tomorrow" in reset time display (#1568)
tukuyomil032 Jun 16, 2026
e012e46
ci: enhance i18n health checks across all locales (#1591)
Yuxin-Qiao Jun 17, 2026
cbf4060
fix: don't highlight non-interactive menu card items on hover (#1589)
elijahfriedman Jun 17, 2026
13616b0
Fix manual refresh quota display (#1583)
Zihao-Qi Jun 17, 2026
05545fe
Show LiteLLM budget spend details (#1569)
hololee Jun 17, 2026
70f37af
Update Antigravity quota UI to sync with backend payload display name…
Yuxin-Qiao Jun 18, 2026
a4dd57a
Cache Pi session parsers (#1574)
ProspectOre Jun 18, 2026
421bf57
Keep Codex quota visible during refresh switchback (#1598)
Yuxin-Qiao Jun 18, 2026
acd0302
Trim menu formatting work (#1572)
ProspectOre Jun 18, 2026
1ef93c6
Bound subprocess output capture (#1576)
ProspectOre Jun 18, 2026
c6b81ef
Add CodexBar agent skill (#1606)
coygeek Jun 18, 2026
6afbaca
fix(history): persist generic provider utilization (#1588)
kiranmagic7 Jun 18, 2026
2fd41ef
feat: resize cost history menu card to the hovered day's content (#1602)
elijahfriedman Jun 18, 2026
b9a3cef
Scope provider status indicators (#1603)
Zihao-Qi Jun 18, 2026
d8ce869
build: add musl CLI source compatibility (#1620)
steipete Jun 18, 2026
04e1af6
feat: add Hide critters menu bar option (#1604)
elijahfriedman Jun 18, 2026
274f2cf
fix: prefer fast Codex CLI sources (#1617)
steipete Jun 18, 2026
d39b4d8
fix: smooth settings tab switching (#1597)
elijahfriedman Jun 18, 2026
016f3b9
build: add opt-in musl SQLite link path (#1609)
Yuxin-Qiao Jun 18, 2026
ae08d86
ci: publish static musl CLI artifacts (#1610)
Yuxin-Qiao Jun 18, 2026
5db1a04
Bound the full Codex web fetch deadline (#1618)
steipete Jun 18, 2026
553f80e
fix: read Windsurf sessions from Devin app origin (#1579)
kiranmagic7 Jun 18, 2026
f986661
fix(codex): correct historical pace run-out forecasts (#1581)
Yuxin-Qiao Jun 18, 2026
73b35b0
ci: shard macOS Swift tests (#1629)
Yuxin-Qiao Jun 18, 2026
62737c6
fix: humanize Antigravity model labels (#1580)
bcharleson Jun 18, 2026
ef2d8ae
Update German translation for 'Refreshing' (#1587)
ChrisLauinger77 Jun 18, 2026
4d3b9d9
fix(i18n): fixes weird german tranlations (#1578)
madebyjulz Jun 18, 2026
5ff81b4
docs: explain Keychain prompts after uninstall (#1595)
Yuxin-Qiao Jun 18, 2026
a6965ee
Avoid unnecessary logging work (#1570)
ProspectOre Jun 18, 2026
f1221c1
fix(ci): surface test discovery failures (#1632)
steipete Jun 18, 2026
d9fffae
fix: isolate cookie import deadlines (#1637)
steipete Jun 18, 2026
f2f9d8b
Make cookie deadline timing tests saturation-safe (#1639)
steipete Jun 18, 2026
518764e
Cache cost usage date parsers (#1573)
ProspectOre Jun 19, 2026
f6f76ff
Release idle WebViews on memory pressure (#1575)
ProspectOre Jun 19, 2026
32db840
Trim app caches on memory pressure (#1577)
ProspectOre Jun 19, 2026
e3ae6f2
Fix opencodego manual cookie usage on Linux CLI (#1599)
Yuxin-Qiao Jun 19, 2026
6d37bbc
fix: stop plan regex from bleeding across TUI lines (#1636)
elijahfriedman Jun 19, 2026
a3ea167
feat: Add Codex rate-limit reset credits to menu card (#1638)
rogdex24 Jun 19, 2026
6ea6fd7
ci: keep lint independent of macOS test shards (#1643)
Yuxin-Qiao Jun 19, 2026
967bb61
Avoid retaining menu refresh state (#1571)
ProspectOre Jun 19, 2026
08e19e3
feat(mistral): add Monthly Plan menu bar metric (subscription usage) …
lfmundim Jun 19, 2026
88b467a
Add usage data confidence metadata (#1594)
Yuxin-Qiao Jun 19, 2026
8009f70
Fix Kiro usage when account probe times out (#1627)
Yuxin-Qiao Jun 19, 2026
c3a0e40
feat: segmented storage breakdown bar with expandable Other (#1641)
elijahfriedman Jun 19, 2026
5e38f6b
ci: run lint on Linux runners (#1646)
Yuxin-Qiao Jun 19, 2026
7398a9f
Fix Antigravity Gemini icon lanes and exhausted bar fill (#1631)
Zihao-Qi Jun 19, 2026
99cd674
Fix menu bar quota for exhausted sublimits (#1645)
Yuxin-Qiao Jun 19, 2026
22b4a65
feat: add dashboard url handling for z.ai api regions (#1582)
renbaoshuo Jun 19, 2026
2673d92
feat(cursor): show personal on-demand spend alongside team pool (#1623)
yashiels Jun 19, 2026
c73dd4a
feat: add alphabetical sort toggle to providers pane (#1634)
elijahfriedman Jun 19, 2026
6c5aa41
docs: clarify universal Homebrew cask (#1628)
steipete Jun 19, 2026
9c7c738
docs: add KodexBar Linux desktop integration (#1557)
tylxr59 Jun 19, 2026
5c286d7
fix: color inline usage bars by provider (#1630)
elijahfriedman Jun 19, 2026
92da482
fix: align Antigravity highest-usage ranking (#1649)
steipete Jun 19, 2026
d3c80a5
Stabilize process cleanup timing tests (#1650)
steipete Jun 19, 2026
77044e4
fix: retry MiMo after auth redirects
Yuxin-Qiao Jun 19, 2026
2fd5bcc
test: stabilize cookie cache timeout ordering (#1651)
steipete Jun 19, 2026
3e8e123
fix(commandcode): stop repeated 'session depleted' notification after…
LPFchan Jun 19, 2026
10dd11f
ci: install lint tools selectively (#1653)
Yuxin-Qiao Jun 19, 2026
cc4e8aa
Reduce release app bundle size (#1612)
jieshu666 Jun 19, 2026
20a4357
fix: prevent date axis labels from being clipped in cost history char…
elijahfriedman Jun 19, 2026
7ffad6b
fix: retry MiniMax China region for invalid token responses (#1648)
steipete Jun 19, 2026
d3551e7
feat: add Codex profile home accounts (#1487)
kiranmagic7 Jun 19, 2026
8640d60
feat: add Burn Down widgets (single window + 5h & 7d combined) (#1255)
jamesjlopez Jun 19, 2026
669d1e9
ci: skip macOS shards for docs-only changes (#1654)
Yuxin-Qiao Jun 19, 2026
1066194
docs: correct Burn Down widget credit
steipete Jun 19, 2026
9c54acd
Disable inactive Claude keychain toggle (#1658)
Zihao-Qi Jun 19, 2026
36f0657
feat: show Bedrock Claude activity (#1655)
steipete Jun 19, 2026
7ad7bda
Fix Linux OpenCode Go and Command Code usage (#1660)
steipete Jun 19, 2026
4eb67b2
perf: improve menu refresh responsiveness
jangisaac-dev Jun 19, 2026
529a933
test: stabilize Kiro inherited-pipe fixture (#1663)
steipete Jun 19, 2026
4b14d2a
fix: expose MiniMax quota diagnostics (#1656)
steipete Jun 19, 2026
0260a9a
fix: align menu items and convert action rows to native NSMenuItems (…
elijahfriedman Jun 19, 2026
0dbcf86
fix: improve Catalan localization
pmontp19 Jun 20, 2026
d71d92f
feat: add combined Codex usage metric
thepraggyverse Jun 20, 2026
21e0ca7
fix: scope chart selection band to bar width and remove cost item det…
elijahfriedman Jun 20, 2026
43b7e46
Add missing docs-list metadata (#1667)
kiranmagic7 Jun 20, 2026
eafe68b
fix: persist renewed Claude web sessions
ProspectOre Jun 20, 2026
06be973
chore: prepare 0.37.0 release
steipete Jun 20, 2026
33a5f43
docs: update appcast for 0.37.0
steipete Jun 20, 2026
0f85e05
chore: start 0.37.1 development
steipete Jun 20, 2026
8c4bdd6
fix: prevent memory pressure isolation crash (#1668)
Zihao-Qi Jun 20, 2026
8d7f1e5
fix: compact menu usage rows (#1669)
elijahfriedman Jun 20, 2026
3f3e2f4
fix: show unavailable Antigravity limits (#1677)
steipete Jun 20, 2026
802d778
ci: cache Linux Swift toolchains (#1670)
Yuxin-Qiao Jun 21, 2026
4700d8e
Show missing locale keys in checker (#1693)
kiranmagic7 Jun 21, 2026
d9bdb07
Align provider card spacing (#1682)
Zihao-Qi Jun 21, 2026
9e7a70a
docs: finalize 0.37.1 changelog
steipete Jun 21, 2026
af842f3
Remove redundant cost tooltip (#1699)
Zihao-Qi Jun 21, 2026
d00c6c0
Fix MiniMax token plan display (#1698)
Yuxin-Qiao Jun 21, 2026
fb8473f
fix: keep menu bar open on refresh (#1700)
elijahfriedman Jun 21, 2026
244b31e
docs: update appcast for 0.37.1
steipete Jun 21, 2026
fb6ab4e
chore: start 0.37.2 development
steipete Jun 21, 2026
c0286e2
test: wait for parseable helper PIDs
steipete Jun 21, 2026
017408f
test: wait for DeepSeek cancellation
steipete Jun 22, 2026
2c0f83a
Check generated llms index in lint (#1701)
kiranmagic7 Jun 22, 2026
c1bfd84
diagnostics: support redacted diagnose output files (#1688)
Yuxin-Qiao Jun 22, 2026
17a2efa
Handle Claude CLI usage rate limits (#1685)
kiranmagic7 Jun 22, 2026
8c83f05
serve: report build version on /health (#1703)
enieuwy Jun 22, 2026
f5b3d38
ci: skip macOS shards for docs site changes (#1671)
Yuxin-Qiao Jun 22, 2026
bde4290
tests: add provider quota fixture contract coverage (#1695)
Yuxin-Qiao Jun 22, 2026
ca31ab2
[security] fix(codex): keep OAuth auth.json private (#1702)
Hinotoi-agent Jun 22, 2026
23e33cd
[security] fix(provider): harden endpoint overrides (#1680)
Hinotoi-agent Jun 22, 2026
ba16332
[security] fix(provider): harden Azure OpenAI endpoint overrides (#1687)
Hinotoi-agent Jun 22, 2026
3fe3d93
chore: prepare 0.37.2 release
steipete Jun 22, 2026
716f46a
style: align Swift 6.2 formatting
steipete Jun 22, 2026
f380287
docs: update appcast for 0.37.2
steipete Jun 22, 2026
ef8007f
chore: start 0.37.3 development
steipete Jun 22, 2026
2a38e5b
feat(cli): add Session pace and JSON pace output
kmatsunami Jun 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
38 changes: 38 additions & 0 deletions .agents/skills/codexbar/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
name: codexbar
description: "CodexBar read. Provider usage, limits, credits, config health. JSON. No writes."
---

# CodexBar

Read CodexBar. Never mutate config/auth.

## Run

```bash
skill="${CODEX_HOME:-$HOME/.codex}/skills/codexbar"
"$skill/scripts/codexbar" doctor
"$skill/scripts/codexbar" providers
"$skill/scripts/codexbar" usage
"$skill/scripts/codexbar" usage --provider codex
"$skill/scripts/codexbar" usage --all
```

All stdout: JSON. Upstream CodexBar shape kept. Less drift, fewer tokens.

## Rules

- Start `doctor` when install/config unknown.
- `usage` reads enabled providers. Prefer this.
- `usage --provider ID` reads one provider.
- `usage --all` expensive; use only when needed.
- Identities hidden by default. `--include-identities` only when user explicitly needs them.
- Secrets always hidden.
- Helper read-only: fixed allowlist only. No config writes, auth repair, enable/disable, key storage.
- Timeout means upstream stuck. Narrow provider or raise `CODEXBAR_TIMEOUT` (default 120 seconds).

## Binary

Auto-find: `CODEXBAR_BIN`, PATH, app bundle, Homebrew cask. If missing: open CodexBar, Preferences > Advanced > Install CLI; or set `CODEXBAR_BIN`.

Each stdout/stderr stream capped at 1 MiB while fully drained. Timeout kills process group.
321 changes: 321 additions & 0 deletions .agents/skills/codexbar/scripts/codexbar
Original file line number Diff line number Diff line change
@@ -0,0 +1,321 @@
#!/usr/bin/env python3
"""Read-only, bounded CodexBar CLI bridge."""

from __future__ import annotations

import argparse
import json
import os
import re
import shutil
import signal
import subprocess
import sys
import threading
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Sequence


MAX_CAPTURE_BYTES = 1024 * 1024
DEFAULT_TIMEOUT = 120.0
BIN_ENV = "CODEXBAR_BIN"
TIMEOUT_ENV = "CODEXBAR_TIMEOUT"
SKIP_DISCOVERY_ENV = "CODEXBAR_SKIP_DISCOVERY"

SECRET = "<redacted:secret>"
IDENTITY = "<redacted:identity>"
EMAIL = "<redacted:email>"

EMAIL_RE = re.compile(r"\b[A-Z0-9._%+\-]+@[A-Z0-9.\-]+\.[A-Z]{2,}\b", re.I)
BEARER_RE = re.compile(r"(?i)\bbearer\s+[A-Z0-9._~+/=\-]+")
JWT_RE = re.compile(r"\beyJ[A-Za-z0-9_\-]+\.[A-Za-z0-9_\-]+\.[A-Za-z0-9_\-]+\b")
LABELED_SECRET_RE = re.compile(
r"(?i)\b(authorization|api[_-]?key|access[_-]?token|refresh[_-]?token|id[_-]?token|"
r"session(?:id)?|secret|password|passwd|cookie|set-cookie)(\s*[:=]\s*)([^\s,;]+)"
)
WORD_SECRET_RE = re.compile(
r"(?i)\b(token|secret|password|passwd|cookie)\s+([A-Z0-9._~+/=\-]{6,})\b"
)
SECRET_KEY_RE = re.compile(
r"(?i)(authorization|api[_-]?key|access[_-]?token|refresh[_-]?token|id[_-]?token|"
r"session(?:id)?|secret|password|passwd|cookie)"
)
IDENTITY_KEYS = {
"accountemail",
"accountorganization",
"accountid",
"accountname",
"email",
"organization",
"userid",
"username",
}
# ProviderIdentitySnapshot.providerID is UsageProvider, not an account identifier.


@dataclass(frozen=True)
class Binary:
path: str
source: str


@dataclass(frozen=True)
class Result:
returncode: int
stdout: bytes
stderr: bytes
timed_out: bool


def normalized_key(value: str) -> str:
return re.sub(r"[^a-z0-9]", "", value.lower())


def timeout_seconds() -> float:
try:
return max(1.0, float(os.environ.get(TIMEOUT_ENV, DEFAULT_TIMEOUT)))
except ValueError:
return DEFAULT_TIMEOUT


def safe_path(path: str) -> str:
home = str(Path.home())
if path == home:
return "~"
if path.startswith(home + os.sep):
return "~" + path[len(home) :]
return re.sub(r"^/Users/[^/]+", "/Users/<redacted>", path)


def candidates() -> list[tuple[str, Path]]:
found: list[tuple[str, Path]] = []
override = os.environ.get(BIN_ENV)
if override:
found.append(("env", Path(override).expanduser()))

path_hit = shutil.which("codexbar")
if path_hit:
found.append(("path", Path(path_hit)))

if os.environ.get(SKIP_DISCOVERY_ENV) == "1":
return found

home = Path.home()
found.extend(
[
("homebrew", Path("/opt/homebrew/bin/codexbar")),
("homebrew", Path("/usr/local/bin/codexbar")),
("app", Path("/Applications/CodexBar.app/Contents/Helpers/CodexBarCLI")),
("app", home / "Applications/CodexBar.app/Contents/Helpers/CodexBarCLI"),
]
)
for root in (Path("/opt/homebrew/Caskroom/codexbar"), Path("/usr/local/Caskroom/codexbar")):
found.extend(("cask", app / "Contents/Helpers/CodexBarCLI") for app in sorted(root.glob("*/CodexBar.app")))
return found


def resolve_binary() -> Binary | None:
self_path = Path(__file__).resolve()
seen: set[Path] = set()
for source, candidate in candidates():
try:
resolved = candidate.resolve()
if resolved in seen or resolved == self_path:
continue
seen.add(resolved)
if resolved.is_file() and os.access(resolved, os.X_OK):
return Binary(str(resolved), source)
except OSError:
continue
return None


def drain(pipe: Any, target: bytearray) -> None:
try:
while True:
chunk = pipe.read(65536)
if not chunk:
break
room = MAX_CAPTURE_BYTES - len(target)
if room > 0:
target.extend(chunk[:room])
finally:
pipe.close()


def stop_group(process: subprocess.Popen[bytes], sig: signal.Signals) -> None:
try:
os.killpg(process.pid, sig)
except ProcessLookupError:
pass


def run_process(argv: Sequence[str]) -> Result:
process = subprocess.Popen(
list(argv),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
start_new_session=True,
)
assert process.stdout is not None
assert process.stderr is not None
stdout = bytearray()
stderr = bytearray()
readers = [
threading.Thread(target=drain, args=(process.stdout, stdout), daemon=True),
threading.Thread(target=drain, args=(process.stderr, stderr), daemon=True),
]
for reader in readers:
reader.start()

timed_out = False
try:
process.wait(timeout=timeout_seconds())
except subprocess.TimeoutExpired:
timed_out = True
stop_group(process, signal.SIGTERM)
try:
process.wait(timeout=2)
except subprocess.TimeoutExpired:
stop_group(process, signal.SIGKILL)
process.wait()

for reader in readers:
reader.join(timeout=3)
return Result(124 if timed_out else process.returncode, bytes(stdout), bytes(stderr), timed_out)


def decode(value: bytes) -> str:
return value.decode("utf-8", errors="replace")


def sanitize_text(value: str, include_identities: bool) -> str:
value = JWT_RE.sub(SECRET, value)
value = BEARER_RE.sub("Bearer " + SECRET, value)
value = LABELED_SECRET_RE.sub(lambda match: match.group(1) + match.group(2) + SECRET, value)
value = WORD_SECRET_RE.sub(lambda match: match.group(1) + " " + SECRET, value)
if not include_identities:
value = EMAIL_RE.sub(EMAIL, value)
return value


def sanitize(value: Any, include_identities: bool, key: str | None = None) -> Any:
if key and SECRET_KEY_RE.search(key):
return SECRET
if key and normalized_key(key) in IDENTITY_KEYS and not include_identities and value is not None:
return EMAIL if "email" in normalized_key(key) else IDENTITY
if isinstance(value, dict):
return {str(child_key): sanitize(child, include_identities, str(child_key)) for child_key, child in value.items()}
if isinstance(value, list):
return [sanitize(child, include_identities) for child in value]
if isinstance(value, str):
return sanitize_text(value, include_identities)
return value


def print_json(value: Any) -> None:
print(json.dumps(value, indent=2, sort_keys=True))


def print_error(kind: str, message: str, *, binary: Binary | None = None) -> None:
payload: dict[str, Any] = {"error": {"kind": kind, "message": sanitize_text(message, False)}}
if binary:
payload["binary"] = {"path": safe_path(binary.path), "source": binary.source}
print_json(payload)


def emit_stderr(result: Result, include_identities: bool) -> None:
text = sanitize_text(decode(result.stderr), include_identities).strip()
if text:
print(text, file=sys.stderr)


def read_json(binary: Binary, argv: Sequence[str], include_identities: bool) -> int:
result = run_process([binary.path, *argv])
emit_stderr(result, include_identities)
if result.timed_out:
print_error("timeout", f"CodexBar exceeded {timeout_seconds():g}s and was stopped.", binary=binary)
return 124
try:
payload = json.loads(decode(result.stdout))
except json.JSONDecodeError as error:
preview = sanitize_text(decode(result.stdout[:400]), False)
print_error("invalid_json", f"CodexBar JSON failed: {error.msg}. stdout={preview!r}", binary=binary)
return result.returncode or 1
print_json(sanitize(payload, include_identities))
return result.returncode


def doctor(binary: Binary, include_identities: bool) -> int:
version = run_process([binary.path, "--version"])
if version.timed_out:
print_error("timeout", f"CodexBar version exceeded {timeout_seconds():g}s.", binary=binary)
return 124
validation = run_process([binary.path, "config", "validate", "--format", "json", "--json-only"])
emit_stderr(version, include_identities)
emit_stderr(validation, include_identities)
if validation.timed_out:
print_error("timeout", f"CodexBar config validation exceeded {timeout_seconds():g}s.", binary=binary)
return 124
try:
issues = json.loads(decode(validation.stdout))
except json.JSONDecodeError as error:
print_error("invalid_json", f"CodexBar config validation failed: {error.msg}.", binary=binary)
return validation.returncode or 1
print_json(
{
"binary": {"path": safe_path(binary.path), "source": binary.source},
"configIssues": sanitize(issues, include_identities),
"version": sanitize_text((decode(version.stdout) or decode(version.stderr)).strip(), include_identities),
}
)
return version.returncode or validation.returncode


def parser() -> argparse.ArgumentParser:
root = argparse.ArgumentParser(prog="codexbar", description="CodexBar read. JSON out. No writes.")
commands = root.add_subparsers(dest="command", required=True)
for name in ("doctor", "providers"):
command = commands.add_parser(name)
command.add_argument("--include-identities", action="store_true")
usage = commands.add_parser("usage")
scope = usage.add_mutually_exclusive_group()
scope.add_argument("--all", action="store_true")
scope.add_argument("--provider")
usage.add_argument("--include-identities", action="store_true")
return root


def main(argv: Sequence[str] | None = None) -> int:
args = parser().parse_args(argv)
binary = resolve_binary()
if not binary:
print_error("missing", "CodexBar CLI not found. Install CLI in CodexBar Advanced settings or set CODEXBAR_BIN.")
return 1
try:
if args.command == "doctor":
return doctor(binary, args.include_identities)
if args.command == "providers":
return read_json(
binary,
["config", "providers", "--format", "json", "--json-only"],
args.include_identities,
)
usage_args = ["usage", "--format", "json", "--json-only"]
if args.all:
usage_args.extend(["--provider", "all"])
elif args.provider:
usage_args.extend(["--provider", args.provider])
return read_json(binary, usage_args, args.include_identities)
except OSError as error:
print_error("launch", str(error), binary=binary)
return 1


if __name__ == "__main__":
try:
raise SystemExit(main())
except KeyboardInterrupt:
print_error("interrupted", "Interrupted.")
raise SystemExit(130)
Loading
Loading