pi-codexbar

Pi extension wrapper for CodexBar provider state and controls

Package details

extension

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_select events → 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 codexbar provider. 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:

  1. /usr/local/bin/codexbar (macOS default)
  2. /usr/bin/codexbar
  3. /opt/homebrew/bin/codexbar
  4. Whatever which codexbar resolves 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:

  1. model_select fires with { provider: 'codexbar', id: 'cheap' }.
  2. The extension calls runSwitch({ action: 'switch', query: 'cheap', excludeProviders: ['codexbar'] }) against the full model registry.
  3. The winner (best-ranked real model) is activated via pi.setModel().
  4. 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 codexbar provider 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:

  1. ~/.pi/agent/extensions/pi-codexbar/aliases.json (pi-codexbar-specific)
  2. ~/.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, …) in model-switch/aliases.json — both extensions see them.
  • Put pi-codexbar-only overrides in pi-codexbar/aliases.json — e.g. a budget alias 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., sessionsessionHigh). 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:

  1. Exact match on the lower-cased pi provider id.
  2. Prefix match — openai-codex-previewcodex.
  3. 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/*.ts and pi loads it through tsx.
  • CI runs npm ci && npm test on push and pull requests (see .github/workflows/).

License

MIT.