{"slug": "show-hn-claude-code-hits-its-5-hour-limit-codex-picks-up-in-the-same-terminal", "title": "Show HN: Claude Code hits its 5-hour limit, Codex picks up in the same terminal", "summary": "LegCli released Leg, a terminal tool that watches coding-agent usage limits and hands off work to the next agent in the same terminal when a limit is hit, starting with Claude Code's five-hour cap and moving to Codex. Leg tracks usage per agent and account via Claude Code's usage endpoint and StopFailure hook, Codex's read-only app-server rate-limit read, agy's log, and Grok's billing proxy endpoint, and refreshes a context-handoff-bundle published on PyPI so the session resumes without retyping. The default ladder runs claude/fable, claude/opus, claude/sonnet, then each remaining installed agent, and Leg strips API-key environment variables such as ANTHROPIC_API_KEY and OPENAI_API_KEY to enforce subscription logins.", "body_md": "**Type `leg claude`, `leg codex`, `leg agy` or `leg grok` instead of the bare command. You get the same interactive agent; Leg opens a board next to it, watches the usage limit, keeps a handoff bundle current, and when the limit hits it starts the next agent in the same terminal from that bundle.**\n\n*Claude hits the five-hour wall. The terminal reads `handing off to codex`, and codex carries on there. Nothing is retyped. ([the full 53-second run](https://legcli.com/#handoff))*\n\nYou keep using your coding agents exactly as you do today, in any terminal,\nfrom your own config directory: Leg adds its hooks in a separate per-session\nsettings file and never edits yours (unless you turn on the [portable\nharness](https://github.com/ucsandman/LegCli/blob/main/docs/harness.md), which writes only marked, backed-up, Leg-owned\nfiles and regions). `leg claude --model opus` is\n`claude --model opus` with four things running alongside it:\n\n1. **A board.** Opened once in your browser, reused after that. Every Leg\nsession in every terminal is a row on it, and the row's register reads\nstatus, repo on branch, uncommitted and unpushed counts, agent and model,\nand how long it has been quiet:`waiting on you  leg on main  dirty 3 ahead 2  claude/fable` . Under that come the prompt, the one thing worth\nknowing, the files it is touching and the usage bucket that will stop it.\nCapacity is one strip at the top rather than a region, with the login\npanels and their gauges behind**Capacity and models** . Two sessions\nediting the same file in one repo are flagged on both rows, and a second\nsession in a checkout that already has one gets its own worktree and a**Land** button instead of writing over the first.\n2. **Usage tracking** per agent and account, from what each CLI already\nexposes: Claude Code's usage endpoint and its`StopFailure` hook, Codex's\nread-only app-server rate-limit read, agy's log, and Grok's billing proxy endpoint.\n3. **A context handoff bundle** ([context-handoff-bundle](https://pypi.org/project/context-handoff-bundle/) )\nrefreshed as the session goes, so the work is always ready to hand off.\n4. **The handoff itself.** Near the limit you get a warning. At the limit Leg\nsaves the bundle, stops the agent, and starts the next option in the same\nterminal from that bundle. The options are a ladder of rungs, and a rung is\nan agent, a login and a model: the default is`claude/fable` ,`claude/opus` ,`claude/sonnet` , then each remaining installed agent. A\nFable limit walls Fable and not the login, so the first move is usually to\nanother model on the same subscription, and for claude that move is`claude --resume <id> --model opus` , which keeps the conversation instead\nof replaying the bundle. Only when every claude rung is out does it move to\nanother CLI. Settings holds the ladder for new terminals,`leg ladder` edits it from a shell, and**Details** on a row edits the copy that\nterminal is running. Nothing is retyped. When every rung is out, Leg tells\nyou which resets first and when, waits for that reset with a countdown, and\nstarts that agent from the bundle.\n\nSubscription logins only: Leg strips `ANTHROPIC_API_KEY`,\n`ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_BASE_URL`, `ANTHROPIC_CUSTOM_HEADERS`,\n`OPENAI_API_KEY`, `OPENAI_BASE_URL`, `OPENAI_API_BASE`, `GEMINI_API_KEY`,\n`GOOGLE_API_KEY`, `GOOGLE_GEMINI_BASE_URL`, `GOOGLE_GENAI_USE_VERTEXAI`,\n`GOOGLE_GENAI_USE_ENTERPRISE`, `GOOGLE_CLOUD_PROJECT`,\n`GOOGLE_CLOUD_LOCATION`, `GOOGLE_APPLICATION_CREDENTIALS`, `GROK_API_KEY`,\n`XAI_API_KEY`, `CLAUDECODE`, `CLAUDE_CODE_*`, `CLAUDE_EFFORT`, and\n`CLAUDE_PLUGIN_DATA` before any agent starts. It then sets\n`CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS=0` for a detached\nClaude print session. Leg never edits `~/.claude/settings.json` or any other\nsettings file of yours; its hooks ride in a separate per-session `--settings`\nfile. The one thing it does write outside `~/.leg` by default is the\nfolder-trust answer, below; the optional [portable harness](https://github.com/ucsandman/LegCli/blob/main/docs/harness.md)\nis the other, and only after `leg harness enable`. `leg uninstall` removes only `~/.leg`.\n\nEach agent CLI asks once, the first time it runs in a directory, whether you\ntrust that folder, and Claude Code asks a second question when a `CLAUDE.md`\nabove the repo imports a file from outside it. A handoff fires when the limit\nhits, which is usually when nobody is watching, so an agent that stopped on\nthat prompt would sit there until morning with the bundle already written.\n\nBefore starting an agent, Leg records the same answer you would have given,\nfor the repository you already chose by typing `leg claude` in it:\n\n| agent | file | what is written | \n|---|---|---|\n| claude | `~/.claude.json` (or`$CLAUDE_CONFIG_DIR/.claude.json` ) | `projects[\"<repo>\"].hasTrustDialogAccepted: true` | \n| claude | the same entry, only when such an import exists | `hasClaudeMdExternalIncludesApproved` ,`hasClaudeMdExternalIncludesWarningShown` | \n| codex | `~/.codex/config.toml` | `[projects.\"<repo>\"] trust_level = \"trusted\"` | \n| agy | `~/.gemini/antigravity-cli/settings.json` | `trustedWorkspaces: [\"<repo>\", \"<worktree>\"]` | \n\nFor Claude Code this is the documented remedy: its permissions guide says to\nset `projects[\"<path>\"].hasTrustDialogAccepted` to `true` in `~/.claude.json`,\nwhere `<path>` is the repository root.\n\nLeg never creates one of those files: if it is not there, that CLI has not\nrun as you yet and its own first-run flow is next, with you at the keyboard. It\nnever rewrites a file to say what it already says, and it never removes what is\nalready in one. When it approves an external `CLAUDE.md` import it prints the\nfull path of every file it approved, to the terminal and to the session\ntimeline on the board, so the approval is on the record rather than invisible.\n\nSet `LEG_TRUST=never` to switch all of it off and answer the prompts\nyourself.\n\nPrerequisites: Node 22 or newer, git, Python 3 with pip, and at least one\nlogged-in agent CLI (`claude`, `codex`, `agy` or `grok`).\n\n```\nnpm install -g @ucsandman/legcli\npip install -U context-handoff-bundle\ncd <any repo>\nleg claude\n```\n\n`npm install -g leg-agents` is the same release: it pins this version of\n`@ucsandman/legcli` and exposes the same `leg` binary.\n\nThat is the whole setup. The first `leg <agent>` starts the board on\n[http://127.0.0.1:4747](http://127.0.0.1:4747) and opens it; later sessions reuse it. Anything after the\nagent name passes straight through (`leg codex -m gpt-5.3-codex-spark`,\n`leg claude --resume`). The agent's own prompt and permission flags pass\nthrough unchanged, and your settings file is never edited: Leg's hooks ride\nin a separate per-session `--settings` file.\n\nLeg is commercial, source-available software, and the source is on\n[GitHub](https://github.com/ucsandman/legcli): every `.mjs` file that runs is in the package you just installed, at\n`$(npm root -g)/legcli/src`, and the license lets you read it and modify\nyour own copy. There is nothing compiled, minified or bundled to see through.\n\nNothing is guessed from screen scraping. Each tap was read from the CLI's source or documentation and then checked on a real machine (2026-09-11, Claude Code 2.1.268, codex-cli 0.153.4, agy 1.2.0); the rightmost column says which.\n\n| agent | usage percentages | the wall (limit hit) | how Leg attaches | status | \n|---|---|---|---|---|\n| claude | `GET api.anthropic.com/api/oauth/usage` with the login Claude Code stored, the same data as`/usage` and the built-in status line (`five_hour` ,`seven_day` ,`utilization` ,`resets_at` ); polled every 60 s | `StopFailure` hook with`error: rate_limit` ([docs](https://code.claude.com/docs/en/hooks#stopfailure) ) | one extra settings file per session via `--settings` , carrying only Leg's own hooks;`autoContinueAtUsageLimit` is set to`false` because Leg owns the handoff | observed live | \n| codex | read-only `account/rateLimits/read` through the app-server, polled every 60 s by the board and active attach; windows are identified by duration (300 minutes = 5h, 10080 = 7d) | `task_complete.error.codex_error_info: usage_limit_exceeded` , message \"You've hit your usage limit … try again at …\" (`codex-rs/protocol/src/error.rs` ) | no model turn and no hook are injected; the board reads the CLI backend and records only returned windows | verified by source and regression tests | \n| agy | none exposed (agy's own status line fetches a quota summary that is written nowhere) | `RESOURCE_EXHAUSTED` , \"it resets in …\", \"out of quota\" in the log | `--log-file` per session;`~/.gemini/antigravity-cli/history.jsonl` gives the prompts and conversation id | observed live (a real `RESOURCE_EXHAUSTED` with its reset was read from the log on 2026-09-11) | \n| grok | `GET cli-chat-proxy.grok.com/v1/billing?format=credits` and`GET cli-chat-proxy.grok.com/v1/user?include=subscription` with OAuth token from`~/.grok/auth.json` (`creditUsagePercent` ,`currentPeriod` weekly reset); polled every 60 s | `-32003` rate limit error, \"You've hit the rate limit for your plan. Try again later.\", \"Rate limited (429)\",`StopFailureKind::RateLimit` (`xai-org/grok-build` ) | `--debug-file grok.log` per session;`~/.grok/sessions/<encoded-cwd>/prompt_history.jsonl` gives prompts and session id | verified by source and proxy endpoint | \n\nWhy not Claude Code's status line JSON (`rate_limits.five_hour.used_percentage`):\non 2.1.268 the custom `statusLine` Leg passes through `--settings` did not\nrun, so the endpoint poll is the source. Leg still writes a `statusLine`\nentry that records the same fields, so the moment a build honours it the poll\nbecomes a fallback.\n\nFor Codex, Leg does not infer availability from a lower percentage: only an\nexplicit available answer from the backend clears an earlier wall. The board\nlabels each bar as `<n>% used` and marks an old reading as stale rather than\npresenting it as current.\n\n1. **Warning.** At 85 % of any window (`LEG_WARN_PCT` ) the session card turns\namber, the event log names the next option, and the terminal bell rings once.\n2. **Limit.** claude: the`StopFailure` hook fires with`rate_limit` . codex: the\nrollout reports`usage_limit_exceeded` . agy: the log says`RESOURCE_EXHAUSTED` . The account is marked walled until the reset the CLI\nreported (or the soonest known window reset).\n3. **Bundle.** Leg writes structured notes (task, the last messages from the\ntranscript,`git diff --stat` , dirty files, files edited this session, recent\ncommits, why it stopped) and runs`context-handoff-bundle save --repo-local` with one slug per session, updated in place (`--update <slug>` ) at every\ncheckpoint, about every two minutes while the session is active, and at\nevery warning, limit and hand-off. If the session maintained`.leg/SYNTHESIS-<session-id>.md` ,\nLeg inlines it into the resume file as a`## Synthesis` section ahead of the raw dump.\n4. **Switch.** The agent process is stopped and the terminal is restored. Two\nclaude destinations keep the conversation itself instead of the bundle: a\nweaker model on the same login (`claude --resume <id> --model <alias>` ),\nand another claude login you added with`leg accounts add` , which sees the\nsame transcript through the`projects` junction the account carries\n(`claude --resume <id>` under that login's`CLAUDE_CONFIG_DIR` ). A weekly\nor Fable wall on one login then continues on the other with the\nconversation it already had, and the timeline says`kept the conversation` . Every other rung starts in the same terminal with a short\npointer prompt:\nread`.leg/RESUME-<session-id>.md` (the`context-handoff-bundle load` output, the`## Synthesis` section if present, and the reason for the switch),\ncheck`git status` and`git diff` , continue, do not ask the human to restate\nthe task. The pointer prompt directs the next agent to read Synthesis first,\ntreat ruled-out approaches as settled, and start from the top-ranked next step.\nThe same text is copied to`.leg/RESUME.md` , the file people open by habit, and\nboth are stamped with the commit and the live terminals they describe.`claude \"<prompt>\"` ,`codex \"<prompt>\"` and`agy -i \"<prompt>\"` all open the\nnormal interactive session with that first turn.\n5. **The ladder.** A rung is an agent, a login and a model, and Leg walks from\nrung 1 every time. Other models on the same login come first, then other\naccounts of the same agent, then every other agent in the saved order, each\ntried once. It is a priority list, not a rotation: put agy at the bottom and\nagy is the last rung from a Claude terminal and from a Codex terminal alike.\nThe board prints the exact sequence with the rung running now marked, plus\nthe first rung eligible from current install and limit state. Use**Change\nthe ladder** in a terminal's expansion to change that terminal,`leg ladder` to do it from a shell, or Settings to set the default copied by new\nterminals. A rung whose CLI is missing, whose wall has not reset, or that\nshares the window that is already out, is skipped with its reason.\n6. **All out.** The terminal prints each option with its reset time, soonest\nfirst, then stays open with a countdown to the first reset and starts that\nagent from the bundle when it arrives. The row says`waiting for <agent> at <time>` . Ctrl-C (or End on the row) quits with exit 3 instead.\n\nYou can force a handoff any time: the **Hand off now** button on the row, or\n`leg sessions handoff <id>`. Verified on this machine: `leg claude` opened\nthe real Claude Code TUI with Leg's hooks firing into the session log, the\nusage poll recorded 36 % of the 5h window and 74 % of the 7d window, the\nwarning fired at 96 % of the 7d window and named codex as the next option, and\na limit saved the bundle, stopped claude and started codex in the same\nterminal with the pointer prompt. A real `StopFailure` arrived on 2026-09-11\nand is kept at `fixtures/live/claude/limit-rate_limit.json`; to drive the path\non demand, `leg sessions simulate-limit <id>` sends the same `StopFailure`\n`rate_limit` payload Claude Code would send through Leg's hook: verified end\nto end on a haiku session, the hook set the limit, the runner saved the\nbundle, stopped claude and started codex, which read `.leg/RESUME.md` on its\nfirst turn. The simulated wall clears after two minutes and is never kept as\nevidence. The first real `StopFailure` was saved that way, with secrets\nscrubbed, at `fixtures/live/claude/limit-rate_limit.json`, and the claude docs\nrow flipped to observed-live (`node scripts/live-limits.mjs`). The same\ncapture is wired for codex `usage_limit_exceeded` and agy `RESOURCE_EXHAUSTED`;\nno payload for either has been kept yet.\n\nTerminals started by this version can change their ladder while they run:\nSettings holds the default for new terminals, and **Details** on a row edits\nthe copy that terminal is running. An older terminal stays on the order it\nstarted with; its row says a restart is needed and can save the desired\ndefault for the next launch. A normal agent exit ends the terminal. It does\nnot trigger a handoff.\n\nWith the [portable harness](https://github.com/ucsandman/LegCli/blob/main/docs/harness.md) enabled (`leg harness enable`,\noff by default), the hand-off also prepares the destination's environment\nbefore it starts: the source agent's global rules, identity, hooks, skills,\nsubagents, slash commands, MCP servers and permissions, rendered into the\ndestination's own files as far as it can represent them. The terminal then\nreads, for instance, `codex harness partial · 8/8 components, 3 dropped · 1 file(s) written`, and the terminal's details list what was dropped and why.\nThe source is fingerprinted so an unchanged environment costs a few stat\ncalls; a policy (`warn`, `sync`, `strict`) says whether a hand-off may write,\nand strict refuses a destination it cannot make safe. Credentials never move\nand which login runs stays the account layer's decision.\n\nTwo agents in one working tree write over each other's files. So when you\nstart `leg codex` in a checkout where `leg claude` is already live, the new\nsession gets its own git worktree, `<repo>/.leg-worktrees/<session-id>` on\nbranch `leg/<session-id>`, cut from the branch the checkout has out, and the\nterminal prints one line saying where it is. The agent starts there; the card,\nthe usage tracking and the handoff work the same. `--no-worktree` shares the\ncheckout on purpose (Leg takes the flag out; the agent never sees it).\n\nThe card of a session with its own worktree has a **Land** button. It sends\nthe branch through the merge queue: whatever the agent left uncommitted is\ncommitted on the branch, the branch is rebased onto its base, the repo's test\ncommand runs (`package.json` `test`, `pytest`, or none with a warning), and the\nbase is fast-forwarded, never merged. When a step fails nothing lands and the\ncard says why: `rebase-conflict` with the files, `tests-red` with the end of\nthe output, `dirty-trunk` when the checkout has local changes the landing\nwould overwrite. Local changes it would not touch are left alone. The\nlanded-on-trunk list says which terminal landed each commit. **Remove** safely\nprunes a finished session only when its worktree is clean and its branch is\nalready on the base. **Remove record** is a separate visible button with a\nconfirmation: it removes only Leg's saved session record and deliberately\nkeeps the worktree, branch, unmerged commits, and dirty files.\n\nVerified live on 2026-09-11 with three haiku sessions in a throwaway repo. The\nfirst stayed in the checkout; the second and third each got a worktree and\nappended a line to README.md. Land on the second, clicked on the real board,\nran the repo's tests and fast-forwarded main; Land on the third bounced with\n`rebase-conflict` on README.md and kept its commit on its branch; the\nlanded-on-trunk list named the second terminal (the screenshot above).\n\nOff until you run it. `leg share on` binds the board to your Tailscale\naddress (or `--bind lan`, or an address you name) and gives every human their\nown name and token; until then the board stays on `127.0.0.1` and there is no\ntoken at all.\n\n```\nleg share on              your own link, printed once\nleg share add sam         sam's link, printed once (a guest)\nleg share add dana --role operator    dana runs cards, not this machine\nleg share                 who is on the board (never a token again)\nleg share rotate sam      sam's old link stops working\nleg share off             back to 127.0.0.1; every link stops working\n```\n\nThere are three roles. **owner** is everything: the machine's settings, the\nharness, every terminal, the cards, the history index and the audit trail.\n**operator** is the pipeline board and their own terminals: they add, run,\napprove, reassign and kill cards, and they never see this machine's settings,\nits home path, its repository paths, its conversation index or the audit.\n**guest** is the terminals lane, read-only and redacted, with **Request\nhandoff** as their only button.\n\nA token is kept as a sha256 hash, so a lost link is re-issued, never re-read. The board takes the token out of the address bar and keeps it in the browser. Your own browser on this machine needs no token.\n\nWhat another human sees is the Terminals region, read-only. Each panel says\nwhose terminal it is. On a panel that is not theirs there is no prompt, no file\nname, no path, no bundle and no event log; what stays is the agent and session\ntail, the status word, the sentence `read-only: wes owns this terminal`,\nrepo@branch, the worktree line, the elapsed clock, and one button,\n**Request handoff**. The instrument head prints `not shared` in place of every\npercentage. A request lands on the owner's panel as `sam asked to take this terminal at 11:04 PM` with **Approve sam** and **Dismiss sam**. The background\nside of the board (cards, logs, the floor) stays the owner's alone. A terminal\nbelongs to the human who started it: `LEG_PERSON=sam leg claude` on the\nsame machine is sam's card, not yours.\n\nThe security pass that goes with it: every `/api` route needs a token, the\nevent stream included; twenty wrong tokens from one address and that address\nwaits a minute; one identity gets 600 requests a minute; a guest gets 403 on\neverything that is not theirs; and the tests send a bad and a missing token to\nevery route. Verified live on 2026-09-11: two terminals on one machine, one wes's and\none sam's; sam's board showed wes's card with the prompt hidden and only\n**Request handoff**, and sam's request reached wes's board (`~/.leg/board.log`:\n\"hand-off requested … by sam\").\n\nOff unless you hand it a certificate. Leg does not issue one: a self-signed pair teaches everyone on the board to click through a warning, which is worse than plaintext on a network that is already private. On Tailscale the pair is one command:\n\n```\ntailscale cert <machine>.<tailnet>.ts.net\nleg share on --tls-cert <machine>.<tailnet>.ts.net.crt --tls-key <machine>.<tailnet>.ts.net.key\n```\n\n`LEG_TLS_CERT` and `LEG_TLS_KEY` do the same without writing the paths into\n`share.json`, and they win over it. With a pair configured the shared address\nserves https and every printed link says `https://`; the companion listener on\n`127.0.0.1`, which exists so this machine's own browser needs no token, stays\nplain http, because the certificate is for the shared name and loopback traffic\nnever leaves the machine. A pair that is half-configured, missing, unreadable\nor empty stops the board with exit 3 rather than quietly serving plaintext.\nRenew the pair, then `leg down && leg up` to pick it up.\n\nSettings → **Audit trail** is one list across every terminal and every card,\nnewest first: hand-offs, landings, approvals, reassignments, kills, each with\nthe person or agent that did it, the repo and the time. Filter by person or by\nkind. It reads what the ledger already recorded, so nothing new is stored, and\nit prints how much it read beside the answer (`14 terminals and 3 cards, 812 events read`) so an empty trail cannot be mistaken for a quiet week. Owner\nonly: the trail names repositories and people.\n\n`leg digest` (default window 8 hours; `--since 2d`, `--since 30m`, or an ISO\ntime; `--json` for the record; `GET /api/digest?since=` on the board, owner\nonly) is the trail read the other way round: grouped by repository, what\nneeds you first. The first line is the volume it was read from (`3 terminals, 2 cards, 1 landing, 412 events read (66 sessions and 9 cards on disk)`), then `needs you`: a live terminal waiting on a question, a card\nparked for a human, a card that failed, a terminal that was lost, in that\norder and each with how long ago. Then one block per repository: every\nterminal that moved in the window with its login and model, state, turns,\nfiles, commits ahead and the events worth a line (a wall, a hand-off and\nwhether it kept the conversation, an all-out wait, the end), every card with\nits last event, every landing with who pressed Land, and last the walls\nstanding right now with their reset times. Nothing new is recorded; a window\nwith nothing in it says so with its counts.\n\n`leg <agent>` opens it; `leg open` reopens it; `leg down` stops it.\n\n- **Verdict and capacity strip** : the largest sentence on the page, then one\nstrip with a token per login carrying the binding bucket, a short track, the\npercentage and one state word or clock:`claude 63% 6:01 PM` ,`codex back Sat 10:11 PM` ,`agy no figure` ,`grok no reading` .**Capacity and models** opens a drawer holding the full login panels: the 5h and 7d rails, the reset\nand how long that is, where and when Leg read the number,`at the wall` with\nits`back <day time>` , and on the claude panel a rail of model chips\n(`fable 63%` ,`opus 12%` , a walled one saying`out until 9:14 PM` ). The\nsticky instrument head is still what`/floor` puts at the top.\n- **Terminals** : one full-width row per session, the ones that need an answer\nfirst. The register reads the status word,`repo on branch` ,`dirty <n>` and`ahead <n>` ,`agent/model` ,`quiet <n>m` , and`own worktree, from main` when\nthe session cut its own worktree; then the first prompt as a\nbutton, exactly one sentence (the highest-ranked thing true about the\nterminal), an`also:` disclosure naming the rest, the files as\ncomma-separated text, the binding bucket as`63% of the fable week` , and an\nelapsed clock with the session tail. Two live sessions in one repo touching\nthe same file print`codex (codex-99ab) is changing src/server.mjs in another checkout; whoever lands second rebases` on both rows. A Claude row\nparked at a permission or idle prompt rises to the top, says`waiting on you` in place of its status word, prints the question verbatim, and puts a\ncount in the browser tab (`(2) Leg` , with a dot on the favicon).\nAfter a Land the sentence is`landed on <base>, <7-char sha>, <n> files, +<added>/-<removed>` , or`Land was attempted at <time> onto <base> and bounced: <first line of the reason>. The branch still holds every commit; nothing was lost.`\n- **Landed on main** : one flat list across every repo the board can see, newest\nfirst, each row with the short sha, the subject, a`repo@branch` chip, and\nwhen plus who. A commit a Land put there says`landed by <agent> (<id tail>)` .\n- **Buttons** , in a fixed order that never reflows: Land, Hand off now,\nDetails, End, under the clock at the right of the row. Land is drawn only\nwhen it can run; its reason is said once above the panel. Once a session has\nended, Remove and Remove record take End's place. Details opens an expansion\nin flow under the panel.**Hand off now** takes the first open rung of the ladder. To name the\ndestination instead, open Details and use**Hand off now to** , which lists\nevery rung with its model, whether it keeps the conversation, and the reason\na greyed one cannot be picked (`at its usage limit, back 11:40` ,`shares the window that is out, buys nothing` ,`not installed on this machine` ).`leg sessions handoff <id> --to claude/default/opus` is the same choice from\na terminal. If the rung you picked walls between the click and the hand-off,\nthe work still continues down the ladder and the terminal says which one\ntook it instead.\n- Below it, **Conversations** : every coding-agent conversation on this machine\nas a count that opens, the ones Leg started and the ones Claude Code, Codex,\nGrok, Antigravity and Copilot keep in their own stores. Filter by agent,\nsearch, repository, or only what Leg started; a row opens its last messages\nin place, with the`leg history continue` command to copy where the agent\ncan resume by id. The same drawer lists every checkout Leg can see. Then**Settings** . Background tasks are not down here: a live card is a row in\nthe**Background** panel directly under Terminals (see below), and only the\nfinished ones collapse into a ledger line beside these counts.\n\nThe board reads `~/.leg/sessions/*/session.json` over server-sent events; a\nsession whose runner process is gone is marked `lost`, never shown as live.\n\nClaude Code, Codex, Grok, Antigravity and Copilot each keep their history in\ntheir own place. `leg history` is one list over all of them, whether or not\nLeg started the conversation, and `leg worktrees` is one list over every\ncheckout: git's, Leg's own, and the ones those conversations ran in.\n\n```\nleg history                          newest first, every agent; --provider, --repo, --search, --json\nleg history show claude:0fc5         where it ran, its last messages, whether Leg can continue it\nleg history continue claude:0fc5     leg claude --resume <id> in that folder, supervised like any session\nleg worktrees                        path, repo, branch, exists, uncommitted, owner, conversations, stale\n```\n\nNothing moves: each agent's store stays where it was, Leg writes only its own\n`~/.leg/history/index.json`, reads transcripts from their head and tail, and\nopens messages only when you open a conversation. A session Leg started and\nthe same conversation in the agent's store are one row, marked `leg`; the rest\nare `external`. On a shared board the whole group is the owner's. The support\nmatrix (which agents list, show messages, continue) and every file read are in\n[docs/history.md](https://github.com/ucsandman/LegCli/blob/main/docs/history.md).\n\nOptional. `leg accounts add claude work` creates\n`~/.leg/accounts/claude/work`, junctions your `hooks`, `skills`, `agents`,\n`commands`, `plugins`, `rules`, `scripts`, `output-styles`, `tools` and\n`projects` into it, copies `settings.json`, `CLAUDE.md` and the status-line\nscripts (refreshed from your real `~/.claude` before every launch), and prints\none line to paste:\n\n```\n$env:CLAUDE_CONFIG_DIR='C:\\Users\\you\\.leg\\accounts\\claude\\work'; claude auth login\n```\n\n`projects` is Claude Code's conversation store, so the second login sees the\nsame conversations and the same auto-memory as the first, and a hand-off from\none login to the other keeps the conversation: the terminal moves to `work`\nwith `claude --resume <id>` and no bundle prompt. That is the whole point of a\nsecond 20x login when the Fable or the weekly window on the first one is out.\nAn account made by an older Leg gets the junction the next time it starts.\nClaude Code writes that directory; Leg only reads it.\n\nSame for codex (`CODEX_HOME`; `config.toml`, `AGENTS.md`, `skills`, `prompts`,\n`rules`, `plugins`, `agents`, `hooks`, `memories` shared; a codex hand-off\nstill takes the bundle, because `codex resume` under a second `CODEX_HOME`\nhas not been observed). agy 1.2.0 has no config-directory override, so it\nstays one account. Only the login lives in the account directory;\n`leg accounts rm` removes the junctions and the directory and never touches\nyour real home.\n\nThe terms, as published (effective dates below):\n\n- Anthropic Consumer Terms (effective 2025-10-08): \"You may not share your Account login information, Anthropic API key, or Account credentials with anyone else\" and you \"must not … bypass any of our systems or protective measures.\"\n- Anthropic Usage Policy (effective 2025-09-15): do not \"Coordinate malicious activity across multiple accounts to avoid detection or circumvent product guardrails\" or \"Utilize automation in account creation.\"\n- OpenAI Terms of Use (effective 2026-01-01): \"You may not share your account credentials or make your account available to anyone else\" and you may not \"circumvent any rate limits or restrictions or bypass any protective measures.\"\n\nOwning two paid subscriptions is not named as prohibited by either. Rotating to\na second account of the same vendor because the first one is rate-limited sits\nclose to OpenAI's \"circumvent any rate limits\" wording and Anthropic's\n\"circumvent product guardrails\". Leg's default chain switches vendors\n(claude → codex → agy), which is plainly fine. Same-vendor rotation only\nhappens after you run `leg accounts add`; that is your call.\n\n- **Never edited** :`~/.claude/settings.json` ,`~/.claude.json` ,`~/.codex/config.toml` , agy's files, your repo's settings. Claude Code gets\nhooks through a per-session`--settings` file under`~/.leg` ; codex and\nagy get nothing injected.\n- **Read, never written** : each agent's own history (`~/.claude/projects` ,`~/.codex/sessions` ,`~/.grok/sessions` ,`~/.gemini/antigravity-cli` ,`~/.copilot/session-state` ) for`leg history` ; the index it builds lives\nunder`~/.leg/history/` , and no SQLite file is ever opened.\n- **Written only after `leg harness enable`** ([the portable harness](https://github.com/ucsandman/LegCli/blob/main/docs/harness.md) ,\noff by default): the destination client's global rules file\n(`~/.codex/AGENTS.md` ,`~/.gemini/GEMINI.md` ,`~/.claude/leg-rules.md` plus\none`@` line in`CLAUDE.md` ), marked`leg harness` regions inside the\nclient's own config files, per-skill directory links, and one file per\nsubagent and slash command. Every file carries`GENERATED by Leg harness` ,\nevery overwrite is backed up under`~/.leg/harness/backups` , a hand-edited\nfile is skipped and named, the source client is never written, and no\ncredential ever moves (an MCP key becomes`${NAME}` ).`leg harness disable` stops it and removes nothing.\n- **Written in your repo** :`.leg/` (session notes,`RESUME.md` and one`RESUME-<session-id>.md` per hand-off),`.context-handoffs/` (the bundles) and`.leg-worktrees/` (a second\nsession's worktree), all added to`.git/info/exclude` , plus the`leg/<session-id>` branch of a session with its own worktree. Landing\nfast-forwards your branch; nothing is ever pushed.\n- **Stripped from every agent's environment** :`ANTHROPIC_API_KEY` ,`ANTHROPIC_AUTH_TOKEN` ,`ANTHROPIC_BASE_URL` ,`ANTHROPIC_CUSTOM_HEADERS` ,`OPENAI_API_KEY` ,`OPENAI_BASE_URL` ,`OPENAI_API_BASE` ,`GEMINI_API_KEY` ,`GOOGLE_API_KEY` ,`GOOGLE_GEMINI_BASE_URL` ,`GOOGLE_GENAI_USE_VERTEXAI` ,`GOOGLE_GENAI_USE_ENTERPRISE` ,`GOOGLE_CLOUD_PROJECT` ,`GOOGLE_CLOUD_LOCATION` ,`GOOGLE_APPLICATION_CREDENTIALS` ,`CLAUDECODE` ,`CLAUDE_CODE_*` ,`CLAUDE_EFFORT` , and`CLAUDE_PLUGIN_DATA` .`CLAUDE_CODE_*` does not include`CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS` : Leg sets that one\nto`0` for a detached Claude print session.\n- **Read but never written or printed** : Claude Code's stored login, sent only\nto`api.anthropic.com` for the usage numbers. The ledger scrubs bearer tokens\nand key shapes from every line regardless.\n- **`leg uninstall --yes`** : removes`~/.leg` (sessions, usage, extra\naccount directories with their junctions, v0.1 cards, the board pidfile) and\nnothing else; then`npm rm -g @ucsandman/legcli` .\n\n```\nleg claude|codex|agy|grok [agent args…] the interactive agent, board alongside, handoff on limit\n      [--no-worktree]                  share the checkout with a live session instead of a worktree\nleg sessions ls [--json]             every session and its usage\nleg sessions show|events <id>\nleg sessions handoff|end <id>        same as the board buttons\nleg sessions handoff <id> --to <agent>[/<account>]   hand off to a destination you name\nleg sessions rm <id>                 forget an ended session\nleg sessions simulate-limit <id>     the real limit path without a real wall (claude, agy, grok)\nleg history [ls] [--provider p] [--repo r] [--search q] [--managed|--external] [--live] [--all] [--json]\n                                     every conversation on this machine, Leg's own and the agents' own (read only)\nleg history show <id> [--messages n] [--json] | continue <id> [agent args…] | refresh [--full] | providers\nleg worktrees [--repo <path>] [--no-dirty] [--json]   every checkout: git's, Leg's, the conversations' (read only)\nleg digest [--since 8h|2d|<iso>] [--json]   what happened while you were away: needs-you first, then every\n                                     terminal, card, landing and wall in the window, by repository (read only)\nleg accounts ls                      logins and their 5h/7d usage\nleg accounts add <claude|codex|grok> <name> | rm <agent> <name> | terms\nleg harness status|inspect|check|explain|history [--json]   the portable harness, read-only\nleg harness enable [--source claude|codex] [--policy warn|sync|strict] [--yes]\nleg harness sync [--to codex,agy] [--force] [--dry-run] | diff <client> | doctor\nleg harness capture [claude|codex] | source <client> | policy <mode> | disable\nleg license                          the license on this machine, or where to buy one\nleg license activate <key> | deactivate | refresh   (refresh renews a Team key)\nleg adapter list|show <n>|check <n>  every adapter, built-in and custom\nleg adapter template [--name n]      a starter spec to fill in\nleg adapter add <file.json> | rm <n> any CLI as a card agent, from JSON\nleg share                            who is on the board (off by default; Team plan)\nleg share on [--bind tailscale|lan|<addr>] [--port N] [--tls-cert <f> --tls-key <f>] | off\nleg share add|rotate|rm <name> [--role owner|operator|guest]   one link per human, printed once\nleg open | down | status             the board\nleg uninstall [--yes]\n```\n\nEnvironment, all optional: `LEG_HOME` (default `~/.leg`), `LEG_PORT`\n(4747), `LEG_ACCOUNT` (start on a named login), `LEG_WARN_PCT` (85),\n`LEG_NO_HANDOFF=1` (warn and record, never switch), `LEG_NO_OPEN=1` (do not\nopen the browser), `LEG_USAGE_POLL_MS` (60000), `LEG_CLAUDE_ARGS` /\n`LEG_CODEX_ARGS` / `LEG_AGY_ARGS` / `LEG_GROK_ARGS` (extra args for a leg Leg starts after\na hand-off, e.g. `-m gpt-5.3-codex-spark`), `LEG_CLAUDE_BIN`,\n`LEG_CODEX_BIN`, `LEG_AGY_BIN`, `LEG_GROK_BIN`, `LEG_CHB_BIN`, `LEG_PERSON` (whose\nterminal this is when the board is shared), `LEG_TLS_CERT` / `LEG_TLS_KEY`\n(serve the shared board over https; they win over `share.json`),\n`LEG_RATE_MAX` (600 requests a minute per human) and `LEG_RATE_MAX_FAILURES`\n(20 wrong tokens per address).\n\nVersion 0.1 was the other way round: you dropped a task card on the board and\nLeg ran the agents headless in a git worktree, one per card, with a fallback\nchain, path leases, a scheduler and a merge queue. All of that still runs, and\na card is now a terminal you are not sitting at: same register, same one\nsentence, same ladder, same bundle. Live cards are rows in the **Background**\npanel directly under Terminals; finished ones fall into one ledger line that\nopens.\n\nStarting one is a single field. `Run in the background:` takes the task, and\nthe sentence under it is inferred with its nouns as buttons: the repo of the\nterminal you were last in, the saved ladder, and the `build` workflow, which\nstops with its changes in the card's worktree and does not merge them.\n**More settings** is the full form, with the **Build, test, and merge** and\n**Factory** workflows that include an automatic land station, plus fallback\nagents, permissions, approval gates, turn caps, leases, trunk, merge method,\ntests, title, and scripted test and demo adapters. **End, and keep going as a\ncard** on a terminal's End row makes a card out of the work in front of you;\n**Take over** on a card gives you the command that turns it back into a\nterminal.\n\n- `leg up` boots the board with the scheduler and merge queue and streams\nredacted logs;`leg card add --repo <path> --task \"<t>\" --chain claude,codex --queue` creates a card; presets`build` ,`build-land` ,`factory` ; station kinds\nagent, test, land, human.\n- Adapters spawn the CLIs headless as argv, never through a shell, with their\nown permission modes and never a bypass flag: `claude -p --output-format json --permission-mode <m>` ,`codex exec --json -s <m> -C <worktree>` ,`agy -p --output-format json --mode <m> --add-dir <worktree>` ,`grok --prompt-file <f> --output-format json --permission-mode <m> --cwd <worktree>` ;`fake` ,`fake-claude` ,`fake-codex` ,`fake-agy` for tests and\ndemos.\n- **Any other CLI** is a card adapter too, from a JSON spec and no code:`leg adapter template --name muse > muse.json` , fill in the command and its\nflags,`leg adapter add muse.json` , then`--chain muse,claude` .`leg adapter check muse` prints the exact command line a leg would run before one does.\nSee[docs/adapters.md](https://github.com/ucsandman/LegCli/blob/main/docs/adapters.md#custom-adapters) . A custom adapter\nruns cards; it is not an interactive`leg <agent>` terminal, because that\nneeds a usage tap and a wall signal, which only the four above expose.\n- A leg that ends on a limit signal, a stall, a crash or exit 0 without\n`.leg/DONE` hands off with a bundle to the next adapter in the same\nworktree; a`land` station rebases, tests and fast-forwards trunk or bounces\nthe card with the failure in the bundle.\n- Optional mirrors, off unless set in `.env` : OpenClaw Workboard\n(`LEG_SYNC_WORKBOARD=1` ) and DashClaw (`LEG_SYNC_DASHCLAW=1` ).\n\nThe full v0.1 story, with the fake-limit demo and the real claude→codex run,\nis in [docs/concepts.md](https://github.com/ucsandman/LegCli/blob/main/docs/concepts.md), [docs/DEMO.md](https://github.com/ucsandman/LegCli/blob/main/docs/DEMO.md),\n[docs/real-run.md](https://github.com/ucsandman/LegCli/blob/main/docs/real-run.md) and [docs/board-guide.md](https://github.com/ucsandman/LegCli/blob/main/docs/board-guide.md).\n\nLeg binds `127.0.0.1`. `leg share on` is the supported way to listen\nanywhere else: it binds your Tailscale or LAN address and every human gets\ntheir own token (see [More than one human](#more-than-one-human)). Without\nshare, setting `LEG_BIND` to a non-loopback address needs `LEG_TOKEN` too,\nor the server refuses to start (exit 3), and requests then need\n`Authorization: Bearer <token>`. Tokenless owner access also requires a\nloopback hostname (`127.0.0.1`, `localhost`, or `[::1]`), which prevents a\nDNS-rebound hostname from inheriting local access. Either way there is no TLS.\n\n- **The board did not open** :`leg open` , or visit[http://127.0.0.1:4747](http://127.0.0.1:4747) .`~/.leg/board.log` has the server's output.\n- **claude's card shows \"usage unknown\"** : Claude Code has no stored claude.ai\nlogin in that config directory (run`claude auth login` ), the stored token\nexpired (start`claude` once, it refreshes), or the usage endpoint answered\nwith something Leg does not recognise. The card says which. The wall is\nstill caught through the hook; only the percentages are missing.\n- **codex usage is unavailable or stale** : the read-only Codex app-server quota\nrequest failed or has not completed in the last five minutes. The board\nretries every minute; an active Codex session also keeps its rollout tap as a\nfallback for percentages and the wall signal.\n- **agy's card has no percentage** : expected, agy exposes none. Leg sees the\nwall when agy hits it.\n- **A session shows `lost`** : the terminal that ran` leg <agent>` is gone\n(closed, crashed, machine slept through a kill). Remove it from the board.\n- **Nested session** :`leg claude` typed inside a Claude Code shell works;\nthe parent's`CLAUDECODE` markers are stripped so the child starts.\n- **`npm install` dies with `edgesOut`** (clone only): the global npm is older\nthan Node; run`npx --yes npm@latest install` once.\n\nMore in [docs/faq.md](https://github.com/ucsandman/LegCli/blob/main/docs/faq.md).\n\n| guide | read it when | \n|---|---|\n| [Getting started](https://github.com/ucsandman/LegCli/blob/main/docs/getting-started.md) | you want `leg claude` running in five minutes | \n| [Concepts](https://github.com/ucsandman/LegCli/blob/main/docs/concepts.md) | sessions, accounts, bundles, and the v0.1 cards, stations, chains and leases | \n| [Board guide](https://github.com/ucsandman/LegCli/blob/main/docs/board-guide.md) | every word, number and button on the board explained | \n| [Configuration](https://github.com/ucsandman/LegCli/blob/main/docs/configuration.md) | environment variables and options | \n| [Portable harness](https://github.com/ucsandman/LegCli/blob/main/docs/harness.md) | carrying rules, hooks, skills, agents, commands and MCP servers to the agent a hand-off lands on: what moves, what does not, policies, ownership, secrets | \n| [History](https://github.com/ucsandman/LegCli/blob/main/docs/history.md) | `leg history` and`leg worktrees` : every conversation and checkout on this machine across agents, the support matrix, what is read and what is written | \n| [Adapters](https://github.com/ucsandman/LegCli/blob/main/docs/adapters.md) | what each CLI exposes and how Leg attaches to it | \n| [CLI contracts](https://github.com/ucsandman/LegCli/blob/main/docs/cli-contracts.md) | exact argv per CLI and the limit-signal table with sources | \n| [FAQ](https://github.com/ucsandman/LegCli/blob/main/docs/faq.md) | a question the others did not answer | \n| [Demo](https://github.com/ucsandman/LegCli/blob/main/docs/DEMO.md) and[real run](https://github.com/ucsandman/LegCli/blob/main/docs/real-run.md) | the v0.1 handoff, fake and real | \n| [Vocabulary](https://github.com/ucsandman/LegCli/blob/main/docs/VOCABULARY.md) | statuses, outcomes and event types | \n| [Roadmap v2](https://github.com/ucsandman/LegCli/blob/main/docs/ROADMAP-v2.md) | where this is going | \n| [Reuse](https://github.com/ucsandman/LegCli/blob/main/docs/REUSE.md) and[deviations](https://github.com/ucsandman/LegCli/blob/main/docs/DEVIATIONS.md) | what was ported and every place the plan changed | \n| [Website](https://github.com/ucsandman/LegCli/blob/main/site) | the public page: static HTML in `site/` , preview with`python -m http.server 4780 --directory site` , deployed to Vercel from that directory; PRODUCT.md and DESIGN.md at the root carry its brief and tokens | \n\nIssues and pull requests are welcome. Read [CONTRIBUTING.md](https://github.com/ucsandman/LegCli/blob/main/CONTRIBUTING.md)\nfor the dev setup and the rules (zero runtime deps, argv spawns only, no\nbypass flags, a privacy check on every commit). Security reports go through\n[SECURITY.md](https://github.com/ucsandman/LegCli/blob/main/SECURITY.md).\n\n```\nnpm install\nnpm test          # node --test + privacy check\nnpm run lint\n```\n\nAny real agent session started only to test Leg runs on the cheapest model\n(`leg claude --model haiku`); the live checks in `test/` never start one.\n\nMaintainer releases use npm trusted publishing with no `NPM_TOKEN`. Bump the\npackage, lockfile, site metadata and release notes, then push `main`.\n`npm version` (and `npm run sync-alias`) writes `packages/leg-agents` to the\nsame version and pins `@ucsandman/legcli` to it; `npm test` fails if they\ndrift. CI waits for the Ubuntu and Windows test matrix, validates both\n`@ucsandman/legcli` and `leg-agents` against npm, and publishes each only\nwhen that version is missing and newer than the stable `latest`. Existing\nversions skip cleanly; older, prerelease, lockstep, and registry-error cases\nfail the job. The npm trusted publisher is bound to `ucsandman/legcli` and\n`.github/workflows/ci.yml` (bind `leg-agents` the same way). Publication\nuses `--provenance=false`.\n\nParts of the runner, ledger and git snapshot were ported from a private\nrepository that was MIT licensed (see [NOTICE](https://github.com/ucsandman/LegCli/blob/main/NOTICE) and\n[docs/REUSE.md](https://github.com/ucsandman/LegCli/blob/main/docs/REUSE.md)), with chat identifiers, machine paths and\npersonal names removed. The test suite runs a privacy check on every commit,\nand the fixtures store home paths as `~`.\n\nLeg is commercial software under the [Leg License Agreement](https://github.com/ucsandman/LegCli/blob/main/LICENSE).\nIt ships as readable JavaScript so you can see what it does on your machine,\nand you may modify it for your own use, but not redistribute it or work\naround the license check. Versions 0.2.0 and 0.3.0 were published under MIT\nand remain available. The version in this source tree is 0.15.0; see\n[npm](https://www.npmjs.com/package/legcli) for published versions and\n[CHANGELOG.md](https://github.com/ucsandman/LegCli/blob/main/CHANGELOG.md) for release notes.\n\nThe first 14 days are a **free trial**: install it, type `leg claude`, and\neverything in both plans is on, with no key and no card. The clock starts on\nyour first session and `leg license status` shows what is left. After that\nit needs a license: **Personal, $79 once**, one human on any number of\nmachines, every release for 12 months and the version you have keeps working\nafter that; **Team, $12 per seat per month**, Personal plus `leg share` for\nmore than one human on the board. Buy at the site, then\n`leg license activate <key>`. There is also a **30-day money-back\nguarantee** after buying, no reason required. A key is a signed token\nchecked offline with the public key in `src/license.mjs`; only a Team key\nrenewal talks to the site. The bare agent CLIs are never affected by any of\nthis; only what Leg adds is licensed.", "url": "https://wpnews.pro/news/show-hn-claude-code-hits-its-5-hour-limit-codex-picks-up-in-the-same-terminal", "canonical_source": "https://github.com/ucsandman/legcli", "published_at": "2026-09-19 15:00:43+00:00", "updated_at": "2026-09-19 15:24:46.421732+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "developer-tools", "ai-products"], "entities": ["LegCli", "Leg", "Claude Code", "Codex", "agy", "Grok", "context-handoff-bundle", "PyPI"], "alternates": {"html": "https://wpnews.pro/news/show-hn-claude-code-hits-its-5-hour-limit-codex-picks-up-in-the-same-terminal", "markdown": "https://wpnews.pro/news/show-hn-claude-code-hits-its-5-hour-limit-codex-picks-up-in-the-same-terminal.md", "text": "https://wpnews.pro/news/show-hn-claude-code-hits-its-5-hour-limit-codex-picks-up-in-the-same-terminal.txt", "jsonld": "https://wpnews.pro/news/show-hn-claude-code-hits-its-5-hour-limit-codex-picks-up-in-the-same-terminal.jsonld"}}