using/cli

The lash binary runs an interactive terminal UI by default, autonomous single-shot modes under --print, a JSONL event stream under --mode json, an stdin/stdout RPC loop under --mode rpc, and administrative flows like --export and --reset. This repository owns the executable and its user-visible contract.

First Run

If ~/.lash/config.json does not exist, lash launches the setup wizard. The wizard provisions an active provider, collects credentials, asks for the default execution mode and RLM dialect, and writes the config file before dropping you into the TUI.

$ lash
# Setup wizard:
# 1. Choose a provider (Anthropic, OpenAI, OpenAI-compatible, Codex, Google OAuth)
# 2. Paste API key (or run OAuth / device-code flow)
# 3. Optionally paste a Tavily API key for the built-in web-search tool
# 4. Choose standard or RLM; if RLM, choose lashlang or typescript
# 5. Drop into the interactive TUI

Re-run the wizard at any time with lash --provider. Use /provider from inside a session to switch providers without restarting.

Slash Commands

Type a / at the start of the prompt to invoke a command. Tab-completion is available. Skill names registered in ~/.lash/skills/ or .agents/lash/skills/ appear as additional /<skill> commands.

CommandAliasesUsageDescription
/clear/new/clearReset conversation
/compact—/compact [focus instructions]Open a compaction Agent Frame seeded by a summary
/controls—/controlsShow keyboard shortcuts
/fork—/forkOpen a forked session in a new terminal
/tree—/treeBrowse and switch branches in the current session
/version—/versionShow lash-cli and lash-sansio versions
/info/status/infoShow current session/runtime info
/model—/model [name]Show or switch the LLM model
/variant—/variant [name]Show or switch the model variant (e.g. high, max)
/mode—/modeShow the session's locked execution mode and, for RLM, its pinned dialect (for example rlm · typescript)
/expand—/expand [compact|normal|full]Show or set the timeline expansion level (0/1/2 also accepted); the same toggles as Ctrl+O and Alt+O, and it works while scrolled up
/provider/login/providerSwitch, add, or re-authenticate providers
/logout—/logoutRemove stored credentials for active provider
/retry—/retryReplay the previous turn payload
/resume/continue/resume [id-or-name]Search sessions or resume by id/name (interactive picker if no arg)
/skills—/skillsBrowse loaded skills
/help/?/helpOpen the commands and shortcuts overlay
/exit/quit/exitQuit

Keyboard Shortcuts

The TUI uses a small, consistent set of shortcuts. The full list is also available in-session via /controls.

History navigation

PageUp / PageDown
Scroll history or the active document overlay.
↑ / ↓
Cycle the input-draft history when not navigating a suggestion popup.

Prompt editing

Enter
Submit immediately when idle; while a turn is active, inject at the earliest safe runtime boundary.
Tab
Accept an autocomplete suggestion when the popup is open; with a draft, submit immediately when idle or queue a next-turn draft while active.
Ctrl+U
Delete draft text from the cursor to the start of the line.
Ctrl+K
Delete draft text from the cursor to the end of the line.
Ctrl+← / Ctrl+→
Move the cursor by word.
Home / End
Jump to the start / end of the prompt.

Selection & clipboard

Ctrl+Shift+C
Copy the current selection or the last assistant response. Configure via LASH_COPY_BINDING if the default conflicts with your terminal.
Ctrl+C
Close a popup or overlay, cancel the active turn, clear a non-empty draft, or quit from an idle empty prompt.
Esc
Close overlays, pickers, and modal flows.

Command palette

Ctrl+P
Open the command palette settings overlay. In RLM mode, each New Session action names the dialect that will be pinned to the new session.

Pickers & multi-select

↑ / ↓ or k / j
Move selection.
Space
Toggle a multi-select option when supported.
Tab / BackTab
Add / remove a note on a selection when supported.

Background process dock

Tab / Shift+Tab
With an empty prompt, cycle focus through the dock of background processes. The dock lists only processes granted to the current session.
Enter
Open an overview of the focused process.
Delete
Cancel the focused process.

A running process shows running; a terminal one shows its outcome — success, error, cancelled, or abandoned. abandoned (ADR 0019) means the owner was lost without recording an outcome and renders in the failure style, like error. A detached command (shell.start with detach: true) never appears in the dock: it returns immediately with status: "detached" and is owned by the host/OS, not tracked as a session process.

CLI Flags

Run lash --help for the full list. The most common flags, grouped by purpose:

Provider & model

--provider
Re-run the provider setup wizard.
--model <name>
Override the default model for this session.
--variant <name>
Override the model-native variant (e.g. high, max, xhigh).
--api-key <key>
One-shot API key for OpenAI or OpenAI-compatible providers.
--base-url <url>
Base URL for the LLM API (OpenAI-compatible).
--tavily-api-key <key> (env TAVILY_API_KEY)
Tavily key for the bundled web-search tool.

Execution

--execution-mode <mode>
standard (default) or rlm.
--rlm-dialect <dialect>
Pin lashlang or typescript for the lifetime of a new RLM session. Refused in standard mode.
--rlm-var name=<json>
Repeatable. Project a read-only bound variable into the RLM autonomous turn.
--rlm-vars-file <path>
Same as above but load the projections from a JSON object file.
--rlm-termination <mode>
Choose natural (default) or require an explicit structured finish with finish-required.

Session

--resume <id_or_name>
Resume by session id or name. Alpha-era per-session .db files are listed as incompatible and refused with their preflight schema stamps; they are never opened or deleted.
--resume-prompt <prompt>
Send a prompt right after resume.

Output modes

-p, --print <prompt>
Autonomous: run the prompt once, write the answer to stdout, exit. See Print mode below.
--mode <interactive|json|rpc>
Select the host surface. interactive is the TUI default; json requires --print and writes JSONL events to stdout; rpc reads LF-delimited requests from stdin and writes LF-delimited records to stdout.
--await-background-work
In autonomous mode, wait for plugin background work before exiting.
--turn-usage-json <path>
In autonomous mode, write per-turn and cumulative usage JSON to a file.

Debug & tracing

--debug
Detailed lifecycle/debug logs plus per-session LLM traces.
--trace-level <standard|extended>
Trace detail. Default standard.
--debug-ui-trace trace.json
Record the live TUI session as a replayable UI trace plus a final snapshot.
--debug-ui-trace-interval-ms <ms>
When recording a UI trace, also capture numbered checkpoint snapshots at the given interval.

Admin

--reset
Perform a whole-home runtime-data reset: delete all databases and attachments in ~/.lash/store/ and the host session roster under ~/.lash/sessions/, preserving provider configuration.
--info
Print current config / runtime info and exit.
--check-update / --update
Check / install the latest release.
--export <session-id>
Export a session from the unified Lash catalog. Combine with --export-format html|json, --export-trace <jsonl>, and --export-out <path>.

JSON & RPC Modes

--mode json and --mode rpc are machine-facing CLI surfaces. They do not enter the TUI and they reserve stdout for LF-delimited JSON records. The stream is versioned: the opening turn_start record (and the RPC ready record) carries protocol_version: 1, so a consumer can pin the wire shape before parsing the rest.

$ lash --mode json --print "Summarise this repository."
{"type":"turn_start","protocol_version":1,"stream_id":1}
{"type":"event","stream_id":1,"activity":{"id":"a1","correlation_id":"c1","type":"model_request_started","protocol_iteration":0}}
{"type":"event","stream_id":1,"activity":{"id":"a2","correlation_id":"call-3","type":"tool_call_completed","call_id":"call-3","name":"exec_command","args":{"...":"..."},"output":{"...":"..."},"duration_ms":41}}
{"type":"turn_finish","stream_id":1,"ok":true,"cancelled":false,"assistant_text":"...","outcome":{...},"usage":{...},"children_usage":[],"errors":[],"execution":{...},"tool_calls":[...]}

Each event wraps one TurnActivity: an id, a correlation_id, and a flattened TurnEvent tagged by type. Tool events report the runtime tool name (exec_command), not the shell.exec surface path; see the Lash SDK's event vocabulary and stability contract.

RPC mode starts a reusable session and reads one JSON object per line from stdin. It opens with a ready record ({"type":"ready","protocol_version":1,"protocol":"lash.rpc.v1","methods":["prompt","ping","shutdown"]}). A prompt request streams turn_start, event, and turn_finish records tagged with the request id, then writes a final response.

{"id":1,"method":"prompt","params":{"prompt":"List the top-level crates."}}
{"id":2,"method":"shutdown"}

Config File

Configuration lives at ~/.lash/config.json (overridable via the LASH_HOME environment variable). The schema is defined by LashConfig in lash-cli and uses serde(deny_unknown_fields), so unknown top-level fields are rejected to catch typos.

{
  "active_provider": "openai",
  "charge_safety": { "mode": "require_guarantee" },
  "execution_mode": "rlm",
  "rlm_dialect": "typescript",
  "providers": {
    "openai": { "type": "openai", "api_key": "sk-..." },
    "anthropic": {
      "type": "anthropic",
      "api_key": "sk-ant-...",
      "options": {
        "cache_retention": "long",
        "max_output_tokens": 16384,
        "reliability": {
          "response_start_timeout": 30000
        }
      }
    }
  },
  "auxiliary_secrets": {
    "tavily_api_key": "tvly-..."
  },
  "mcp_servers": {
    "docs": {
      "transport": "stdio",
      "command": "uvx",
      "args": ["mcp-server-docs"],
      "graceful_period_ms": 3000,
      "post_kill_wait_ms": 1000
    }
  },
  "agent_models": {},
  "model_defaults": {
    "openai": { "model": "openai/gpt-5.5", "variant": "medium" }
  }
}
FieldTypePurpose
active_providerstringKey of the currently active provider; must exist in providers.
providersmapPer-provider credentials and metadata, keyed by provider kind. Shared provider tuning lives under options, including cache_retention (none | short | long), max_output_tokens, and reliability.response_start_timeout in milliseconds. Omit the response-start timeout or set it to 0 to retain the legacy min(request, chunk) bound.
charge_safetyobjectLive host retry policy. Defaults to {"mode":"require_guarantee"} when unset. Applied on every creation and reopen, including /clear, /resume, autonomous runs, and native queued work; it is excluded from durable session policy serialization.
execution_modestringDefault mode for fresh sessions: standard or rlm.
rlm_dialectstringDefault language pinned to fresh RLM sessions: lashlang or typescript.
auxiliary_secretsobjectSecrets for non-LLM services. Currently tavily_api_key.
mcp_serversmapMCP server configs by name. Discriminated by transport: stdio, streamable_http, or sse. Each server may set graceful_period_ms before forced termination and post_kill_wait_ms after it.
agent_modelsmapUser-overridable model names per subagent capability.
model_defaultsmapFresh-session model + variant default per provider kind. Session resumes ignore this and use the persisted head.

Retries & charge safety

By default, Lash requires a provider idempotency or resume guarantee before retrying a generation that may already have been billed. To accept bounded duplicate billing, set this top-level config key:

"charge_safety": {
  "mode": "accept_duplicate_billing",
  "max_unsafe_retries": 2,
  "max_duplicate_cost_tokens": 2000
}

Lash clamps max_unsafe_retries to at most five per logical LLM call. The optional token bound counts input, output, cache-read, and cache-write tokens already billed for the abandoned generation. Missing partial usage counts as zero toward this bound, so it cannot cap unknown charges; the retry-count limit still applies. Omit max_duplicate_cost_tokens or set it to null for no token bound. Nonretryable failures remain nonretryable. This is configuration, with no CLI flag.

Provider retry settings live under providers.<kind>.options.reliability.retry. ProviderRetryPolicy.jitter_ms defaults to 500 milliseconds of per-attempt additive random jitter; 0 disables it. retry_after_cap_ms defaults to 60000 (60 seconds). A longer server Retry-After now refuses immediately instead of sleeping a truncated delay.

Failed-turn diagnostics show the charge refusal code, billed tokens, and whether partial output exists. Zero usage is labeled as reported or defaulted because the evidence does not distinguish missing usage from a reported zero. Partial text stays out of the terminal timeline. Advisory issues render as warnings, and omitted tool records disclose their counts. JSON/RPC finish records also carry failure_evidence and omitted.

Environment Variables

The CLI reads a small set of environment variables; most are overrides for paths or behavior the config file doesn't cover.

VariablePurpose
LASH_HOMEOverride the root data directory (defaults to ~/.lash/).
OPENAI_API_KEY / OPENAI_COMPATIBLE_API_KEYProvider API keys used during setup or one-shot sessions when no stored key is configured.
OPENROUTER_API_KEYOpenRouter API key accepted as an OpenAI-compatible shortcut when --base-url points at https://openrouter.ai/api/v1.
TAVILY_API_KEYTavily API key for the bundled web search tool. Alternative to --tavily-api-key and the config file.
LASH_GOOGLE_CLIENT_ID / LASH_GOOGLE_CLIENT_SECRETOverride the OAuth client credentials used by the Google / Code Assist provider.
CODE_ASSIST_ENDPOINT / CODE_ASSIST_API_VERSIONOverride the Google Code Assist API endpoint and version for provider integration testing.
LASH_COPY_BINDINGOverride the copy keystroke: ctrl-shift-c (default) or ctrl-y. Ctrl+C stays reserved for cancel semantics, so ctrl-c falls back to the default.
LASH_RUNBOOK_DIALECTDefault RLM dialect when an older config file does not yet declare rlm_dialect.
LASH_LOGtracing-subscriber log filter (e.g. debug, warn, lash=trace).
LASH_NO_OSC52Disable OSC 52 clipboard escape sequences.
LASH_INSTALL_DIROverride the install dir used by --update.
LASH_REPOOverride the GitHub repo used by --check-update / --update.
LASH_TOKIO_STACK_BYTESOverride the worker-thread stack size used by the CLI Tokio runtime.
GOOGLE_CLOUD_PROJECT[_ID]Google Cloud project ID for the Gemini / Code Assist provider.

Resume & Fork

Every session has a UUID and normally a generated human-readable name. --resume <id_or_name> accepts either; legacy .db filenames are refused as incompatible. Without an argument, /resume opens an interactive picker showing the 50 most recent live root sessions from Lash's unified catalog, sorted by recency. The host roster contributes display metadata only, so catalog-only sessions remain visible with fallback labels. The picker hides zero-turn sessions when any non-empty session exists; direct /resume <id-or-name> may still target any session.

The current durable compatibility markers are session schema 50, trace schema 18, and remote protocol 54. lash --version and /info report them.

This pin is a forward-only, whole-home runtime-data cutover. Older session, process, trigger, and effect-replay databases are refused before provider startup; there is no migration chain. Retain or export needed data with the old compatible binary first. Then run lash --reset and confirm to remove the entire runtime store and session roster together, preserving provider configuration. Never reset databases piecemeal. Old trace files need a compatible reader; remote peers need matching protocol versions.

/fork branches the current session at the current message and opens the fork in a new terminal window. Lash resolves the launcher from the running executable and falls back to finding lash on $PATH if that binary isn't launchable. The launched fork runs lash --resume <new-session-id> in the new terminal.