History navigation
- PageUp / PageDown
- Scroll history or the active document overlay.
- ↑ / ↓
- Cycle the input-draft history when not navigating a suggestion popup.
The lash binary, shipped by the lash-cli crate, 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. It is the runtime's reference application and a worked embedding example: a testing bed for the library, not a separate product, and not required to build on lash. The runtime is the crate you embed. This page enumerates the user-visible surface: flags, slash commands, key bindings, the setup wizard, the config file, and the environment variables it reads.
If ~/.lash/config.json does not exist, lash launches the setup wizard. The wizard provisions an active provider, collects credentials, 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. 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.
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.
| Command | Aliases | Usage | Description |
|---|---|---|---|
/clear | /new | /clear | Reset conversation |
/compact | — | /compact [focus instructions] | Open a compaction Agent Frame seeded by a summary |
/controls | — | /controls | Show keyboard shortcuts |
/fork | — | /fork | Open a forked session in a new terminal |
/tree | — | /tree | Browse and switch branches in the current session |
/version | — | /version | Show lash-cli and lash-sansio versions |
/info | /status | /info | Show 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 | — | /mode [name] | Show the session's execution mode (standard / rlm); it is locked for the session, so changing it requires a new session started with --execution-mode |
/provider | /login | /provider | Switch, add, or re-authenticate providers |
/logout | — | /logout | Remove stored credentials for active provider |
/retry | — | /retry | Replay the previous turn payload |
/resume | /continue | /resume [id-or-name] | Search sessions or resume by id/name (interactive picker if no arg) |
/skills | — | /skills | Browse loaded skills |
/help | /? | /help | Open the commands and shortcuts overlay |
/exit | /quit | /exit | Quit |
The TUI uses a small, consistent set of shortcuts. The full list is also available in-session via /controls.
LASH_COPY_BINDING if the default conflicts with your terminal.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.
Run lash --help for the full list. The most common flags, grouped by purpose:
--provider--model <name>--variant <name>high, max, xhigh).--api-key <key>--base-url <url>--tavily-api-key <key> (env TAVILY_API_KEY)--execution-mode <mode>standard (default) or rlm.-c, --context-approach <approach>rolling_history or observational_memory.--rlm-var name=<json>--rlm-vars-file <path>--resume <id_or_name>.db filename.--resume-prompt <prompt>-p, --print <prompt>--mode <interactive|json|rpc>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--turn-usage-json <path>--debug--trace-level <standard|extended>standard.--debug-ui-trace trace.json--debug-ui-trace-interval-ms <ms>--om-observation-message-tokens <tokens>--om-observation-buffer-tokens <tokens>--om-observation-block-after-tokens <tokens>--om-observation-max-tokens-per-batch <tokens>--om-previous-observer-tokens <tokens>--om-reflection-observation-tokens <tokens>--om-reflection-buffer-activation-percent <percent>--om-reflection-block-after-tokens <tokens>--reset~/.lash/ and ~/.cache/lash/ and exit.--info--check-update / --update--export <db>--export-format html|json, --export-trace <jsonl>, and --export-out <path>.lash --print "<prompt>" runs Lash autonomously: it boots, executes the prompt, prints the settled TurnFinish::AssistantMessage prose to stdout, and exits. Print mode runs without a TUI; status and tool output go to stderr. An autonomous system prompt is injected so the assistant completes the task without asking for input.
Each completed tool call prints a one-line status marker to stderr: [tool] <name> · <status> · <duration>, where status is ok, error, or cancelled and the duration is dropped when it is negligible. The <name> is the runtime tool name (exec_command, not the shell.exec surface path); see Reporting channels → Tool names.
$ lash --print "Summarise this branch's changes in two bullets."
# Assistant prose to stdout, tool/lifecycle logs to stderr:
# [tool] exec_command · ok · 41ms
# [tool] read_file · ok
$ lash --print "Investigate the latest test failure." \
--await-background-work \
--turn-usage-json out/usage.json
--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; for the full event vocabulary and stability contract see Reporting channels.
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"}
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",
"providers": {
"openai": { "type": "openai", "api_key": "sk-..." },
"anthropic": {
"type": "anthropic",
"api_key": "sk-ant-...",
"options": {
"cache_retention": "long",
"max_output_tokens": 16384
}
}
},
"auxiliary_secrets": {
"tavily_api_key": "tvly-..."
},
"mcp_servers": {
"docs": {
"transport": "stdio",
"command": "uvx",
"args": ["mcp-server-docs"]
}
},
"agent_models": {},
"model_defaults": {
"openai": { "model": "openai/gpt-5.5", "variant": "medium" }
}
}
| Field | Type | Purpose |
|---|---|---|
active_provider | string | Key of the currently active provider; must exist in providers. |
providers | map | Per-provider credentials and metadata, keyed by provider kind. Shared provider tuning lives under options, including cache_retention (none | short | long) and max_output_tokens. |
auxiliary_secrets | object | Secrets for non-LLM services. Currently tavily_api_key. |
mcp_servers | map | MCP server configs by name. Discriminated by transport: stdio, streamable_http, or sse. |
agent_models | map | User-overridable model names per subagent capability. |
model_defaults | map | Fresh-session model + variant default per provider kind. Session resumes ignore this and use the persisted head. |
The CLI reads a small set of environment variables; most are overrides for paths or behavior the config file doesn't cover.
| Variable | Purpose |
|---|---|
LASH_HOME | Override the root data directory (defaults to ~/.lash/). |
OPENAI_API_KEY / OPENAI_COMPATIBLE_API_KEY | Provider API keys used during setup or one-shot sessions when no stored key is configured. |
TAVILY_API_KEY | Tavily API key for the bundled web search tool. Alternative to --tavily-api-key and the config file. |
LASH_GOOGLE_CLIENT_ID / LASH_GOOGLE_CLIENT_SECRET | Override the OAuth client credentials used by the Google / Code Assist provider. |
CODE_ASSIST_ENDPOINT / CODE_ASSIST_API_VERSION | Override the Google Code Assist API endpoint and version for provider integration testing. |
LASH_COPY_BINDING | Override 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_LOG | tracing-subscriber log filter (e.g. debug, warn, lash=trace). |
LASH_NO_OSC52 | Disable OSC 52 clipboard escape sequences. |
LASH_INSTALL_DIR | Override the install dir used by --update. |
LASH_REPO | Override the GitHub repo used by --check-update / --update. |
LASH_TOKIO_STACK_BYTES | Override 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. |
Every session has a UUID and a generated human-readable name. --resume <id_or_name> accepts either, plus the legacy .db filename for old sessions. Without an argument, /resume opens an interactive picker showing the 50 most recent sessions sorted by recency. The picker hides zero-turn sessions when any non-empty session exists; direct /resume <id-or-name> may still target any session.
/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.