pi-codexbar
Pi extension wrapper for CodexBar provider state and controls
Package details
Install pi-codexbar from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-codexbar- Package
pi-codexbar- Version
1.0.6- Published
- Apr 27, 2026
- Downloads
- 911/mo · 182/wk
- Author
- harrisiirak
- License
- MIT
- Types
- extension
- Size
- 73.3 KB
- Dependencies
- 1 dependency · 2 peers
Pi manifest JSON
{
"extensions": [
"./src/index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-codexbar
A pi coding-agent extension that surfaces CodexBar provider usage — session, weekly and monthly quota windows, remaining credits, and plan/login info — directly inside the pi TUI. It renders a live footer widget below the input editor, exposes /codexbar-status and /codexbar-switch slash commands, and refreshes automatically when the active provider/model changes.
It is intentionally a thin wrapper: all provider knowledge lives in the CodexBar CLI. This extension only maps pi's provider identifiers to CodexBar's, runs codexbar usage …, caches the JSON, and paints it.
Background
pi-coding-agent talks to many AI providers (Anthropic, OpenAI, Copilot, OpenRouter, Gemini, …). Each one has its own billing / quota model, and none of them is visible from inside the agent. CodexBar is a local menubar app + CLI that already aggregates this state for you from its bundled provider adapters, without requiring explicit logins.
This extension stitches the two together:
- pi emits
session_start,agent_end,model_selectevents → the extension asks CodexBar for the current provider's usage and rewrites the footer. - The user runs
/codexbar-status [provider]→ the extension fetches fresh state and renders it as both a widget and a notification. - The user runs
/codexbar-switch <query>→ the extension ranks matching models by remaining usage budget and switches to the best candidate. - Built-in keys and user aliases are registered as virtual models under the
codexbarprovider. Selecting one from the Pi model picker resolves it to the best-ranked real model automatically. - Results are cached on disk with a short TTL so footer redraws don't hammer the CLI.
Installation
pi install npm:pi-codexbar
Or, for local development, symlink the checkout into pi's extension directory and run /reload:
git clone <repo-url> ~/src/pi-codexbar
ln -s ~/src/pi-codexbar ~/.pi/agent/extensions/pi-codexbar
The extension ships TypeScript sources directly (no build step) and is loaded by pi through tsx.
Requirements
| Requirement | Purpose |
|---|---|
| Node.js ≥ 24 | pi-coding-agent runtime; native TS via tsx. |
| pi-coding-agent | Peer dependency. The extension attaches to session_start, agent_end, and model_select events and registers a slash command. |
| CodexBar CLI | Required on $PATH (or at one of the known locations below). pi-codexbar never talks to provider APIs directly — it spawns codexbar usage … and parses the JSON. |
CodexBar binary discovery order:
/usr/local/bin/codexbar(macOS default)/usr/bin/codexbar/opt/homebrew/bin/codexbar- Whatever
which codexbarresolves to in the current shell
If nothing is found the footer falls back to codexbar: unavailable and /codexbar-status reports the error — the extension never crashes pi.
Note: Authentication is handled entirely by CodexBar and its provider adapters. pi-codexbar never stores tokens, never prompts for credentials, and has no opinion about how you logged in (cookies, OAuth,
sessionKey, …).
Quick Commands
| Command | Description |
|---|---|
/codexbar-status |
Fetch and render usage for the current session's provider. |
/codexbar-status <provider> |
Force a specific provider — accepts either a CodexBar id (claude, codex, copilot, gemini, openrouter) or a pi-native id (it's mapped automatically). |
/codexbar-switch list [query] |
List candidate models for a query (shows resolve tier and matching models). |
/codexbar-switch <query> |
Switch to the model with the highest remaining usage budget matching the query. Accepts a provider/id (e.g. anthropic/claude-sonnet-4-20250514), a provider name (e.g. openai), a built-in key (e.g. cheap, vision, reasoning, long-context), or a user-defined alias. |
| Model picker | Built-in keys and user aliases also appear as codexbar/cheap, codexbar/vision, etc. in the Pi model picker. Selecting one resolves it to the best-ranked real model — no slash command needed. |
/codexbar-switch <query> --dry-run |
Preview the ranked candidates without actually switching. Shows each model’s remaining budget percentage. |
/codexbar-switch <query> --exclude=<provider> |
Exclude one or more providers from consideration. Repeat the flag to exclude multiple: --exclude=openai --exclude=anthropic. |
/codexbar-toggle |
Turn the footer widget on/off in real time. When off, the widget is cleared and session_start / agent_end / model_select no longer refresh it. State is persisted to user-scope settings.json under the root enabled flag (project-scope override still applies as usual). |
The status command prints a notification with the formatted usage line and refreshes the footer widget.
/codexbar-switch
The switch command resolves a query against the model registry, user aliases, and built-in classifications, then ranks matching models by remaining CodexBar usage budget. It can also be invoked as a tool (codexbar_switch_model) from within an agent session.
Query resolution tiers (first non-empty tier wins):
| Tier | Match |
|---|---|
exact |
provider/id literal, e.g. anthropic/claude-sonnet-4-20250514. |
registry |
Token-subsequence match against model ids in pi's registry — opus-4-7 matches claude-opus-4-7-20251022, gemini-3.1 matches gemini-3.1-pro. |
alias |
User-defined key in aliases.json (see below). |
builtin |
One of the built-in classifications listed below. |
Built-in classifications:
| Key | Selects |
|---|---|
cheap |
Top 5 models by ascending total cost (input + output per 1M tokens) |
vision |
Models that accept image input |
reasoning |
Models with reasoning enabled |
long-context |
Models with context window ≥ 200K tokens |
Notification conventions:
| Notification | Meaning |
|---|---|
⏳ |
Resolving candidates… |
📊 |
Preview / list / dry-run result |
✅ |
Successfully switched |
⚠️ |
Validation error (bad flags, unknown query) |
❌ |
Runtime error (usage unavailable, switch failed) |
User-defined aliases can be placed in ~/.pi/agent/extensions/pi-codexbar/aliases.json or ~/.pi/agent/extensions/model-switch/aliases.json. Each key maps to a model string (provider/id) or an array of model strings. The extension merges both files, with pi-codexbar taking precedence on collisions.
Model picker aliases
Built-in keys (cheap, vision, reasoning, long-context) and all user-defined aliases are registered as virtual models under the codexbar provider. They show up in the Pi model picker and can be selected like any other model.
When you pick e.g. codexbar/cheap:
model_selectfires with{ provider: 'codexbar', id: 'cheap' }.- The extension calls
runSwitch({ action: 'switch', query: 'cheap', excludeProviders: ['codexbar'] })against the full model registry. - The winner (best-ranked real model) is activated via
pi.setModel(). - A notification confirms the switch:
✅ cheap → anthropic/claude-haiku-4-5.
The virtual model is never used to stream requests — it's a one-click shortcut that always resolves to a real provider.
Note: The virtual
codexbarprovider has placeholder request config (no real API endpoint). If alias resolution fails, the extension notifies you with the specific reason and leaves the virtual entry active so you can pick another.
codexbar_switch_model tool
The same functionality is exposed as a tool so an agent can switch itself mid-session. Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
action |
'switch' | 'list' |
yes | switch picks the best-ranked model and activates it; list returns resolved candidates without changing state. |
query |
string |
optional | Model selector — see Query resolution tiers above. Empty reuses the current provider's models. |
excludeProviders |
string[] |
optional | Pi-native provider ids to exclude from ranking (e.g. ['openai', 'anthropic']). |
dryRun |
boolean |
optional | With action: 'switch', return the ranked budget breakdown without activating any model. |
Returns a single text content block. On success: ✅ Switched to <provider>/<id> (for switch), or the formatted candidate table (for list / dryRun). On failure: ❌ <reason>. The tool never throws — errors are always returned as text so the agent can react.
Usage examples
All examples assume pi is running and the CodexBar CLI is on $PATH. Slash commands and tool calls are interchangeable — the tool is what the agent calls autonomously, the slash command is what you type.
1. Switch to any Claude model with the most remaining budget
/codexbar-switch anthropic
Resolves via the registry tier (provider name), ranks the matching Anthropic models by remaining session/weekly/monthly window, activates the winner.
2. Pick the cheapest model right now (by cost × budget)
/codexbar-switch cheap
cheap is a built-in that shortlists the five cheapest models by total token cost, then ranks them by remaining budget so you get the cheapest model that still has headroom.
3. Switch by token subsequence (partial model id)
/codexbar-switch opus-4-7
Matches anthropic/claude-opus-4-7-20251022 (and any other id where the tokens opus, 4, 7 appear in order). Useful when you can't remember the exact date stamp.
4. Switch by exact provider/id
/codexbar-switch anthropic/claude-sonnet-4-20250514
Top-priority exact tier — never ambiguous, skips ranking (only one candidate).
5. Preview without switching
/codexbar-switch reasoning --dry-run
Ranks the reasoning built-in's candidates and prints the budget breakdown, but leaves the active model unchanged. Good for "what would happen if I switched right now?".
6. Exclude providers
/codexbar-switch coding --exclude=openai --exclude=anthropic
Resolves the coding alias, drops any OpenAI/Anthropic candidates, ranks the rest. Repeat --exclude to stack, or use --exclude=openai,anthropic with a comma.
7. List candidates without ranking
/codexbar-switch list vision
Shows the tier that matched and the list of candidate provider/ids for the vision built-in. No CodexBar calls, no budget fetch — pure resolution preview.
8. Agent tool call: inspect before switching
{
"name": "codexbar_switch_model",
"input": { "action": "list", "query": "opus" }
}
Returns a text block with the resolved candidates so the agent can reason about which to pick.
9. Agent tool call: budget-aware switch with exclusions
{
"name": "codexbar_switch_model",
"input": {
"action": "switch",
"query": "cheap",
"excludeProviders": ["openai"],
"dryRun": false
}
}
Picks the cheapest non-OpenAI model that still has budget and activates it.
aliases.json
User aliases let you define your own keys for /codexbar-switch <key>. Each value is either a single provider/id string or an array of them (first-match wins inside an array, but pi-codexbar ranks the whole array by remaining budget before picking).
File locations and precedence:
~/.pi/agent/extensions/pi-codexbar/aliases.json(pi-codexbar-specific)~/.pi/agent/extensions/model-switch/aliases.json(shared with the pi-model-switch extension)
Both files are loaded and merged; pi-codexbar's file wins on key collisions.
Example aliases.json:
{
"default": "openai/gpt-5.4",
"coding": [
"openai/gpt-5.4",
"anthropic/claude-sonnet-4-20250514",
"github-copilot/claude-sonnet-4.6"
],
"reasoning": [
"anthropic/claude-opus-4-7-20251022",
"openai/gpt-5.4"
],
"sonnet": [
"anthropic/claude-sonnet-4-20250514",
"github-copilot/claude-sonnet-4.6"
],
"cheap-local": [
"ollama/llama-3.3-70b",
"ollama/qwen2.5-coder-32b"
]
}
With that file in place, /codexbar-switch coding evaluates all three coding models, fetches their CodexBar usage, and activates whichever has the most budget left. /codexbar-switch sonnet does the same for the two Sonnet routes — handy when you want the Sonnet quality tier and don't care which backend serves it.
Interop with pi-model-switch
pi-codexbar intentionally reads ~/.pi/agent/extensions/model-switch/aliases.json so aliases you've already defined for the pi-model-switch extension (slash command + tool for direct model switching) are available in pi-codexbar for free. You don't need to duplicate them.
Suggested arrangement:
- Put shared aliases (
coding,sonnet,cheap, …) inmodel-switch/aliases.json— both extensions see them. - Put pi-codexbar-only overrides in
pi-codexbar/aliases.json— e.g. abudgetalias that only includes providers with CodexBar support. - Use pi-model-switch when you want a direct, deterministic switch to a known model.
- Use pi-codexbar (
/codexbar-switch) when you want the same alias resolved to whichever candidate has the most remaining budget right now.
Footer Widget
On every session_start, agent_end, and model_select, the extension resolves the active model's provider, maps it to a CodexBar id, and repaints a widget called codexbar-usage above or below the input editor.
A typical line looks like:
claude (max) │ S(5h): 42% │ W(7d): 18%M(1mo): 6% │ $23.45 │ ⏱ Apr 20, 3:00 PM
Tokens available in the format string:
| Token | Meaning |
|---|---|
{provider} |
CodexBar provider id (e.g., claude, codex). |
{plan} |
Login method / plan label (max, api, …). |
{session} |
Primary window usage — e.g., S(5h): 42%. |
{weekly} |
Secondary window usage. |
{monthly} |
Tertiary window usage (if the provider reports one). |
{credits} |
Remaining credit balance, formatted as $X.XX. |
{session_reset} |
Human-readable reset time for the primary window. |
{weekly_reset} |
Reset time for the secondary window. |
{monthly_reset} |
Reset time for the tertiary window. |
Empty tokens and the ⏱ marker are stripped from the rendered line, so you can leave tokens you don't care about in the format string without collecting stray │ separators.
Windows exceeding colors.highThreshold percent (default 80) switch from their normal color to the matching *High color (e.g., session → sessionHigh). Override the threshold or individual colors in settings.
Configuration
pi-codexbar merges configuration from three layers, in increasing precedence (last wins):
| Scope | Path | Priority |
|---|---|---|
| Builtin | <package root>/settings.json & <package root>/provider-mappings.json |
Lowest |
| User | ~/.pi/agent/extensions/pi-codexbar/{settings,provider-mappings}.json |
Medium |
| Project | <cwd>/.pi/extensions/pi-codexbar/{settings,provider-mappings}.json |
Highest |
Each layer is optional. Missing keys fall back to the previous layer and, ultimately, to in-code defaults. Merging is shallow per top-level key (footer, colors, provider mapping entries) — provide only what you want to override.
User-level overrides
Drop a file at ~/.pi/agent/extensions/pi-codexbar/settings.json:
{
"footer": {
"format": "{provider} │ {session} │ {credits}",
"placement": "aboveEditor"
},
"colors": {
"provider": "#ffffff",
"session": "#00ff88",
"sessionHigh": "#ff0055",
"highThreshold": 75
}
}
Project-level overrides
Place a file at <repo>/.pi/extensions/pi-codexbar/settings.json. Use this when a given repo should always show a specific layout — e.g., a team monorepo where everyone is on a flat-rate plan and the {credits} token should be hidden.
Settings schema
| Key | Type | Default |
|---|---|---|
enabled |
bool | true — toggled by /codexbar-toggle; persisted at user scope. |
footer.format |
string | {provider} {plan} │ {session} │ {weekly}{monthly} │ {credits} │ ⏱ {session_reset} |
footer.placement |
enum | belowEditor (also accepts aboveEditor) |
colors.provider |
hex | #d787af |
colors.plan |
hex | #808080 |
colors.session / …High |
hex | #5faf5f / #ff5f5f |
colors.weekly / …High |
hex | #00afaf / #ff8700 |
colors.monthly / …High |
hex | #af87d7 / #ff5f5f |
colors.reset |
hex | #808080 |
colors.separator |
hex | #4e4e4e |
colors.credits |
hex | #febc38 |
colors.error |
hex | #ff5f5f |
colors.highThreshold |
number | 80 (percent; switches session/weekly/monthly to *High colors above this value) |
See the bundled settings.json for the complete default payload.
Provider mappings
pi and CodexBar use different provider identifiers. The bundled provider-mappings.json translates pi ids (and provider family names) into the canonical CodexBar id:
{
"anthropic": "claude",
"openai-codex": "codex",
"github-copilot": "copilot",
"google": "gemini",
"openrouter": "openrouter"
}
Lookup rules:
- Exact match on the lower-cased pi provider id.
- Prefix match —
openai-codex-preview→codex. - Otherwise the id is passed through untouched.
Override or extend on either scope:
// ~/.pi/agent/extensions/pi-codexbar/provider-mappings.json
{
"my-internal-proxy": "openrouter",
"anthropic": "claude-experimental"
}
Only the entries you list are merged in; everything else still comes from the bundled mapping (or the user layer if you're overriding at the project scope).
Resolution priority: project mapping > user mapping > bundled mapping > identity passthrough.
Common Recipes
| Goal | Config change |
|---|---|
| Minimal footer — provider + credits only | footer.format = "{provider} │ {credits}" |
| Warn earlier when approaching quota | colors.highThreshold = 50 |
| Show the widget above the editor | footer.placement = "aboveEditor" |
| Route a custom proxy provider to CodexBar | Add a row to provider-mappings.json (user or project scope) |
| Disable the widget in a flat-rate repo | Project-scope settings with footer.format = "{provider}" |
Caching
Provider usage is cached under ~/.pi/agent/extensions/pi-codexbar/.cache/usage-<provider>.json with a 60-second TTL. This keeps footer refreshes on every agent_end from spawning one CodexBar process per turn.
- Stale files are overwritten on the next successful fetch.
- Errors are not cached — a failing run always re-hits the CLI.
- To force a refresh, either wait out the TTL or delete the cache file.
Events & Runtime Behavior
| Pi event | Behavior |
|---|---|
session_start |
Detect provider from ctx.model.provider, fetch usage, render widget. Also re-registers the virtual codexbar provider so alias edits pick up new entries. |
agent_end |
Refresh the widget after every agent turn. |
model_select |
When the selection targets codexbar/<alias>, resolve the alias through runSwitch and activate the winner. Otherwise re-fetch usage for the new provider's footer. |
All three handlers swallow errors internally — a failed CodexBar call never blocks the event loop or interrupts pi.
Alias resolution notifications
| Notification | Meaning |
|---|---|
✅ <alias> → <provider>/<id> |
Alias resolved; winner activated. |
⚠️ No candidates matched "<alias>". |
Alias resolution produced zero candidates. |
⚠️ Alias resolution did not return a switch candidate. |
Resolver returned list/preview instead of switch. |
❌ Failed to switch to <provider>/<id> — API key may not be configured. |
pi.setModel() returned false (missing auth for the resolved model). |
Development
npm install
npm test # node --test, via tsx loader
npm run run # smoke-run the extension entry point
- No bundler / transpile step — the extension ships
src/*.tsand pi loads it throughtsx. - CI runs
npm ci && npm teston push and pull requests (see.github/workflows/).
License
MIT.