LegCli

FAQ

Real questions, short answers, sourced from the code and the other docs in this directory.

Why subscription CLIs only, never a per-token API? Leg spawns each CLI's own logged-in session (claude, codex, agy). Every child process has 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 stripped out (src/env.mjs), then gets CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS=0 for a detached Claude print session. A stray API key in your shell cannot silently take over billing or shadow the subscription login. If stderr ever says "another auth source is set", that leg is classified auth_failed, a failed launch, and does not count as a usage limit.

Why does Leg poll an endpoint for claude's usage instead of reading the status line? Because Claude Code 2.1.268 did not run a custom status line from a settings file Leg controls when this was tried on 2026-09-11 (recorded in DEVIATIONS.md); hooks from the same --settings file did fire. So the numbers come from GET api.anthropic.com/api/oauth/usage with the login Claude Code already stored, which is the same data /usage shows. Leg still writes the statusLine entry, so the endpoint poll becomes a fallback the moment a build honours it, and your own status-line command is chained first either way. See adapters.md and src/taps/claude-usage.mjs.

Why does codex get no hook when claude does? Because injecting one would put a prompt in your way. codex asks you to review new hooks before it runs them, so a hook per Leg session would mean a review prompt per Leg session. It is not needed: an interactive codex writes the whole thread to a rollout file under ~/.codex/sessions/YYYY/MM/DD/, and flushes it per event. Leg finds the rollout whose session_meta cwd is the session's directory and tails it for the rate limits, the prompts and the edited files.

Why does agy show no percentage? Because agy exposes none. agy 1.2.0 is a closed Go binary; its own status line fetches a quota summary from the backend and writes it nowhere on disk. The board shows "no % from agy" rather than an empty bar. The wall itself is still caught: Leg passes --log-file per session and watches for RESOURCE_EXHAUSTED, "it resets in …" and "out of quota". Those strings are present in agy.exe. A real RESOURCE_EXHAUSTED wall was caught live on 2026-09-11 and handed the session off; the fixture in fixtures/limits/agy/ is still tagged docs-only in cli-contracts.md because the payload itself was never captured to fixtures/live/agy/.

Am I allowed to add a second account? That is your call, and the terms are quoted in full in the README under "Second accounts, and what the terms say" (leg accounts terms prints the same summary). The short version: owning two paid subscriptions is not named as prohibited by Anthropic or OpenAI, but rotating to a second account of the same vendor because the first is rate-limited sits close to OpenAI's "circumvent any rate limits" wording and Anthropic's "circumvent product guardrails". Leg's default chain switches vendors (claude, codex, agy), which is plainly fine. Same-vendor rotation only happens after you run leg accounts add.

What does leg uninstall remove? ~/.leg and nothing else: sessions, usage files, the extra account directories with their junctions, the v0.1 cards and runs, and the board pidfile. It removes the junctions as links, never following them into your real ~/.claude or ~/.codex. It does not touch any file of yours, any repo, or the agent CLIs themselves. Run leg uninstall with no flag to print what would go, --yes to do it; then npm rm -g legcli if you want the package gone too.

Can I run leg claude inside a Claude Code shell? Yes. A parent Claude Code session exports CLAUDECODE and CLAUDE_CODE_* markers that make a nested Claude refuse to start; sanitizeEnv (src/env.mjs) strips them along with the API-key variables, so the child starts normally. It becomes its own session with its own card on the board, unrelated to the parent's.

Why are --dangerously-skip-permissions and similar flags never available? Each adapter's argv() calls assertAllowed() (src/adapters/common.mjs), which checks the requested mode against that adapter's allowed list and rejects any flag on its forbiddenFlags list, before anything spawns. Requesting bypassPermissions, --yolo, --full-auto, danger-full-access, or similar throws immediately; nothing ever runs with permission checks off.

What actually happens when a pipeline leg hits a usage limit? (For an interactive session, see concepts.md.) src/limits.mjs classifies the leg's exit code, output and diff evidence as limit. Leg writes a handoff bundle in the same worktree (task, done so far, the diff, open findings) via context-handoff-bundle, then starts the next adapter in that station's chain from the bundle's resume text. If the chain has no next adapter, the card fails. See concepts.md.

Can two agents edit the same files at the same time? Not inside one card: a station runs one chain leg at a time. Across cards, the scheduler only starts a queued card when its declared leases (path globs) do not overlap any running card's leases (src/leases.mjs); the default lease is ** (the whole repo), so two cards with no leases set always serialize. The overlap check is a deliberate approximation biased toward false positives: an unnecessary serialization costs minutes, a wrongly parallel card can corrupt a merge.

For terminals: a second leg <agent> in a checkout that already has a live session gets its own worktree and branch, so the two never write over each other's files. Whoever presses Land first fast-forwards trunk; the second one rebases onto it, or bounces with the conflicting files named on the card. --no-worktree shares the checkout when that is what you want.

Can someone else watch my board? Only if you run leg share on, which is off by default. It binds your Tailscale or LAN address and prints one link per human (leg share add <name>), each with its own token. A guest sees the terminals lane read-only and nothing a terminal has said, read or written: no prompt, no file names, no paths, no bundle, no events, no logs, and none of the pipeline side. The limit line keeps only the reason and the reset time, never the raw limit text. The one thing they can do on your terminal is ask for a hand-off, which you approve or dismiss on the card. leg share off ends it and every link stops working. There is no TLS, so keep it on Tailscale or a network you trust.

What if the CLI I want isn't installed? node bin/leg.mjs up --dry prints a preflight table with ok/missing per adapter. A missing adapter only matters if a card's chain names it: that leg fails to launch (launch_failed), which does not advance the chain, install and log in, then press Rerun. A chain only needs the adapters it names; leave a missing one out.

How do I resume after a reboot or a crash? Every run's run.json under $LEG_HOME/cards/<id>/runs/<n>/ is the source of truth, not process memory. leg down kills active agents but each run's supervisor still writes its final verdict; the next leg up (or the scheduler) finds any unsettled run with no live driver and re-attaches to apply that verdict, logging a re-attached to run N event. Nothing manual to do beyond starting Leg again.

Does Leg push to GitHub, or open pull requests? No. The default land_mode: ff only rebases and fast-forwards the local trunk branch inside the repo you gave it; there is no remote write anywhere in src/worktree.mjs or src/mergequeue.mjs. land_mode: pr builds a real gh pr create argv but is stub-only: it refuses to run unless LEG_GH_BIN points at a real gh (or a test stub), and even then it never pushes or creates a remote for you, that is out of scope for this build.

Is there a hosted version of Leg? No. Leg is local-first: the board binds 127.0.0.1 by default, every card's state lives in files under LEG_HOME, and there is no service to sign into. See configuration.md for what changes if you deliberately bind it to a shared address.

How are secrets handled? src/redact.mjs holds one pattern list (API keys, bearer tokens, GitHub and AWS tokens, key=value secrets); scrub() rewrites matches to [REDACTED] in every log line the launcher prints and in handoff bundle text, and the ledger's own writer refuses to record an event whose summary or body matches a secret pattern at all. Every adapter's env() strips the API-key/base-URL variables from the child process. npm test and the pre-commit hook both run scripts/privacy-check.mjs, which additionally scans the whole tree for a short list of strings specific to the private codebase Leg's runner/ledger were ported from.

What happens if I close the terminal instead of Ctrl-C? The board server and any running agents keep running as detached processes. Run node bin/leg.mjs down (or npm run stop) from another terminal to stop them cleanly, or just start leg up again later: it re-attaches to any run left in progress rather than launching a duplicate.

Can I run a card without the board? Yes: node bin/leg.mjs card run <card-id> drives one card through the orchestrator directly and exits when it reaches a waiting or terminal state, printing the final status. node bin/leg.mjs card show <id> and card events <id> work without the server running too, since they read the same on-disk ledger the board reads.

What does LEG_NO_SCHEDULER=1 do, and why would I set it? It boots the board server without its embedded scheduler, so no queued card starts automatically, useful when you want to drive every card by hand with card run (for example, inside a test) while still watching it on the board.

Is there a limit to how long a chain can be, or how many legs a card can take? No fixed limit; the chain array can be as long as you like. A card fails only when a leg's outcome needs to hand off and the chain has no next entry left, or (separately) after LEG_MAX_LAND_ATTEMPTS test/land bounces.

Windows vs macOS/Linux: what's actually verified? Built and tested on Windows (every worktree, git and taskkill path in the source has Windows-specific handling: MSYS_NO_PATHCONV=1, taskkill /T /F, native .exe resolution). CI (.github/workflows/ci.yml) runs the full test suite on both ubuntu-latest and windows-latest on every push and pull request.

How do I report a bug? Open a GitHub issue using the bug report template (.github/ISSUE_TEMPLATE/bug_report.md). For a security vulnerability, do not open a public issue, see SECURITY.md instead.

Where do I find the exact word Leg uses for a given status, event, or button? VOCABULARY.md: one table per category (statuses, outcomes, station kinds, event types, actor types, human actions, bounce reasons), pulled straight from the source identifiers.


Leg is commercial, source-available software by Wes Sander. The source you run ships in the npm package. Questions or a refund: legcli@practicalsystems.io.