# CLI reference

> pi binary flags and argument parsing, auth subcommands, experimental auth entry points, and package bin wiring.

- Repository: earendil-works/pi
- GitHub: https://github.com/earendil-works/pi
- Human docs: https://grok-wiki.com/public/docs/earendil-works-pi-7860a70e44d1
- Complete Markdown: https://grok-wiki.com/public/docs/earendil-works-pi-7860a70e44d1/llms-full.txt

## Source Files

- `packages/coding-agent/src/cli/args.ts`
- `packages/coding-agent/test/args.test.ts`
- `packages/coding-agent/src/cli/auth-command.ts`
- `packages/coding-agent/src/cli/experimental/auth.ts`
- `packages/coding-agent/package.json`
- `packages/coding-agent/README.md`

---

---
title: "CLI reference"
description: "pi binary flags and argument parsing, auth subcommands, experimental auth entry points, and package bin wiring."
---

The `@earendil-works/pi-coding-agent` package installs the `pi` binary at `dist/cli.js`. That entry sets `process.title`, marks `PI_CODING_AGENT=true` / `AI_AGENT=pi`, configures the HTTP dispatcher, then hands `process.argv.slice(2)` to `main()` in `src/main.ts`. `main()` dispatches auth, package, and config subcommands first; everything else goes through `parseArgs()` into interactive, print/JSON, or RPC run modes.

## Package bin wiring

| Surface | Value |
|---------|--------|
| npm package | `@earendil-works/pi-coding-agent` |
| Binary name | `pi` |
| Bin target | `dist/cli.js` |
| Node entry source | `src/cli.ts` (`#!/usr/bin/env node`) |
| Bun compile entry | `src/bun/cli.ts` → compiled `dist/pi` via `build:binary` |
| Config dir key | `package.json` → `piConfig.configDir` = `.pi` |
| Engines | Node `>=22.19.0` |

```json
// package.json (bin + config)
{
  "bin": { "pi": "dist/cli.js" },
  "piConfig": { "configDir": ".pi" }
}
```

Build makes the CLI executable: `chmod +x dist/cli.js dist/rpc-entry.js`. Related public exports (`main`, `./rpc-entry`, `./client`) are documented on [Package exports](/package-exports).

### App identity derived at runtime

| Constant | Default |
|----------|---------|
| `APP_NAME` | `pi` (overridable via package rename / piConfig) |
| Config directory | `~/.pi/agent` |
| Agent dir env | `PI_CODING_AGENT_DIR` |
| Session dir env | `PI_CODING_AGENT_SESSION_DIR` |

## Invocation shape

```bash
pi [options] [@files...] [messages...]
pi <subcommand> [subcommand-options]
```

### Dispatch order

`main(args)` handles requests in this order:

1. **Offline bootstrap** — if `--offline` is present or `PI_OFFLINE` is truthy (`1` / `true` / `yes`), set `PI_OFFLINE=1` and `PI_SKIP_VERSION_CHECK=1`.
2. **`pi auth …`** — early exit via `runAuthCommand`.
3. **Package commands** — `install` / `remove` / `uninstall` / `update` / `list`.
4. **`pi config`** — resource enable/disable TUI.
5. **`parseArgs(args)`** — general options and messages.
6. **One-shot exits** — `--version`, `--export`, then runtime setup; `--help` and `--list-models` run after extensions load so help can list extension flags.

Unknown short options (single `-` that is not a known shorthand) are hard errors. Unknown long options are collected as extension flags (`unknownFlags`).

## Subcommands

### Package management

| Command | Usage | Notes |
|---------|-------|-------|
| `install` | `pi install <source> [-l] [--approve\|--no-approve]` | Sources: `npm:…`, `git:…`, HTTPS/SSH git URLs, local paths |
| `remove` | `pi remove <source> [-l] …` | Alias: `uninstall` |
| `update` | `pi update [source\|self\|pi] [--self\|--extensions\|--models\|--all] [--extension <source>] [--force] …` | Default with no target: self-update pi only |
| `list` | `pi list [--approve\|--no-approve]` | Lists packages from user and project settings |
| `config` | `pi config [-l] [--approve\|--no-approve]` | TUI to enable/disable package resources; Tab switches global vs project |

`-l` / `--local` writes project settings (`.pi/settings.json`) for install/remove/config. Trust overrides: `-a` / `--approve`, `-na` / `--no-approve`.

### Auth subcommands

Handled before general argument parsing. Require at least one of `--provider` or `--model`. Only those two options are accepted with the auth verb (plus the flags listed below).

| Command | Usage |
|---------|-------|
| Print API key | `pi auth print-api-key [--provider <provider>] [--model <model>]` |
| Print bearer token | `pi auth print-bearer-token [--provider <provider>] [--model <model>] [--min-expiry <duration>]` |
| Check readiness | `pi auth check [--provider <provider>] [--model <model>] [--json] [--credentials] [--no-refresh]` |
| Help | `pi auth`, `pi auth help`, or any auth form with `-h` / `--help` |

**Print commands**

- Write a single credential line to stdout (trailing newline).
- OAuth refresh goes through `ModelRuntime.getAuth()` (15s abort timeout).
- `--min-expiry` (bearer only): duration `N` + `ms|s|m|h` (for example `30m`, `1h`). Default minimum validity when printing bearer tokens is **30 minutes**.
- Errors if the provider uses the wrong credential type (API key vs OAuth), if none is configured, or if multiple providers match without `--provider`.

**`auth check`**

| Flag | Effect |
|------|--------|
| `--json` | Emit full result object as JSON |
| `--credentials` | Include the resolved credential (text mode: credential only; JSON: `credentials` field). Only when status is `ready` |
| `--no-refresh` | Use `ReadOnlyAuthStorage`; do not refresh expired OAuth |

Exit codes for check:

| Status | Exit code | Meaning |
|--------|-----------|---------|
| `ready` | `0` | Provider has usable auth |
| `not_ready` | `1` | Provider missing / credentials missing / credential unavailable |
| `invalid` | `2` | Invalid state or unexpected failure |

Plain text output without `--credentials` is the status string (`ready`, `not_ready`, `invalid`). Reasons include `provider_not_found`, `credentials_not_configured`, `credential_not_available`, `invalid_state`.

<RequestExample>
```bash
# API key for external tooling
pi auth print-api-key --provider openai

# OAuth bearer (refreshes when near expiry)
pi auth print-bearer-token --provider openai-codex --min-expiry 1h

# Machine-readable readiness
pi auth check --provider anthropic --json
```
</RequestExample>

## Global options

Parsed by `parseArgs()` into the `Args` object. Help text is produced by `printHelp()` (and can append extension-registered flags).

### Modes and output

| Flag | Type | Default / notes |
|------|------|-----------------|
| `--mode <mode>` | `text` \| `json` \| `rpc` | Only these three values are accepted |
| `-p`, `--print` | boolean | Non-interactive: process prompt and exit. Next non-flag / non-`@` token (including `---` YAML frontmatter) is consumed as a message |
| `--export <file>` | path | Export session JSONL to HTML and exit; optional output path is the first positional message |

**App mode resolution** (`resolveAppMode`):

| Condition | Mode |
|-----------|------|
| `--mode rpc` | `rpc` |
| `--mode json` | `json` |
| `--print`, or stdin not a TTY, or stdout not a TTY | `print` |
| Otherwise | `interactive` |

Piped stdin content is merged into the initial prompt except in RPC mode (stdin is the RPC channel). If stdin has data and the mode would be interactive, mode becomes print.

### Model and provider

| Flag | Notes |
|------|-------|
| `--provider <name>` | Provider id (help default text: `google`) |
| `--model <pattern>` | Pattern or id; supports `provider/id` and optional `:<thinking>` (for example `sonnet:high`) |
| `--api-key <key>` | Runtime API key for the selected model; requires `--model` / provider+model / scoped models |
| `--thinking <level>` | `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`. Invalid values are warnings |
| `--models <patterns>` | Comma-separated patterns for Ctrl+P cycling (globs / fuzzy) |
| `--list-models [search]` | List models and exit; optional fuzzy search (not a flag or `@file`) |

### Session

| Flag | Notes |
|------|-------|
| `-c`, `--continue` | Continue most recent session |
| `-r`, `--resume` | Interactive session picker |
| `--session <path\|id>` | Session file or partial UUID |
| `--session-id <id>` | Exact project session id; creates if missing |
| `--fork <path\|id>` | Fork into a new session |
| `--session-dir <dir>` | Storage/lookup directory (overrides settings and env) |
| `--no-session` | Ephemeral (in-memory) session |
| `-n`, `--name <name>` | Display name at startup; empty/missing value errors |

**Conflicts**

- `--fork` cannot combine with `--session`, `--continue`, `--resume`, or `--no-session`.
- `--session-id` cannot combine with `--session`, `--continue`, or `--resume`.
- RPC mode rejects `@file` arguments.

### Tools

| Flag | Effect |
|------|--------|
| `-t`, `--tools <list>` | Comma-separated allowlist (built-in, extension, custom) |
| `-xt`, `--exclude-tools <list>` | Comma-separated denylist |
| `-nt`, `--no-tools` | Disable all tools by default |
| `-nbt`, `--no-builtin-tools` | Disable built-ins only; keep extension/custom tools |

Built-in names: `read`, `bash`, `edit`, `write`, `grep`, `find`, `ls` (`grep` / `find` / `ls` are off by default in the product surface).

### Resources and prompts

| Flag | Effect |
|------|--------|
| `-e`, `--extension <path>` | Load extension (repeatable) |
| `-ne`, `--no-extensions` | Skip discovery; explicit `-e` still applies |
| `--skill <path>` | Load skill file/dir (repeatable) |
| `-ns`, `--no-skills` | Disable skill discovery |
| `--prompt-template <path>` | Load template file/dir (repeatable) |
| `-np`, `--no-prompt-templates` | Disable template discovery |
| `--theme <path>` | Load theme file/dir (repeatable) |
| `--no-themes` | Disable theme discovery |
| `-nc`, `--no-context-files` | Disable `AGENTS.md` / `CLAUDE.md` discovery |
| `--system-prompt <text>` | Replace default system prompt |
| `--append-system-prompt <text>` | Append text or file contents (repeatable) |

### Trust, TUI, misc

| Flag | Effect |
|------|--------|
| `-a`, `--approve` | Trust project-local files for this run |
| `-na`, `--no-approve` | Ignore project-local files for this run |
| `--tui-mode <mode>` | `regular` (default) or `fullscreen` (experimental UI) |
| `--verbose` | Force verbose startup |
| `--offline` | Disable startup network ops (same as `PI_OFFLINE=1`) |
| `-h`, `--help` | Show help (after extension load when possible) |
| `-v`, `--version` | Print package version and exit |

### Positional inputs

| Form | Behavior |
|------|----------|
| `@path` | File attachment (`fileArgs`); `@` prefix stripped |
| bare text | Message string (`messages`) |
| `--flag=value` | Unknown long flag with equals value |
| `--flag value` | Unknown long flag with next non-flag token as value |
| `--flag` | Unknown boolean long flag (`true`) |

Unknown long flags become `extensionFlagValues` for extension CLI registration (for example plan-mode’s `--plan`).

## Environment variables (CLI-relevant)

| Variable | Role |
|----------|------|
| Provider API key vars | See help / [Authentication](/authentication) (many `*_API_KEY` names) |
| `PI_CODING_AGENT_DIR` | Config directory (default `~/.pi/agent`) |
| `PI_CODING_AGENT_SESSION_DIR` | Session storage (overridden by `--session-dir`) |
| `PI_PACKAGE_DIR` | Override package directory (Nix/Guix-style layouts) |
| `PI_OFFLINE` | `1` / `true` / `yes` disables startup network ops |
| `PI_TELEMETRY` | Override install telemetry (`1`/`true`/`yes` or `0`/`false`/`no`) |
| `PI_SHARE_VIEWER_URL` | Base URL for `/share` (default `https://pi.dev/session/`) |
| `PI_EXPERIMENTAL` | Must be exactly `1` for experimental first-time setup |
| `PI_STARTUP_BENCHMARK` | Interactive-only startup benchmark path |

## Experimental auth and remote CLI scaffolding

Under `src/cli/experimental/` there is a composable command tree (`experimentalCli`) used by unit tests. It is **not** currently invoked from `main()`; the production binary still uses the classic `parseArgs` path for normal sessions.

| Command form | Options | Purpose in the tree |
|--------------|---------|---------------------|
| Default (`pi`) | `--listen`, `--auth-token`, `--auth-token-file`, plus legacy `parseArgs` remainder | Host-style parse result with optional listen addresses and auth input |
| `server` | `--listen`, `--auth-token`, `--auth-token-file` | Server invocation; rejects leftover classic CLI options |
| `client` | `--connect`, `--auth-token`, `--auth-token-file` | Client invocation; rejects leftover classic CLI options |

### Experimental auth input

```ts
type AuthInput =
  | { type: "token"; token: string }
  | { type: "file"; path: string };
```

| Option | Behavior |
|--------|----------|
| `--auth-token <token>` | Inline token |
| `--auth-token-file <path>` | Token file path |
| Both | Error: mutually exclusive |
| Duplicate same option | Error: may only be specified once |

### Transport addresses

Only Unix sockets: `unix:///absolute/path` (no authority, query, or fragment). Used with `--listen` / `--connect`.

When experimental features are enabled (`PI_EXPERIMENTAL=1`), interactive first-time setup can prompt for theme and analytics opt-in before runtime services start.

## Argument parsing edge cases

| Case | Behavior |
|------|----------|
| `-p` followed by `--provider` | Does not treat the flag as a prompt; continues option parsing |
| `-p` followed by `---…` text | Consumed as prompt (YAML frontmatter-safe) |
| Invalid `--thinking` | Warning diagnostic; run continues without that level |
| Invalid `--tui-mode` / missing value | Error diagnostic; process exits 1 if any error diagnostics exist |
| Missing `--name` value | Error: `--name requires a value` |
| Extension load failure in runtime | Reported; hint `pi -ne` |
| `--api-key` without model | Error diagnostic at runtime construction |

## Examples

```bash
# Interactive
pi
pi "List all .ts files in src/"

# Files + prompt
pi @prompt.md @image.png "What color is the sky?"

# Print / JSON / RPC
pi -p "Summarize package.json"
pi --mode json -p "List tools"
pi --mode rpc

# Session
pi --continue "What did we discuss?"
pi --resume
pi --session-id my-run --name "Refactor auth"
pi --fork 1234abcd
pi --no-session -p "Ephemeral ask"

# Model and tools
pi --model openai/gpt-4o "Help me refactor this"
pi --models "anthropic/*,*sonnet*"
pi --tools read,grep,find,ls -p "Review src/"
pi --exclude-tools bash
pi --no-builtin-tools -e ./my-ext.ts

# Resources
pi --no-extensions -e ./only-this.ts
pi --no-skills --skill ./one-skill
pi --export ~/.pi/agent/sessions/.../session.jsonl
pi --list-models sonnet
```

## Verification

```bash
pi --version
pi --help
pi auth check --provider openai --json
pi --list-models
```

Expected signals: version string matches package version; help lists options and extension flags when extensions load; auth check exits 0/1/2 with status text or JSON.

## Related pages

<CardGroup>
  <Card title="Installation" href="/installation">
    npm and installer paths, bin entry, and verification for @earendil-works/pi-coding-agent.
  </Card>
  <Card title="Run modes" href="/run-modes">
    Interactive, print/JSON, RPC, and SDK invocation and when to use each.
  </Card>
  <Card title="Authentication" href="/authentication">
    API keys, OAuth login, credential storage, and refresh behavior.
  </Card>
  <Card title="RPC mode" href="/rpc-mode">
    Process integration via rpc-entry and the JSON command stream.
  </Card>
  <Card title="Package exports" href="/package-exports">
    Public npm surface: main, rpc-entry, client, bin name, piConfig.
  </Card>
  <Card title="Tools and allowlists" href="/tools">
    Default tools, extension tools, and --tools / --exclude-tools filters.
  </Card>
</CardGroup>
