No comments yet. Be the first to share your thoughts.
Automatic Claude Code account switching. Run claude exactly as you always do; when the active account nears its usage limit, tokenmaxxing swaps the credential to a fresher account at a safe turn boundary and your session keeps running on it - no restart, same conversation. Works across many concurrent sessions. Only when the whole pool is at its limit does anything visible happen: a countdown that auto-resumes at the soonest reset, when that reset falls within policy.maxWaitMs (default 1h - a longer wait stays put rather than parking your terminal for hours).
Scope: Claude Code only, macOS and Linux. It pools subscription accounts (Pro/Max), not API keys.
A running claude re-checks the credential store between requests, so a swapped credential is adopted in-place (within ~30s on macOS, the next request on Linux) - a swap never restarts your session. The one case that still needs process management is a fully depleted pool: a session cannot pause itself, so a thin claude supervisor on your PATH stops it at a committed turn boundary (the transcript is already on disk, nothing is lost), shows a countdown, and auto-resumes claude --resume <id> at the soonest reset. Everything else about claude is unchanged - all flags, MCP, hooks, and skills pass through.
Requires and Claude Code, on macOS or Linux.
Or with Nix (same source-run-by-Bun package; init still owns credentials, the claude shim, and settings merges). Install onto PATH first, then init — nix run ... -- init alone leaves supervisor shims without a stable tokenmaxxing on PATH after the ephemeral run exits:
nix-darwin:
Home Manager:
init imports the account you're already on, installs the claude supervisor + four settings.json entries (the tokenmaxxing statusLine, a subagentStatusLine, a Stop hook, a SessionStart hook), and adds the supervisor's bin dir to PATH in your shell rc (idempotent; it must sit ahead of the real claude to intercept it). Restart your shell, then add more accounts and go:
Switching engages (configurable) once the active account's 5-hour session window is 50% used - from there, every evaluation greedily converges on the usable account furthest behind its own weekly pace, and does nothing when the current account already wins. Independent of that, crossing a hard screening bar - 95% session or 98% weekly - always forces a switch. The bars also screen candidates on any of:
claude -p '/usage' (free, 0 tokens, TTL-cached) whenever the active model is one of policy.switchModels, so a Fable session switches on the Fable cap while a Sonnet session rides the aggregate.The bars' headroom is deliberate: it's the budget to reach a clean turn boundary (plus up to one turn of adoption lag on macOS) before the wall. The session bar sits lower (95) because a 5-hour reset is cheap to sit out; weekly quota is use-it-or-lose-it, so it drains closer to the wall (98). The greedy engagement floor sits far below both: weekly allowance is forfeited at each account's fixed reset, so once half a session window justifies the swap, quota is best burned on whichever account has the most at risk.
The target is chosen greedily off each account's cached windows: among usable accounts (every window under its bar, or past its reset), the one furthest behind its own weekly pace - highest remaining% divided by time to its weekly reset - because unused weekly allowance is forfeited at the fixed per-account reset. Cached resets are absolute UTC epochs, so a stale snapshot still resolves correctly: a weekly reset that has passed extrapolates forward in 7-day steps, and a session window past its reset counts as empty. Both tokenmaxxing switch and the automatic path rank the current account too and do nothing when it already wins, so they are idempotent - evaluating periodically converges on the right account.
~/.config/tokenmaxxing/config.json (every field optional):
projectionMargin is a fixed safety margin subtracted from each threshold bar (effective bar = threshold - margin), so a large turn is less likely to blow past a bar between checks; greedySessionFloor is the session-used % at which the greedy convergence engages; switchModels names the models whose per-model cap triggers a switch; usagePollTtlMs is how long a /usage per-model poll stays fresh; maxWaitMs bounds the depleted-pool countdown - a soonest reset further out than this does not pause the session (no respawn marker is written and the session simply keeps hitting its limit until an account recovers).
State lives entirely in ~/.config/tokenmaxxing/. Per-account credentials follow the platform's Claude Code store: the login keychain on macOS (tokenmaxxing-cred-<uuid8> items, never plaintext on disk), 0600 files under ~/.config/tokenmaxxing/creds/ on Linux (the same plaintext model claude itself uses for ~/.claude/.credentials.json).
For agents you build on the against your own pooled accounts, tokenmaxxing is importable as a library (your agent app must run under Bun: tokenmaxxing ships TypeScript source and uses bun:ffi):
The SDK reads credentials when it spawns the claude subprocess and has no statusLine, so none of the CLI-side supervisor machinery applies; the integration is boundary-driven instead. ensureBestAccount() runs the exact greedy decision the CLI hooks and timer run (screening bars, pace-pressure target, post-swap cooldown - all shared code); like them, it deliberately does nothing until the decision engages (the active session past policy.greedySessionFloor, or a bar crossed), so a fresh account rides instead of churning. pooledOptions() pins pathToClaudeCodeExecutable to the real claude binary and supplies a full replacement env with every ambient credential override (ANTHROPIC_API_KEY, CLAUDE_CODE_OAUTH_TOKEN, ...) scrubbed, so the subprocess resolves the pool's live credential and nothing else. The pooled surface requires the default Claude Code credential store: it fails fast if CLAUDE_CONFIG_DIR or CLAUDE_SECURESTORAGE_CONFIG_DIR is set in your app's environment, because a swap would write the live credential where those point while the spawned subprocess reads the default store. stopHookCheck re-runs the decision at turn boundaries; a swap it lands takes effect on the next subprocess spawn (it never yanks a mid-query token). If your app loads user settings (see the SDK's settingSources), the Stop hook tokenmaxxing init installed may already fire in SDK sessions too - stopHookCheck makes the check explicit and works when settings are restricted.
This is for pooling your own subscription accounts in agents you run yourself - the same personal-use posture as the CLI. Anthropic does not allow third-party products to offer claude.ai login or rate limits, including agents built on the Agent SDK; don't ship this surface to third parties.
The same pooling works for OpenAI's Codex CLI (your own ChatGPT-subscription accounts):
Codex mechanics differ from Claude Code in one hard way: a running codex process refuses a credential swapped to a different account, so a restart is the switch. The installed Stop hook runs the same greedy pace-pressure decision at each turn boundary (usage read free from codex's own rate-limit endpoint: percentages plus absolute reset times, weekly aggregate and per-model caps alike); when it swaps, the supervisor relaunches codex resume <session-id> on the fresh account with the transcript intact. tokenmaxxing switch --codex [sel] does it manually, status/watch/ls show both pools.
Two codex-specific facts worth knowing: codex does not run hooks it has not been told to trust, so after init --codex you must open codex once and trust the tokenmaxxing Stop hook via /hooks (auto-switching is inert until then); and codex has no cross-process lock on auth.json, so tokenmaxxing serializes all of its own credential writes behind its own lock and swaps only at idle turn boundaries.
claude stop for the countdown; anything typed in that split second is lost.flock + re-check keeps racing hooks from burning two accounts.init/add touch the keychain interactively so the first security access isn't cold inside a headless hook.TypeScript on Bun. One multi-call entry (src/main.ts) runs the CLI, the claude supervisor, and the hook/statusLine shims. validates every external-boundary payload (credential blobs, hook/statusLine stdin, OAuth responses, config). for utilities. The supervisor is process/terminal-only. It never proxies API traffic or touches tokens in flight. Cross-process coordination uses flock(2) via bun:ffi (macOS has no flock(1); one codepath for both platforms). Credential I/O goes through one platform-selected store: security(1) generic-passwords on macOS, atomic 0600 file writes on Linux.
MIT
$ claude ...you work normally; swaps are invisible (watch the statusline account flip)... ⏳ tokenmaxxing: all accounts at their limit. Resuming on work@acme.com when it resets (Ctrl-C to resume now).bun add -g tokenmaxxingtokenmaxxing initnix profile install github:anaclumos/tokenmaxxingtokenmaxxing init# flake inputs: tokenmaxxing.url = "github:anaclumos/tokenmaxxing";modules = [ inputs.tokenmaxxing.darwinModules.withOverlay { programs.tokenmaxxing.enable = true; }];# then: tokenmaxxing initimports = [ inputs.tokenmaxxing.homeManagerModules.default ];programs.tokenmaxxing.enable = true;programs.tokenmaxxing.package = inputs.tokenmaxxing.packages.${pkgs.system}.default;# then: tokenmaxxing inittokenmaxxing add # logs one in, in isolation, and pools itclaude # use claude as always{ "thresholds": { "session": 95, "weekly": 98 }, "policy": { "projectionMargin": 0, "greedySessionFloor": 50, "switchModels": ["fable"], "usagePollTtlMs": 90000 }}import { query } from "@anthropic-ai/claude-agent-sdk";import { ensureBestAccount, pooledOptions, stopHookCheck } from "tokenmaxxing";
await ensureBestAccount(); // run the switch decision before the spawn (swaps once it engages - see below)
for await (const message of query({ prompt: "...", options: { ...pooledOptions(), // pinned real claude + scrubbed env -> the pooled live credential hooks: { Stop: [{ hooks: [stopHookCheck] }] }, // re-decide at every turn boundary },})) { // capture the session id from the init message if you want `resume` across swaps}tokenmaxxing init --codex # import your current codex login + install the codex supervisor & Stop hooktokenmaxxing add --codex # log in another account, isolated - your primary login is untouchedcodex # use codex as always