@pentoshi/clai 4.11.18 → 4.11.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,27 +2,27 @@
2
2
 
3
3
  > A fast, terminal-native AI agent that runs real tools — built to run on **free API tiers**, stay alive across rate limits with **multi-key + multi-provider switching**, and do serious work: **building, debugging, and scope-based pentesting / bug bounty**.
4
4
 
5
- `clai` is an agentic CLI. It doesn't just describe what to do — it edits files, runs shell commands, scans hosts, fetches HTTP evidence, keeps a durable task plan, and verifies its own work before claiming success. It runs in your terminal with three full surfaces — an OpenTUI full-screen console (default on macOS/Linux), a classic Ink UI (default on Windows), and a noninteractive stream renderer for prompts and pipes. The original line-by-line REPL is gone: every interactive surface is a full-screen app sharing the same features, commands, and session state.
5
+ `clai` is an agentic CLI. It doesn't just describe what to do — it edits files, runs shell commands, scans hosts, fetches HTTP evidence, keeps a durable task plan, and verifies its own work before claiming success. It runs in your terminal with three full surfaces — an OpenTUI full-screen console (default on macOS/Linux), a classic Ink UI (default on Windows), and a noninteractive stream renderer for prompts and pipes. Every interactive surface is a full-screen app sharing the same features, commands, and session state.
6
6
 
7
7
  Two things make it practical for everyday use:
8
8
 
9
- - **It's cheap-to-free to run.** A fresh install runs **keyless out of the box** on the built-in Free provider — no signup, no API key. Point it at Google Gemini, NVIDIA NIM, OpenRouter, Bynara, Hetzner, or a local Ollama — all have free access — and clai stacks them. Add several keys per provider; when one hits a rate limit, it rotates to the next automatically. Want frontier models instead? Serve **Kimi K3 on your own Modal endpoint** on Modal's **$30/month of free credit**, or start on **Lightning AI's 40M free tokens**.
9
+ - **It's cheap-to-free to run.** A fresh install runs **keyless out of the box** on the built-in Free provider — no signup, no API key. Point it at DeepSeek, Kimi, GLM, MiniMax, Gemini, NVIDIA NIM, OpenRouter, OpenAI, Anthropic, or run completely offline with local Ollama. Add multiple keys per provider; when one hits a rate limit, it rotates to the next automatically.
10
10
  - **It's honest.** Findings need real tool output. Builds get typechecked/run before "done." Compaction and history keep long sessions coherent instead of hallucinating progress.
11
11
 
12
12
  ---
13
13
 
14
14
  ## Highlights
15
15
 
16
- - **Free-tier first.** 19 providers wired in, 6 cloud free tiers + local Ollama. The default provider is the **keyless Free** gateway (`free-2/kilo-auto/free`) so a fresh install runs at no cost with zero setup — no API key required.
17
- - **Multi-key smart switching.** Up to 10 keys per provider with a *sticky* active key and circular rotation on rate-limit / auth / quota / transient / 5xx / empty-response errors. Disable any key or endpoint row to skip it without deleting it. Optional cross-provider fallback and a free-only filter.
18
- - **Bring your own endpoint.** Deploy Kimi K3 (or Qwen / DeepSeek / GLM / GPT-OSS / your own fine-tune) to a [Modal](#modal--run-kimi-k3-on-your-own-endpoint-on-30month-of-free-credit) endpoint and drive it from clai on **$30/month of free compute credit**. Modal, Lightning AI and TokenRouter each keep a list of up to 10 base URLs with a sticky active one, so several deployments live side by side and you switch with one command — same editor, same ★ active row as keys.
16
+ - **Free-tier first.** Built-in **keyless Free** gateway (`free-2/kilo-auto/free`) so a fresh install runs at no cost with zero setup — no API key required.
17
+ - **Multi-key smart switching.** Up to 10 keys per provider with a *sticky* active key and circular rotation on rate-limit, quota, transient, or 5xx errors. Disable any key to skip it without deleting it. Optional cross-provider fallback and a free-only filter.
18
+ - **Broad provider support.** Native support for DeepSeek, Kimi (Moonshot), GLM (Zhipu AI), MiniMax, OpenAI, Anthropic, Google Gemini, Ollama, NVIDIA NIM, OpenRouter, Qwen, and custom OpenAI-compatible endpoints.
19
19
  - **Scope-based pentesting.** Opt-in engagement scope with authorized/excluded targets, allowed phases, rate and concurrency ceilings, redirect and DNS-rebinding escape detection, and out-of-scope flagging — designed for authorized pentests and bug-bounty programs.
20
20
  - **Real building & debugging.** Scaffolds apps, edits code surgically, installs packages, runs builds/tests, starts dev servers as background jobs, and probes them before reporting success.
21
21
  - **Durable plans.** `plan.create` / `task.update` drive a live checklist that survives context compaction and reloads with `/history` — the agent works task-by-task and won't fake completion.
22
- - **Durable agent sessions.** Interactive sessions run behind an authenticated local PTY broker, so an agent keeps working after `/minimise`, an SSH disconnect, or switching to another history session; `clai --resume <id>` reattaches to the same live UI and output stream.
23
- - **Persistent interactive terminals.** Conversation-owned PTY or pipe sessions keep REPLs such as Python, Metasploit, Meterpreter, database consoles, and debuggers open across model turns. The agent can send follow-up input, read incremental output, resize, interrupt, and close them without losing state.
22
+ - **Durable agent sessions.** Interactive sessions run behind a local broker, so an agent keeps working after `/minimise`, an SSH disconnect, or switching to another history session; `clai --resume <id>` reattaches to the same live UI and output stream.
23
+ - **Persistent interactive terminals.** Conversation-owned PTY or pipe sessions keep REPLs such as Python, Metasploit, Meterpreter, database consoles, and debuggers open across model turns.
24
24
  - **Native + text tool calling.** Uses provider-native function calling where available, with a text-fence fallback (`toolCalling: auto|native|text`).
25
- - **MCP, explicitly controlled.** Discovers local stdio and remote HTTP/SSE servers from project and compatible inherited configs. MCP tools are off by default; `/mcp` inspects and adds servers, and picking one drops an editable `@mcp:<server>` token into your prompt — stack as many as you need, delete one with backspace, or switch the whole session on with `/mcp all`.
25
+ - **MCP, explicitly controlled.** Discovers local stdio and remote HTTP/SSE servers from project configs. MCP tools are off by default; `/mcp` inspects and adds servers, and picking one drops an editable `@mcp:<server>` token into your prompt.
26
26
  - **Safety gate you control.** Every action is classified safe / confirm / block; deletes always confirm with a preview; destructive patterns are blocked.
27
27
 
28
28
  ---
@@ -58,112 +58,29 @@ cd clai && npm install && npm run build && npm start
58
58
 
59
59
  Node.js ≥ 22. Type `clai` in any terminal to start.
60
60
 
61
- ### Platform notes
62
-
63
- | Environment | Default surface | Explicit alternative |
64
- |---|---|---|
65
- | Interactive macOS/Linux terminal at least 60×14 | OpenTUI full-screen console | `clai --classic` for the Ink UI |
66
- | Interactive Windows terminal | Classic Ink UI | `--ui classic` / `--classic` |
67
- | stdin or stdout is not a TTY | Noninteractive stream renderer | use a prompt or pipe stdin |
68
-
69
- **Windows:** the platform-selection code chooses the classic Ink UI before any Bun/OpenTUI
70
- startup probe. This repository's macOS validation does not claim Windows Terminal,
71
- PowerShell, cmd.exe/conhost, VS Code terminal, ConPTY, or Windows-binary runtime evidence;
72
- those target-host checks remain release gates. Use `clai --classic` explicitly when you want
73
- the same selection on any platform.
74
-
75
- **Linux — terminal recommendations for full mouse & hover support:**
76
-
77
- The full-screen TUI uses mouse tracking and hover events via OpenTUI's Zig FFI renderer. Not all Linux terminal emulators report these correctly. If you see missing hover highlights, broken scroll, or no mouse response:
78
-
79
- | Terminal | Mouse/Hover | Notes |
80
- |----------|-------------|-------|
81
- | **Kitty** | ✅ Full | Recommended — fastest GPU-rendered terminal, full mouse protocol support |
82
- | **Alacritty** | ✅ Full | GPU-accelerated, excellent mouse support |
83
- | **WezTerm** | ✅ Full | Cross-platform, great defaults |
84
- | **iTerm2** (macOS) | ✅ Full | Native macOS, full mouse + hover |
85
- | **macOS Terminal** | ✅ Full | Works out of the box |
86
- | **GNOME Terminal** | ⚠️ Partial | Mouse clicks work; hover/SGR mouse mode may be limited |
87
- | **Konsole** | ⚠️ Partial | Some hover events may not report |
88
- | **xterm** | ⚠️ Partial | Needs `xterm -xrm 'XTerm*allowMouseOps: true'` |
89
- | **Linux TTY** (bare `/dev/tty`) | ❌ None | No mouse support; use `clai --classic` |
90
- | **tmux / screen** | ⚠️ Varies | Add `set -g mouse on` to `~/.tmux.conf`; passthrough depends on the outer terminal |
91
-
92
- > **Tip:** For the best experience on Linux, use **Kitty**, **Alacritty**, or **WezTerm**. These support SGR extended mouse reporting, which is what OpenTUI requires for hover, click, and scroll events. If your terminal doesn't support hover, clai still works — you just won't see hover highlights on buttons and links.
93
- >
94
- > For tmux users: `set -g mouse on` enables mouse passthrough, but hover quality depends on the outer terminal emulator.
61
+ > **Tip for Linux users:** For the best mouse and hover support in the full-screen TUI, modern GPU-rendered terminals like **Kitty**, **Alacritty**, or **WezTerm** are recommended. On macOS, **iTerm2** and the default Terminal work out of the box.
95
62
 
96
63
  ---
97
64
 
98
- ## Fast downloads (Cloudflare R2 mirror)
99
-
100
- Release binaries are mirrored to a **Cloudflare R2** bucket served through Cloudflare's global CDN — significantly faster than GitHub's release hosting in most regions, and free for this project's traffic (R2 has **zero egress fees**).
101
-
102
- Mirror layout at `https://downloads.clai.aniketpandey.website`:
103
-
104
- | Path | Contents |
105
- |---|---|
106
- | `/vX.Y.Z/` | Binaries + `.sha256` sidecars for release vX.Y.Z (latest release only — see retention) |
107
- | `/latest/` | Same files, refreshed on every release |
108
- | `/install/` | `install.sh` / `install.ps1` used by the one-liners above |
109
- | `/version.json` | Latest released version (update-check fallback) |
110
-
111
- **Retention.** R2's free tier covers 10 GB and a single release is roughly 650 MB across six
112
- platforms, so the mirror keeps only the newest release: after the new binaries are uploaded
113
- *and* re-verified byte-for-byte through the public URL, superseded `vX.Y.Z/` prefixes are
114
- purged. `/latest/`, `/install/` and `/version.json` are always retained. Set the repo variable
115
- `R2_KEEP_RELEASES` to keep more than one version (default `1`). Pruning never runs unless the
116
- new release verified successfully, and a failed purge only warns — it can never fail a release.
117
- GitHub Releases keeps every version permanently, so older downloads still work through the
118
- automatic fallback; only the CDN acceleration is lost for them.
119
-
120
- **Security model:** installers and `clai update` try the R2 mirror first for speed and fall back to GitHub Releases automatically. The SHA256 checksum is fetched from GitHub Releases (falling back to the mirror) and verified locally before anything is installed — the trust anchor stays with GitHub, so a bad mirror can only slow you down, never feed you tampered bytes. `CLAI_SKIP_CHECKSUM=1` still bypasses verification, at your own risk. If the mirror is unreachable but raw.githubusercontent.com works, grab the installer from `https://raw.githubusercontent.com/pentoshi007/clai/main/install/` instead — it makes the same mirror-first/fallback choices when it runs.
121
-
122
- User knobs:
123
-
124
- ```sh
125
- CLAI_DOWNLOAD_BASE=https://your-mirror.example
126
- CLAI_NO_MIRROR=1
127
- ```
128
-
129
- ### Maintainer setup (one-time, ~10 minutes)
130
-
131
- 1. **Create the bucket.** Cloudflare dashboard → R2 → Create bucket, e.g. `clai-releases`. Any location hint works.
132
- 2. **Make it publicly readable.** Bucket → Settings → Public access:
133
- - **Option A — `r2.dev` (zero DNS config):** enable the managed "r2.dev URL" → you get `https://pub-<hash>.r2.dev`.
134
- - **Option B — custom domain (the default everywhere in this repo):** add `downloads.clai.aniketpandey.website` as a custom domain (requires the zone's DNS on Cloudflare). Free SSL, and eligible for the edge cache once the cache rule in step 7 is in place.
135
- If you end up on a different hostname, update `DEFAULT_MIRROR_BASE` in `src/commands/update-install.ts`, the default base in `install/install.sh` and `install/install.ps1`, and the URLs in `manifests/` — or export `CLAI_DOWNLOAD_BASE` until a release ships the change.
136
- 3. **Create an API token.** R2 → Manage R2 API Tokens → Create API token: permission **Object Read & Write**, scoped to the `clai-releases` bucket only. Keep the **Access Key ID**, **Secret Access Key**, and the **Account ID** shown on the token page.
137
- 4. **Add repo secrets** (GitHub repo → Settings → Secrets and variables → Actions → Secrets): `R2_ACCOUNT_ID`, `R2_ACCESS_KEY_ID`, `R2_SECRET_ACCESS_KEY`, `R2_BUCKET_NAME`.
138
- 5. **Add repo variable** (Variables tab): `R2_PUBLIC_URL` = your public base URL, e.g. `https://downloads.clai.aniketpandey.website`, no trailing slash.
139
- 6. **Done.** On every `v*.*.*` tag, the `publish-r2` job in `.github/workflows/release.yml` uploads binaries + checksums to `/vX.Y.Z/` and `/latest/`, refreshes `/install/` and `/version.json`, re-downloads every asset through the public URL to re-verify its SHA256 against the sidecars, then purges superseded `vX.Y.Z/` prefixes. Missing secrets → the job skips without failing the release; missing `R2_PUBLIC_URL` → the public-URL verification is skipped and, because nothing was verified, pruning is skipped too. The `/version.json` write is advisory: if the token cannot write bucket-root objects the job warns and mirrors the manifest to `/latest/version.json` instead, since update checks already fall back to GitHub Releases.
140
-
141
- 7. **Turn on edge caching (one cache rule).** Release binaries have no file extension, and Cloudflare's default cache only covers known static extensions — so without a rule every download is served from R2 origin and `cf-cache-status` stays `DYNAMIC`. Add a Cache Rule (dashboard → Caching → Cache Rules) matching `Hostname eq downloads.clai.aniketpandey.website` with **Cache eligibility: Eligible for cache** and **Use origin cache-control: on**. The upload job already stamps the right `Cache-Control` on every object — `public, max-age=31536000, immutable` for `/vX.Y.Z/` (content-addressed by version, so it can never go stale), `max-age=300` for `/latest/` and `/install/`, and `max-age=60` for `/version.json`. Verify with `curl -sI <url> | grep cf-cache-status`: the second request for the same asset should report `HIT`.
142
-
143
- **Cost:** the R2 free tier covers 10 GB-months of storage, 1M Class A and 10M Class B operations per month, and egress is always free, so this stays $0 at clai's scale. With the cache rule in place repeat downloads are served from the edge and never reach R2, which also keeps Class B operations near zero.
144
-
145
- ---
146
-
147
-
148
65
  ## Quick start
149
66
 
150
- Out of the box, clai runs **keyless** on the built-in Free provider — no signup, no API key:
67
+ Out of the box, `clai` runs **keyless** on the built-in Free provider — no signup, no API key:
151
68
 
152
69
  ```sh
153
70
  clai # launch the full-screen agent console (already on the free provider)
154
71
  ```
155
72
 
156
- Prefer a different provider? Get a free key, add it, and go:
73
+ Prefer a different provider? Set your key and start:
157
74
 
158
75
  ```sh
159
- # Add a free key (NVIDIA shown; Gemini/OpenRouter/Bynara/Hetzner work the same)
160
- clai set nvidia nvapi-your_key_here
161
- clai use nvidia
76
+ # Add an API key (DeepSeek shown; Gemini, OpenAI, Anthropic, Kimi, etc. work the same)
77
+ clai set deepseek sk-your_key_here
78
+ clai use deepseek
162
79
 
163
80
  # Launch the full-screen agent console
164
81
  clai
165
82
 
166
- # Or one-shot from the shell
83
+ # Or one-shot directly from the shell
167
84
  clai "explain what this repo does and find the entrypoint"
168
85
  clai --mode agent "add a /health endpoint to the Express app and run the tests"
169
86
  ```
@@ -175,237 +92,56 @@ clai set ollama --url http://localhost:11434
175
92
  clai use ollama
176
93
  ```
177
94
 
178
- Want a frontier open model on your own hardware budget? Deploy **Kimi K3** to a Modal endpoint and run it on Modal's **$30/month of free compute credit** — see [Modal](#modal--run-kimi-k3-on-your-own-endpoint-on-30month-of-free-credit).
179
-
180
95
  ---
181
96
 
182
- ## Run it on free tiers (and keep it running)
183
-
184
- This is the core of clai's design: assemble capacity from free tiers, then survive rate limits automatically.
185
-
186
- ### Supported providers
187
-
188
- | Provider | Default model | Tier | Env var |
189
- |----------|---------------|------|---------|
190
- | Free (keyless) | `free-2/kilo-auto/free` | Free · keyless | — (no key needed) |
191
- | NVIDIA NIM | `openai/gpt-oss-20b` | Free | `NVIDIA_API_KEY` |
192
- | Google Gemini | `gemini-3.5-flash` | Free | `GEMINI_API_KEY` |
193
- | OpenRouter | `meta-llama/llama-3.3-70b-instruct:free` | Free | `OPENROUTER_API_KEY` |
194
- | Bynara | `mimo-v2.5-free` | Free | `BYNARA_API_KEY` |
195
- | Ollama | `llama3.1:8b` | Local / free | `OLLAMA_HOST` |
196
- | OpenAI | `gpt-5.4-mini` | Paid | `OPENAI_API_KEY` |
197
- | Anthropic | `claude-3-5-haiku-latest` | Paid | `ANTHROPIC_API_KEY` |
198
- | AgentRouter | `claude-opus-4-6` | Paid | `AGENTROUTER_API_KEY` |
199
- | AWS Mantle | `anthropic.claude-haiku-4-5` | Paid | `ANTHROPIC_API_KEY` |
200
- | Qwen Cloud | `qwen3.7-plus` | Paid (DashScope) | `DASHSCOPE_API_KEY` |
201
- | Modal | `moonshotai/Kimi-K3` | Usage-based ($30/mo credit) | `MODAL_PROXY_TOKEN_ID` + `MODAL_PROXY_TOKEN_SECRET` |
202
- | Lightning AI | `openai/gpt-5` | Per token (40M free to start) | `LIGHTNING_API_KEY` |
203
- | TokenRouter | `moonshotai/kimi-k3` | Per token (prepaid) | `TOKENROUTER_API_KEY` |
204
- | Meta (Muse Spark) | `muse-spark-1.2` | Paid (per token) | `MODEL_API_KEY` |
205
- | Fireworks | `accounts/fireworks/models/kimi-k2p6` | Paid (per token) | `FIREWORKS_API_KEY` |
206
- | Hetzner | `Qwen/Qwen3.6-35B-A3B-FP8` | Free (experiment — no billing yet) | `HETZNER_API_KEY` |
207
- | OrcaRouter | `openai/gpt-4o-mini` | Paid (per token, zero markup) | `ORCAROUTER_API_KEY` |
208
- | Merge Gateway | `openai/gpt-5.2` | Paid (per token, free tier budget) | `MERGE_GATEWAY_API_KEY` |
209
-
210
- Several "paid" providers also expose limited free allowances — the tier label reflects what the default keys usually buy you. Flip `freeOnly` off to include paid providers in fallback.
211
-
212
- #### Free — keyless, zero setup
213
-
214
- clai's default provider needs **no account, no API key, and no config**. It bundles **two keyless gateways** under one provider, namespaced by source so overlapping model names never collide: `free-1/…` ids come from the primary gateway and `free-2/…` ids from the [Kilo gateway](https://api.kilo.ai/api/gateway). Both serve rotating catalogs of free OpenAI-compatible models — `free-2/kilo-auto/free` (default), `free-1/mimo-v2.5-free`, `free-1/hy3-free`, `free-1/x-preview-f-free`, `free-2/stepfun/step-3.7-flash:free`, `free-2/nvidia/nemotron-3-ultra-550b-a55b:free` — with SSE streaming, native tool calling, `/effort` reasoning levels, and `reasoning_content` thinking folded into the normal `/think` block.
215
-
216
- ```sh
217
- clai # already on free — nothing to set up
218
- /model # live catalogs from both gateways, free-1/… + free-2/… (cached 1h)
219
- ```
220
-
221
- - **Keyless by design.** Requests go out with no `Authorization` header; `/model` lists only the keyless models from each gateway, so premium ids stay hidden until you add a key.
222
- - **Two sources, one provider.** Pick `free-1/…` or `free-2/…` in `/model`; a bare id with no prefix routes to `free-1`. If one gateway is rate limited, switching to the other is one `/model` pick away.
223
- - **Optional key upgrades you.** `clai set free <key>` (or `FREE_API_KEY`) unlocks premium models on your account. Premium ids without a key fail fast with a clear 402-style message instead of a confusing upstream error.
224
- - **Best-effort tier.** The free sets rotate upstream and are capacity-constrained / rate limited — if a request fails, retry once. For dependable daily use, add a key for any other provider (`clai set <provider> <key>`, then `clai use <provider>`).
225
-
226
- #### Modal — run Kimi K3 on your own endpoint, on $30/month of free credit
97
+ ## Providers & Key Management
227
98
 
228
- [Modal Endpoints](https://modal.com/docs/guide/endpoints) deploy an open-weight model on your own serverless URL that speaks the OpenAI Chat Completions API. That's the cheapest route to a **frontier open model like Kimi K3** from clai: the Starter plan is $0/month and includes **$30 of compute credit every month** once a payment method is on file, endpoints scale to zero so idle time costs nothing, and billing is per-second compute rather than per token.
99
+ ### Supported Providers
229
100
 
230
- Kimi K3, start to finish:
101
+ | Provider | Default Model | Tier | Environment Variable |
102
+ |----------|---------------|------|----------------------|
103
+ | **Free (keyless)** | `free-2/kilo-auto/free` | Free · Keyless | — (no key needed) |
104
+ | **DeepSeek** | `deepseek-chat` | Paid / Usage | `DEEPSEEK_API_KEY` |
105
+ | **Kimi (Moonshot)** | `moonshot-v1-auto` | Paid / Usage | `KIMI_API_KEY` / `MOONSHOT_API_KEY` |
106
+ | **GLM (Zhipu AI)** | `glm-4-flash` | Free tier / Paid | `GLM_API_KEY` / `ZHIPU_API_KEY` |
107
+ | **MiniMax** | `MiniMax-Text-01` | Paid / Usage | `MINIMAX_API_KEY` |
108
+ | **Google Gemini** | `gemini-2.5-flash` | Free tier / Paid | `GEMINI_API_KEY` |
109
+ | **NVIDIA NIM** | `openai/gpt-oss-20b` | Free tier | `NVIDIA_API_KEY` |
110
+ | **OpenRouter** | `meta-llama/llama-3.3-70b-instruct:free` | Free / Paid | `OPENROUTER_API_KEY` |
111
+ | **Ollama** | `llama3.1:8b` | Local / Free | `OLLAMA_HOST` |
112
+ | **OpenAI** | `gpt-5.4-mini` | Paid | `OPENAI_API_KEY` |
113
+ | **Anthropic** | `claude-3-5-haiku-latest` | Paid | `ANTHROPIC_API_KEY` |
114
+ | **Qwen Cloud** | `qwen3.7-plus` | Paid (DashScope) | `DASHSCOPE_API_KEY` |
115
+ | **Bynara** | `mimo-v2.5-free` | Free | `BYNARA_API_KEY` |
116
+ | **Hetzner** | `Qwen/Qwen3.6-35B-A3B-FP8` | Free (experiment) | `HETZNER_API_KEY` |
231
117
 
232
- ```sh
233
- pip install modal && modal setup # once
234
- modal endpoint create --model moonshotai/Kimi-K3 --name kimi-k3 # prints an id + dashboard link
235
- modal endpoint list # copy the endpoint URL from here
236
- modal workspace proxy-tokens create # wk-… id + ws-… secret (shown once)
237
-
238
- clai set modal --url <endpoint-url> # e.g. https://ws--ep-kimi-k3-server.us-west.modal.direct
239
- clai set modal wk-yourTokenId:ws-yourTokenSecret
240
- clai use modal # model defaults to moonshotai/Kimi-K3
241
- ```
118
+ Model lists for all providers are fetched dynamically from their respective APIs. You can switch models anytime using `/model` or `clai model <name>`.
242
119
 
243
- The catalog also covers Qwen, DeepSeek, GLM, Gemma, GPT-OSS, Nemotron and your own fine-tunes — swap `--model` and deploy another endpoint. Two things differ from every other provider: the base URL belongs to your workspace, and auth is a [proxy token](https://modal.com/docs/guide/webhook-proxy-auth) *pair* sent as `Modal-Key` / `Modal-Secret` instead of a bearer key. Endpoints are authenticated by default, and proxy tokens (`wk-`/`ws-`) are not interchangeable with Modal API tokens (`ak-`/`as-`).
244
-
245
- Endpoints and token pairs are both multi-entry lists with a sticky active choice, up to 10 each:
120
+ ### Manage Keys
246
121
 
247
122
  ```sh
248
- clai set modal --url <a> --url <b> # store two deployments; the last becomes active
249
- clai set modal --url <a> # re-passing a known URL just activates it
250
- clai unset modal --url # clear the URLs, keep the token pairs
251
- clai keys # every URL listed, ★ marks the active one
252
- ```
253
-
254
- Token pairs rotate on failure like any other provider's keys (the pair is stored as one `id:secret` string). Endpoints fail over alongside them: on a rate limit, workspace/auth mismatch, quota error, or 5xx, clai tries the next stored endpoint with the next key, and the ★ active endpoint sticks to the one that worked, so the next run starts on the healthy URL. `MODAL_BASE_URL` overrides the stored list entirely.
255
-
256
- From inside the console, `/provider modal` walks the same setup in two prompts — endpoint URL first (shown in full, since it isn't a secret), then the token pair — and leaves the active provider untouched if you cancel.
257
-
258
- Sticky sessions are on by default — clai sends a stable `Modal-Session-ID` for each conversation, with separate IDs for each subagent and auxiliary request stream. `MODAL_SESSION_ID` optionally seeds those IDs; it does not collapse active agents onto one shared ID. Standalone requests without a conversation use the configured ID directly, or a generated process-local ID. Sticky routing helps preserve warm caches, but does not guarantee a cache hit. The first request after idle still pays container start-up, so clai allows up to 3 minutes for the first token.
259
-
260
- `/info modal` prints the whole thing as a walkthrough: numbered setup steps, which credentials go where, the plan limits (Starter also caps you at 3 seats, 100 containers and 10 GPUs of concurrency), and a troubleshooting table.
261
-
262
- #### Lightning AI (one key, many vendors)
263
-
264
- [Lightning AI Model APIs](https://lightning.ai/docs/platform/inference/model-apis) are an OpenAI-compatible gateway in front of OpenAI, Anthropic, Google and Lightning-hosted open models — one key, billed per token, with **up to 40 million free tokens to start**. Model ids are vendor-namespaced.
265
-
266
- ```sh
267
- clai set lightning <your-api-key> # key: lightning.ai → Model APIs → show API key
268
- clai use lightning
269
- /model openai/gpt-5 # or anthropic/claude-opus-4-8, google/gemini-3.5-flash,
270
- # lightning-ai/gpt-oss-120b (cheapest open weights)
271
- ```
272
-
273
- `/model` reads the gateway's live catalog (41 ids at last check) and dedupes it. Keys are multi-key with rotation like everywhere else. The base URL defaults to the shared gateway but is overridable the same way as Modal's — point it at a private Lightning Inference deployment with `clai set lightning --url <url>`, or `LIGHTNING_BASE_URL`. Costs vary by orders of magnitude across the catalog, so check `/info lightning` before long agent runs.
274
-
275
- #### TokenRouter (frontier open models, one key)
276
-
277
- [TokenRouter](https://docs.tokenrouter.me) is an OpenAI-compatible gateway to Kimi, DeepSeek, Qwen, GLM, GPT-OSS and MiniMax — long-context open models aimed at agentic coding, billed per token from a prepaid balance.
278
-
279
- ```sh
280
- clai set tokenrouter sk-your-key # key: your account → API Keys
281
- clai use tokenrouter # model defaults to moonshotai/kimi-k3
282
- /model deepseek/deepseek-v4-pro # 1M context · or moonshotai/kimi-k2.7-code, z-ai/glm-5.1, MiniMax-M3
283
- ```
284
-
285
- Model ids are vendor-namespaced (`moonshotai/kimi-k2.6`, `deepseek/deepseek-v4-pro`, `qwen/qwen3.7-plus`); the old short ids (`kimi-k2p6`, `glm-5p1`, …) are still accepted and silently redirected to the current namespaced id. `/model` reads the live `/models` list, which is filtered to the channels your key can reach. clai carries the real per-model context windows — 1M for DeepSeek V4, 512K for MiniMax M3, 256K for Kimi/Qwen, 200K for GLM — so the context meter and auto-compaction are accurate instead of guessing 128K. Reasoning arrives as `reasoning_content` and is folded into the normal thinking block, so `/think` and `/effort` work. Base URL defaults to `https://api.tokenrouter.com/v1` and is overridable with `clai set tokenrouter --url <url>` or `TOKENROUTER_BASE_URL` if your account uses a different host.
286
-
287
- #### Fireworks and Hetzner (open-model inference)
288
-
289
- [Fireworks](https://fireworks.ai) is a per-token inference gateway for open models — Kimi, DeepSeek, GLM, Qwen, GPT-OSS, Llama — with SSE streaming and native tool calling.
290
-
291
- ```sh
292
- clai set fireworks fw_your_key # key: fireworks.ai → API keys
293
- clai use fireworks # model defaults to accounts/fireworks/models/kimi-k2p6
294
- ```
295
-
296
- [Hetzner Inference](https://docs.hetzner.com/general/company-and-policy/experiments/inference/) is Hetzner's OpenAI-compatible experiment API serving open-weight models (Qwen3.6-35B with vision, 262K context) from EU servers. It is free while experimental — there is no billing yet — with fair-use per-key rate limits and no SLA or production guarantee.
297
-
298
- ```sh
299
- clai set hetzner your-token # token: experiments.hetzner.com → Create API Token
300
- clai use hetzner # model defaults to Qwen/Qwen3.6-35B-A3B-FP8
301
- ```
302
-
303
- Both fetch the live model catalog for `/model` (cached 1h) and rotate keys like every other provider.
304
-
305
- #### OrcaRouter (one key, eleven upstreams, zero markup)
306
-
307
- [OrcaRouter](https://docs.orcarouter.ai) is an OpenAI-compatible gateway that routes OpenAI, Anthropic, Google Gemini, DeepSeek, xAI Grok, Qwen, Kimi, MiniMax, Z.ai GLM and more behind one key — billed at each provider's published price with no token markup. Model ids are vendor-prefixed (`openai/gpt-4o-mini`, `anthropic/claude-sonnet-4.6`, `google/gemini-2.5-flash`, `deepseek/deepseek-reasoner`, …), and `orcarouter/auto` picks the cheapest model that fits the request.
308
-
309
- ```sh
310
- clai set orcarouter sk-your-key # key: orcarouter.ai/console (starts with sk-)
311
- clai use orcarouter # model defaults to openai/gpt-4o-mini
312
- /model anthropic/claude-sonnet-4.6 # or any id from the live /models catalog
313
- ```
314
-
315
- Streaming, native tool calling, structured outputs (`response_format`), vision via `image_url` and prompt caching all work. Reasoning uses one unified `reasoning_effort` knob (low/medium/high, plus minimal/max on some models) that the gateway translates to each upstream's native shape — `/think` and `/effort` map onto it, and thinking arrives as `reasoning_content` in the usual thinking block. `/model` reads the live catalog (cached 1h), filtered to Chat-Completions-reachable models so image/video/tts ids stay out of the picker. Keys are multi-key with rotation like everywhere else; env var is `ORCAROUTER_API_KEY`.
316
-
317
- #### Merge Gateway (one key, many vendors, OpenAI-compatible)
318
-
319
- [Merge Gateway](https://gateway.merge.dev) fronts OpenAI, Anthropic, Google and other
320
- upstreams behind a single key. It exposes two surfaces: its own Responses-style API at
321
- `/v1` and a drop-in OpenAI-compatible surface at `/v1/openai`. clai drives the
322
- OpenAI-compatible surface, so streaming, native tool calling, prompt caching, reasoning
323
- effort, multi-key rotation and cross-provider fallback all behave exactly as they do for
324
- every other OpenAI-compatible provider.
325
-
326
- ```sh
327
- clai set merge-gateway mg_your-key # key: https://gateway.merge.dev (starts with mg_)
328
- clai use merge-gateway # model defaults to openai/gpt-5.2
329
- /model anthropic/claude-sonnet-4-6 # or any id from the live /models catalog
330
- ```
331
-
332
- Model ids are vendor-prefixed (`openai/gpt-5.2`, `anthropic/claude-sonnet-4-6`,
333
- `google/gemini-3.5-flash`, `deepseek/deepseek-reasoner`). `/model` reads the live catalog
334
- (cached 1h) filtered to ids reachable over Chat Completions, so embedding, image and audio
335
- entries stay out of the picker; if the catalog cannot be fetched, a documented offline
336
- subset is shown. Reasoning uses one `reasoning_effort` knob (low/medium/high) that the
337
- gateway translates per upstream, and thinking arrives as `reasoning_content` in the usual
338
- `/think` block. Requests carry both `Authorization: Bearer` and `X-API-Key`, matching the
339
- two shapes the gateway accepts. The free tier has a budget: once exhausted the gateway
340
- answers `402`, which clai treats as a quota error and rotates to the next key or provider.
341
- Aliases: `merge-gateway`, `mergegateway`, `merge`, `mg`. Env var `MERGE_GATEWAY_API_KEY`.
342
- `/info merge-gateway` prints the full walkthrough.
343
-
344
- #### Experiential Labs (one key, every vendor, OpenAI-compatible)
345
-
346
- [Experiential Labs](https://platform.experientiallabs.ai) fronts hosted providers,
347
- your own provider keys (BYOK) and platform-funded credits behind a single key. Every
348
- model is a slug that resolves through a provider waterfall with automatic failover, and
349
- the gateway is a drop-in OpenAI-compatible endpoint — streaming, native tool calling,
350
- prompt caching and reasoning effort all behave exactly as they do for every other
351
- OpenAI-compatible provider.
352
-
353
- ```sh
354
- clai set explabs xpl_your-key # key: https://platform.experientiallabs.ai/settings/api-keys (starts with xpl_)
355
- clai use explabs # model defaults to claude-fable-5.1
356
- /model gpt-5.6-sol # or any slug from the live catalog
357
- ```
358
-
359
- `/model` lists the slugs your key can call (from `/v1/models`), enriched with catalog
360
- facts from the public `/api/models` (context window, vision, per-model reasoning
361
- efforts) and cached for an hour; image/embedding/batch slugs stay out of the picker and
362
- a documented offline subset is shown when the gateway is unreachable. Reasoning uses one
363
- `reasoning_effort` knob (low/medium/high/xhigh/max) that passes through when the route
364
- supports it and snaps to the nearest supported level otherwise, so `/think` and `/effort`
365
- work across every vendor. Keys are multi-key with rotation like everywhere else; env var
366
- is `EXPLABS_API_KEY`. Aliases: `explabs`, `experiential`, `experientiallabs`,
367
- `experiential-labs`, `exp`. `/info explabs` prints the full walkthrough.
368
-
369
- ### Manage keys
370
-
371
- ```sh
372
- clai set nvidia nvapi-first_key # store a key (appends if one exists)
373
- clai set nvidia nvapi-second_key # add another key for the same provider
123
+ clai set deepseek sk-first_key # store a key
124
+ clai set deepseek sk-second_key # add another key for multi-key rotation
374
125
  clai set gemini --from-env GEMINI_API_KEY
375
- echo "nvapi-..." | clai set nvidia --stdin
126
+ echo "sk-..." | clai set deepseek --stdin
376
127
  clai set ollama --url http://localhost:11434
377
- clai set modal --url https://ws--ep-kimi-k3-server.us-west.modal.direct # endpoint (repeatable)
378
- clai set modal wk-tokenId:ws-tokenSecret # …then the proxy token pair
379
- clai set lightning <key> # Lightning AI Model APIs
380
- clai set tokenrouter sk-your-key # TokenRouter
381
- clai set fireworks fw_your_key # Fireworks
382
- clai set hetzner your-token # Hetzner Inference (experiments.hetzner.com)
383
- clai set orcarouter sk-your-key # OrcaRouter (orcarouter.ai/console)
384
- clai set merge-gateway mg_your-key # Merge Gateway (gateway.merge.dev)
385
- clai set explabs xpl_your-key # Experiential Labs (platform.experientiallabs.ai)
386
- clai set free <key> # optional: unlock premium models (free is keyless by default)
387
- clai unset modal --url # drop stored endpoint URLs, keep the keys
388
- clai keys # providers + masked keys (★ active) + endpoint URLs
389
- clai use nvidia # set active provider
390
- clai provider # interactive provider/model picker
391
- clai unset nvidia # remove ALL keys for a provider
128
+ clai keys # list providers with masked keys (★ active)
129
+ clai use deepseek # set active provider
130
+ clai provider # interactive provider/model selector
131
+ clai unset deepseek # remove keys for a provider
392
132
  ```
393
133
 
394
- In the console, **`/set`** opens a multi-row editor: add rows with `+`, remove rows, star a row to make it the active one, or disable a row to skip it in rotation without deleting it (`d` in the classic UI, `Ctrl+D` or the ○ marker in OpenTUI) — then **Save**, or **Reset all**. Providers with their own base URL (Modal, Lightning AI, TokenRouter) get a second **endpoints** row in the `/set` picker that edits their URL list the same way — URLs are shown in full rather than masked, since they aren't secrets. `/set <provider> https://…` adds and activates one URL directly. **`/keys`** lists keys masked plus the active endpoint, marking disabled rows `(disabled)`; **`/unset`** clears a provider. Disabled entries are never tried until re-enabled, and disabling every key for a provider fails fast with a clear error instead of burning requests.
395
-
396
- ### Smart switching (how it stays up)
134
+ In the interactive console:
135
+ - **`/set`** opens the multi-row key editor. Add keys (`+`), set the active key (★), or disable a key (`○`) without deleting it.
136
+ - **`/keys`** displays configured keys (masked) and active status.
137
+ - **`/provider`** and **`/model`** open interactive pickers for switching providers and models on the fly.
397
138
 
398
- - **Multi-key rotation** — up to **10 keys per provider**. The last key that worked is *sticky*; on failure clai rotates circularly to the next key. Endpoint providers (Modal, Lightning AI, TokenRouter) fail over their stored endpoints alongside the keys, and the ★ active endpoint follows the one that worked.
399
- - **Disable without deleting** — toggle any key or endpoint row disabled in the `/set` editor and rotation skips it until you re-enable it.
400
- - **What triggers a switch** — HTTP 429 (rate limit), 401/403 (auth), 402 / quota / billing text, transient network errors, 500–504, and empty completions. Auth and quota errors switch **immediately** (no backoff wait); rate limits back off briefly first.
401
- - **Cross-provider fallback** *(opt-in)* — `/fallback on` lets clai try other configured providers after the active one is exhausted (only when running a provider's default model).
402
- - **Free-only mode** *(opt-in)* — `/freeonly on` excludes paid-cloud providers from the fallback chain, so you never accidentally spend.
403
- - **Quiet status** — a single non-stacking status line shows what happened, e.g. `switching nvidia key [2/4] …ab12 (rate limited)`. Keys are always masked to the last 4 chars.
139
+ ### Smart Switching & Resilience
404
140
 
405
- ```sh
406
- /freeonly on # stay on free tiers only
407
- /fallback on # allow other providers when the current one is exhausted
408
- ```
141
+ - **Multi-key rotation** — Store up to **10 keys per provider**. The last key that worked is *sticky*. On encountering a rate limit (HTTP 429), quota limit, auth error, or 5xx server error, `clai` automatically rotates to the next available key.
142
+ - **Disable without deleting** — Toggle any key disabled in the `/set` editor; rotation skips it until you re-enable it.
143
+ - **Cross-provider fallback** *(opt-in)* — `/fallback on` lets `clai` fall back to other configured providers when the active provider is exhausted.
144
+ - **Free-only mode** *(opt-in)* — `/freeonly on` restricts fallback strictly to free tiers so you never accidentally spend.
409
145
 
410
146
  ---
411
147
 
@@ -415,11 +151,11 @@ In the console, **`/set`** opens a multi-row editor: add rows with `+`, remove r
415
151
 
416
152
  The same agent that runs recon also ships code. It explores before it writes, matches your existing stack from lockfiles, edits surgically, and proves the result:
417
153
 
418
- - Scaffolds and extends apps; replaces starter boilerplate with real features (a scaffold alone is treated as incomplete).
154
+ - Scaffolds and extends apps; replaces starter boilerplate with real features.
419
155
  - Surgical file tools: `fs.edit`, `fs.replaceLines`, `fs.append`, plus multi-file writes.
420
156
  - Runs the checks that apply — typecheck, build, unit/integration tests — and fixes failures before claiming success.
421
157
  - Starts dev servers as background jobs, tails until ready, probes `localhost`, and reports the URL / port / job id with the server left running.
422
- - Debugging loop: reproduce → read the actual error → fix root cause → re-verify (never "diagnosed but not fixed").
158
+ - Debugging loop: reproduce → read the actual error → fix root cause → re-verify.
423
159
 
424
160
  ```sh
425
161
  clai --mode agent "convert this Vite React app to Next.js App Router, keep all features, run the build"
@@ -428,7 +164,7 @@ clai --mode agent "this test is flaky — find the race and fix it"
428
164
 
429
165
  ### Scope-based pentesting & bug bounty
430
166
 
431
- clai is built to run real, authorized security work — not to narrate it. It follows a recon-first methodology and keeps you inside the boundaries you set.
167
+ `clai` is built to run real, authorized security work — not to narrate it. It follows a recon-first methodology and keeps you inside the boundaries you set.
432
168
 
433
169
  ```
434
170
  recon / discovery → fingerprint stack → plan.create (kind=pentest)
@@ -445,7 +181,7 @@ recon / discovery → fingerprint stack → plan.create (kind=pentest)
445
181
  4. `/implement` and execute task-by-task; expand the plan as new attack surface appears without wiping completed work.
446
182
  5. **Report** with structure — title, severity, evidence, reproduction, impact, remediation — and honest residual/untested notes.
447
183
 
448
- **Scope enforcement is real, not cosmetic.** When scope is active, clai checks each target against your authorized/excluded lists, enforces token-bucket rate limits and a concurrency ceiling, detects **redirects that leave scope** and **DNS-rebinding escapes**, and flags out-of-scope hosts instead of touching them. Loopback GET/HEAD stays allowed for local dev verification. Scope is opt-in: with no scope defined, scoping is simply off.
184
+ **Scope enforcement is real, not cosmetic.** When scope is active, `clai` checks each target against your authorized/excluded lists, enforces token-bucket rate limits and a concurrency ceiling, detects **redirects that leave scope** and **DNS-rebinding escapes**, and flags out-of-scope hosts instead of touching them.
449
185
 
450
186
  ```sh
451
187
  clai authorize-pentest AGREE
@@ -458,7 +194,7 @@ Dedicated recon tools: `pentest.recon` (bundled whois/dns/nmap), `pentest.webDis
458
194
 
459
195
  ### General security & sysadmin workflows
460
196
 
461
- Log triage, config hardening, packaging, network questions, OCR of a screenshot or PDF report, quick OSINT — all handled by the same agent under the same safety gate.
197
+ Log triage, config hardening, packaging, network analysis, OCR of a screenshot or PDF report, quick OSINT — all handled by the same agent under the same safety gate.
462
198
 
463
199
  ---
464
200
 
@@ -472,27 +208,27 @@ Three modes, switchable anytime with a slash command, `Shift+Tab`, or `clai --mo
472
208
  | **agent** | Executes: edits, installs, scans, verifies, works the plan. |
473
209
  | **plan** | Research and design a durable plan; approve with `/implement` before execution. |
474
210
 
475
- **Reasoning / thinking** is controlled with `/effort` (alias `/reasoning`), accepting `on`, `off`, `none`, `minimal`, `low`, `medium`, `high`, or `xhigh`. clai only sends reasoning options to models that support them — and if a model rejects them at runtime, it marks that model, retries once without them, and tells you (`… rejected reasoning options — retrying without them`) so a session never hangs on an unsupported knob.
211
+ **Reasoning / thinking** is controlled with `/effort` (alias `/reasoning`), accepting `on`, `off`, `none`, `minimal`, `low`, `medium`, `high`, or `xhigh`. `clai` sends reasoning options only to models that support them.
476
212
 
477
213
  ---
478
214
 
479
215
  ## Safety gate
480
216
 
481
- You own authorization; clai still gates risk on every action:
217
+ You own authorization; `clai` gates risk on every action:
482
218
 
483
219
  | Level | Behavior |
484
220
  |-------|----------|
485
221
  | **safe** | Auto-runs read-only work: `fs.read/list/search`, `sysinfo`, `dns.lookup`, `whois.lookup`, `http.fetch` GET, `web.search`/`web.fetch`, recon scanners. |
486
- | **confirm** | Asks first for mutations: file writes/edits, installs, moves, aggressive/mutating shell. |
487
- | **block** | Refuses destructive patterns (`rm -rf /`, fork bombs, classic exfil signatures) and SSRF-prone fetches. |
222
+ | **confirm** | Asks first for mutations: file writes/edits, installs, moves, mutating shell commands. |
223
+ | **block** | Refuses destructive patterns (`rm -rf /`, fork bombs, exfiltration signatures) and SSRF-prone fetches. |
488
224
 
489
- `fs.delete` always confirms (with an optional `v` preview) even under allow-all. `tool.batch` inherits the highest risk level of its children. Use `/permissions` to choose the default confirmation level and `/allow` / `/disallow` for a per-session tool allow-list.
225
+ `fs.delete` always confirms (with an optional diff preview) even under allow-all. Use `/permissions` to choose the confirmation level and `/allow` / `/disallow` for a per-session tool allow-list.
490
226
 
491
227
  ---
492
228
 
493
229
  ## Terminal UI
494
230
 
495
- The old line REPL has been removed — there are only full surfaces now. An OpenTUI full-screen console is selected for a sufficiently large interactive POSIX terminal. The classic Ink UI is selected on Windows, on smaller terminals, or with `--classic`; non-TTY prompts use the noninteractive stream renderer. In the classic UI, mouse reporting is on by default so wheel swipes scroll the live tail and panels instead of being misread as prompt-history keys; native text selection stays one modifier away (Shift/Option/Fn), and `CLAI_CLASSIC_MOUSE=0` turns mouse reporting off. The interactive surfaces provide streaming chat, nested tool cards (including `tool.batch` sub-calls), file diffs, a live plan pane, pickers, history, and secure masked key prompts. MCP tools stay off by default; picking a server with `/mcp` inserts an aqua `@mcp:<server>` token into the prompt you are typing — several tokens can coexist, backspace deletes one like any other text, and `all` / `off` add no token. MCP counts never appear in status bars.
231
+ The interactive console provides streaming chat, nested tool cards, file diffs, a live plan pane, pickers, session history, and masked key prompts.
496
232
 
497
233
  | Action | Key |
498
234
  |--------|-----|
@@ -503,62 +239,24 @@ The old line REPL has been removed — there are only full surfaces now. An Open
503
239
  | Plan pane / plan detail | `Ctrl+H` / `Ctrl+P` |
504
240
  | Background jobs | `Ctrl+J` |
505
241
  | Expand thinking / tool output | `Ctrl+T` / `Ctrl+O` |
506
- | Copy the focused thinking block | `c` |
242
+ | Copy focused thinking block | `c` |
507
243
  | Search transcript | `Ctrl+R` |
508
244
  | Copy selection | `Ctrl+Shift+C` |
509
245
  | Commands / file mentions | `/` · `@` |
510
246
  | MCP servers / project config | `/mcp` |
511
247
  | Command help / shortcut reference | `Ctrl+G` / `/shortcuts` |
512
248
 
513
- Completed thinking collapses to one clickable `✦ Thought for 3.2s` line; clicking it (or `Ctrl+T`) opens the reasoning in a card that scrolls internally when the reasoning is long. Clicking a card focuses it — the border turns violet and the wheel scrolls that card instead of the chat, even at its first and last row — while an unfocused card dims, shows `click to focus`, and leaves the wheel to the transcript. Moving the pointer out or clicking elsewhere releases it, and `c` copies the focused block's full reasoning and then releases it.
514
-
515
- Tool cards show the command/input clearly, with a live elapsed timer next to the command name while they run (the final duration stays visible afterwards), and keep long scan tails in an expandable OUTPUT pager (search, copy, export). File writes show a diff preview. When the agent finishes each prompt — naturally or aborted — a `✻ Worked for 1m16s` row is appended under the response, and it is restored when the session is resumed from `/history`. The status line names what the agent is actually doing right now — `responding` while streaming, `compacting` during auto or manual context compaction — instead of holding the last tool name. Prompts typed while the agent is busy are queued and run automatically in order once the turn settles; the queue pauses only when the previous turn was cancelled, errored, or stopped by the loop guard. Compaction cards preserve session memory without dropping the plan, and `/history` restores full sessions — prompts, tool results, and the matching plan — even after an abort or autosave.
516
-
517
- Automatic session titles describe the conversation's combined tasks, rather than replacing earlier work with the latest topic. Related follow-ups are grouped into shared themes, aiming for at most 12 words and 96 characters. Naming uses balanced request excerpts and the saved conversation transcript, including prompts inside compacted sections, so earlier tasks remain available after compaction and resume. Titles are first generated after two user prompts and reconsidered every three prompts; manually naming a session disables further automatic updates until it is reset or restored.
518
-
519
- On exit, both interactive surfaces leave the alternate screen and print a sign-off card on the normal terminal: the wordmark beside a labelled block — session title, folder, elapsed time and message count, reasoning/cache token notes, and the command that reopens the session — followed by the same per-provider/model token table `/usage` shows. Resuming is `clai --resume <id>` (a unique id prefix is enough, and `-c/--continue` picks the newest session for the current directory). Sessions that were never persisted (`--no-history`, private mode, or nothing sent) say so instead of offering a resume command. The card is borderless and reflows down to very narrow terminals: the wordmark drops from six rows to four, then the block stacks beneath it, and labels give way before the resume command is ever shortened.
520
-
521
- ### Context usage and prompt caching
522
-
523
- The composer context chip shows the latest provider-reported input token count, including during follow-ups and compaction. It waits for the first response instead of showing a speculative count. Local estimates are marked `~` and used when prompt usage is unavailable; internal request-size checks remain separate. A new provider measurement can legitimately be lower after compaction. Cached tokens are part of the reported prompt total, not an extra context bucket to add again.
524
-
525
- Automatic compaction targets 200,000 assembled request tokens by default, including tool schemas and replayed context. Existing saved budgets at or below the former 180,000 default are migrated to `autoCompactRequestTokens: 200000` in the configuration file. When a custom session context window is set, its compaction target is 70% of that window instead: for example, 253,000 → 177,100 and 1,000,000 → 700,000. Both targets remain subject to output, safety, and compaction headroom: without a custom window, a 200,000-token model has a 156,992-token trigger, while a sufficiently large model reaches the full 200,000 default target. Dispatch also reserves the actual requested completion budget, including expanded continuations, and attempts compaction if the request no longer fits. Unusable summaries preserve the original history; clai will not silently delete context to force an oversized request through.
526
-
527
- Same-route follow-ups retain previously sent request state, and supported cache-affinity keys stay stable for the session. Compaction reuses the previous request prefix when it fits and makes at most one generation attempt; rejected, truncated, or unusable summaries retain the original history rather than silently issuing another request. Cache hits still depend on the provider's expiry, routing, and supported caching behavior. Changing models or replacing history with a compacted summary can require a new cache prefix.
528
-
529
- Recovery instructions and responder result ledgers append changed snapshots without deleting previously sent messages; identical updates are not duplicated. This shared history behavior applies to AgentRouter and other compatible providers. Custom OpenAI-compatible providers can declare `profile.cache.kind: "affinity-key"` with an `affinityField` and optional `isolationField` supported by their API. Chat Completions and Responses requests populate those fields with stable session keys; unrelated request controls cannot be replaced by cache fields. API compatibility alone does not imply support for every provider-specific caching option.
530
-
531
- Anthropic, AWS Mantle, and Gemini keep changing request context in conversation order rather than moving it ahead of all prior messages. Anthropic-compatible requests use bounded conversation cache checkpoints in addition to the stable system checkpoint. The cache percentage is cumulative cached input divided by cumulative input tokens for requests with cache accounting, not the percentage of the session still held in a provider cache. A few uncached requests can lower that percentage, and later hits improve it gradually rather than immediately restoring its previous value.
532
-
533
- Long-session memory optimizations preserve conversation and transcript content: rendering caches have size-aware eviction, terminal reattachment uses a fixed-size ring with the existing replay limit, and history recovery scans records incrementally. Bounded output previews and diagnostic excerpts own their text instead of retaining the full backing strings of large tool outputs. These are storage and rendering optimizations, not additional model-context truncation.
534
-
535
- Compatible providers negotiate their API before sending the conversation: a short synthetic request uses the production serializers, authentication/custom headers, streaming mode, and reasoning/tool-control options, with at most 512 output tokens. Optional Responses fields are negotiated on these small requests. If Responses exposes no visible reasoning, a small Chat request checks whether Chat actually exposes it; clai prefers Chat only with positive evidence, otherwise retaining Responses. Encrypted/private reasoning and reasoning-token counts are not treated as visible text, and neither API can guarantee reasoning text on every answer.
536
-
537
- Preflight uses no conversation history, images, or original tool schemas and has separate cache affinity. Choices are shared for matching provider, endpoint, model, credentials, headers, mode, and options; concurrent callers share negotiation. Observations expire after 30 minutes for new sessions, while active sessions retain their choice to protect cache prefixes. Cancellation, authentication/rate-limit failures, timeouts, and server errors do not mark a route unsupported. A cold negotiation uses at most three small requests with a shared 30-second deadline; these are billable provider calls, not conversation generations, and do not replace the real request's usage/context reporting. Small probes validate basic protocol/control support, not every image format, tool schema, or replay artifact.
538
-
539
- Successful real Responses API requests are retained even when the provider does not expose reasoning; clai does not regenerate an answer or silently change protocols merely to obtain visible thinking. Ordinary requests do not evict earlier tool interactions just because their accumulated arguments cross a rolling size threshold. Explicit compaction and context admission checks still apply.
540
-
541
- When switching providers or models, incompatible reasoning and interrupted native tool transactions are projected into compatible history without deleting the original conversation. Tool results remain available as text where native replay is unsafe, while compatible new tool transactions stay native. Known text-only GLM-5 routes do not receive image parts or the `image.view` tool. If a route rejects image input, clai can retry once without images and tells both the user and model that the image contents are unavailable; the original attachments remain available when switching back to a vision-capable model.
249
+ - **Thinking blocks:** Clickable `✦ Thought for 3.2s` rows open internal reasoning in a scrollable card (`Ctrl+T`). Pressing `c` copies the reasoning text.
250
+ - **Tool cards:** Show running commands with live elapsed timers, status indicators, and expandable output pagers (`Ctrl+O`) with search and copy capabilities.
251
+ - **File diffs:** Edits and writes render clean inline diff previews before changes take effect.
542
252
 
543
253
  ### Background sessions, minimise, and SSH reattach
544
254
 
545
- Normal persistent interactive launches use a per-session local PTY broker. The UI and agent run inside that broker-owned terminal, while the shell process you interact with is a disposable client. This keeps the exact same live UI, pending confirmation, queue, tools, MCP connections, and terminal subprocesses alive when the client goes away.
546
-
547
- - `/minimise` (also `/minimize`) detaches immediately and returns the shell prompt without cancelling the current turn. It prints the session id and the exact `clai --resume <id>` command.
548
- - An SSH or terminal disconnect has the same detach behavior. SSH into the VM again as the same OS user and run `clai --resume <id>`; `clai -c` prefers the newest live session in the current directory.
549
- - `/history` marks running, attached, and detached sessions. Selecting another session attaches or starts its own runtime instead of loading over a running agent, so the outgoing agent continues independently.
550
- - One terminal controls a session at a time. Attaching from another terminal safely transfers control and releases the old terminal; this prevents duplicate keystrokes while still allowing cross-computer SSH takeover.
551
- - The broker stores only bounded terminal replay (2 MiB), uses an authenticated user-only Unix socket or Windows named pipe, and keeps runtime metadata/locks user-readable only. It never opens a TCP port.
552
- - A detached runtime stays alive while a turn, compaction, queued prompt, or responder job is active. Once truly idle and detached, it exits after 30 minutes by default; the saved session still resumes normally without keeping idle processes forever.
553
- - `--no-history`, private mode, non-TTY/one-shot runs, and systems without a usable PTY retain the existing direct foreground behavior. `/minimise` reports that background detach is unavailable instead of partially detaching.
554
-
555
- Optional controls:
255
+ Interactive sessions run behind a local broker so an agent continues working across terminal disconnects:
556
256
 
557
- ```sh
558
- CLAI_SESSION_RUNTIME_IDLE_MS=1800000 # detached idle lifetime (1 minute–24 hours)
559
- CLAI_SESSION_RUNTIME_MAX_IDLE=2 # idle-detached LRU cap (1–256; 0 disables)
560
- CLAI_DISABLE_SESSION_RUNTIME=1 # force legacy direct foreground ownership
561
- ```
257
+ - **`/minimise`** (or `/minimize`) detaches immediately and returns you to your shell without interrupting the turn. It displays the session ID and resume command.
258
+ - **SSH disconnects:** If an SSH session drops, reconnect and run `clai --resume <id>` (or `clai -c` to continue the latest session in the current directory).
259
+ - **`/history`** lists all active, attached, and detached sessions.
562
260
 
563
261
  ---
564
262
 
@@ -567,72 +265,24 @@ CLAI_DISABLE_SESSION_RUNTIME=1 # force legacy direct foreground ownership
567
265
  | Command | Does |
568
266
  |---------|------|
569
267
  | `/ask` · `/agent` · `/plan` | Switch mode (plan = design-then-approve) |
570
- | `/implement` · `/discard` | Approve+execute or drop the current plan |
571
- | `/model [name\|#]` · `/provider [name]` | Pick model / switch provider |
572
- | `/set [provider]` · `/unset [provider]` · `/keys` · `/info [provider]` | Manage API keys, endpoint URLs, and view provider setup / pricing info |
573
- | `/effort [level]` · `/reasoning [level]` | Thinking / reasoning effort |
268
+ | `/implement` · `/discard` | Approve and execute or drop the current plan |
269
+ | `/model [name]` · `/provider [name]` | Select model / switch provider |
270
+ | `/set [provider]` · `/unset [provider]` · `/keys` | Manage API keys and view provider configuration |
271
+ | `/effort [level]` · `/reasoning [level]` | Configure thinking / reasoning effort |
574
272
  | `/freeonly [on\|off]` · `/fallback [on\|off]` | Free-only filter · cross-provider fallback |
575
273
  | `/search [provider]` · `/search-provider` | Choose web-search backend |
576
- | `/mcp [server\|all\|off\|list\|status\|tools\|locations\|refresh\|stop <server>\|start <server>\|login <server>\|add notion]` | Browse/select MCP servers, stop or restart one, sign in with OAuth, or connect official Notion MCP |
577
- | `/scope [show\|add\|new\|clear]` | Engagement scope |
578
- | `/output [last\|id\|list]` | Open full tool output (also `Ctrl+O`) |
579
- | `/jobs` | Background jobs (also `Ctrl+J`) |
580
- | `/orchestrator [on\|off\|status\|models]` · `/orchestration` | Show delegation status, configure subagent model fallbacks, or turn subagents on/off |
581
- | `/agents [id\|stop id\|restart id]` | Pick a subagent to inspect live output, or stop/restart its assignment |
582
- | `/compact` · `/context` | Compact history now · show context size |
583
- | `/history` · `/save <name>` · `/new` · `/clear` · `/reset` | Session lifecycle (`/clear` deletes the current session outright) |
584
- | `/allow <tool>` · `/disallow <tool>` · `/permissions` | Tool permissions |
274
+ | `/mcp [...]` | Browse, configure, start, or stop MCP servers |
275
+ | `/scope [show\|add\|new\|clear]` | Manage engagement scope |
276
+ | `/output [last\|id\|list]` | Open full tool output pager (also `Ctrl+O`) |
277
+ | `/jobs` | View background jobs (also `Ctrl+J`) |
278
+ | `/compact` · `/context` | Compact history now · show context token size |
279
+ | `/history` · `/save <name>` · `/new` · `/clear` | Session lifecycle management |
280
+ | `/allow <tool>` · `/disallow <tool>` · `/permissions` | Tool permission management |
585
281
  | `/cwd <path>` | Change working directory |
586
282
  | `/think` · `/thinking` | Show thinking from the last response |
587
- | `/privacy [...]` | Private mode · clear history/logs/artifacts |
588
- | `/minimise` · `/minimize` | Detach this terminal while the live session continues in the background |
589
- | `/update` · `/help` · `/shortcuts` · `/exit` | Housekeeping |
590
-
591
- Orchestration is enabled in new and restored sessions. `/orchestrator`,
592
- `/orchestration`, and `/orchastrator` show the current status and open a picker
593
- with **On**, **Off**, and **Models…**. `/orchestrator status` prints the status
594
- without changing it, `/orchestrator on` / `/orchestrator off` set it directly,
595
- and `/orchestrator models` opens the subagent model editor. The active position
596
- determines the first eligible model; other enabled rows are fallbacks in rotation
597
- order. With no saved chain, subagents follow the parent session route. A fallback
598
- is used only for a retryable pre-output failure, and model-chain changes apply to
599
- new attempts.
600
- Turning orchestration off stops active children and blocks new starts and
601
- restarts for this session; turning it back on re-enables delegation. `/agents`
602
- opens the separate live inspector. `/agents` opens the same live inspector in
603
- Classic and OpenTUI: select a child, press `Esc` to return to the picker, then
604
- select another child or **Main agent**. Inspecting output never interrupts the
605
- main turn. The pager follows live output; `l` toggles follow. Turning
606
- orchestration off stops active children and prevents new starts and restarts.
607
- `/agents stop <id>` stops an individual child. `/agents restart <id>` reruns the
608
- assignment when orchestration is enabled. Saved child reports can be inspected
609
- after resuming their parent session; `--no-history` keeps child records in
610
- memory only.
611
-
612
- The last usable child summary is stored separately from activity and survives
613
- stop, restart, and compaction. `subagent.read` with `view=summary` recovers it with
614
- its original attempt and completion status; a prior summary never marks a stopped
615
- attempt complete. Undelivered terminal results return through the parent inbox on
616
- session restore, and acknowledged results are not replayed on later restores.
617
-
618
- Children have no fixed concurrency, step, or assignment-time budget. By default,
619
- they inherit the parent's provider/model and project root; a configured subagent
620
- model chain selects a primary execution route and fallbacks without changing the
621
- assignment identity. Children retain independent histories and stable cache
622
- prefixes. Their only tools are confined file reads/listings/searches and web
623
- search/fetch; shell, editing, MCP, arbitrary HTTP actions, and further delegation
624
- are unavailable. Provider context and transport safety limits still apply. Context
625
- compaction retains evidence and continues research without displaying internal
626
- partial reports as deliverables.
627
-
628
- The parent leaves delegated investigations to their children and continues only
629
- necessary non-overlapping work. Results arrive automatically at safe model
630
- boundaries or wake an idle parent. When no independent work remains,
631
- `subagent.wait` suspends without polling or a default deadline; omit the ID to
632
- receive whichever child completes, fails, or stops first. Healthy work should not
633
- be cancelled for slowness or because the parent repeated it. Reports are retained
634
- whole and paged with `subagent.read`; use the returned attempt and `nextOffset`
635
- to continue reading. Activity reads default to three recent events.
283
+ | `/privacy [...]` | Private mode · clear history, logs, or artifacts |
284
+ | `/minimise` · `/minimize` | Detach terminal while the session runs in background |
285
+ | `/update` · `/help` · `/shortcuts` · `/exit` | Utilities and exit |
636
286
 
637
287
  ---
638
288
 
@@ -644,49 +294,34 @@ clai [prompt...] # interactive UI, or one-shot with a prom
644
294
  -y/--yes --no-history
645
295
  --show-thinking --verbose --quiet # one-shot stream controls
646
296
  --tui --classic
647
- --ui <tui|v2|opentui|classic|legacy|ink> # aliases; ignored with a prompt
648
- --resume <sessionId> -c/--continue # reattach live or reopen saved session; ignored with a prompt
297
+ --resume <sessionId> -c/--continue # reattach live or reopen saved session
649
298
 
650
- clai set <provider> [key] # --from-env <VAR> | --stdin | --url <url> (repeatable) | --skip-ping
651
- clai unset <provider> # remove all keys for a provider (--url = endpoint URLs instead)
652
- clai keys # list providers with masked keys + endpoint URLs
299
+ clai set <provider> [key] # --from-env <VAR> | --stdin | --url <url>
300
+ clai unset <provider> # remove all keys for a provider
301
+ clai keys # list providers with masked keys
653
302
  clai use <provider> # set active provider
654
303
  clai provider [provider] # switch provider or open picker
655
304
  clai model <model> # set model for the active provider
656
305
  clai mode <ask|agent|plan> # set default mode
657
306
  clai search-provider <brave|tavily|duckduckgo>
658
- clai config [key] [value] # print / get / set config
307
+ clai config [key] [value] # view or update configuration
659
308
  clai doctor # check installed tools + provider config
660
- clai history [--show <id>] # list sessions / print one (with resume commands)
661
- clai update # check for updates
309
+ clai history [--show <id>] # list saved sessions
310
+ clai update # check for updates and upgrade
662
311
  clai authorize-pentest AGREE # enable scan/attack tools (one-time ack)
663
- clai scope <show|new|add|clear> # engagement scope (new: --targets --exclude --phases
664
- # --name --note --expires --max-rate --max-concurrency)
665
- clai privacy <status|on|off|retention|clear-history|clear-logs|clear-artifacts|clear-all>
312
+ clai scope <show|new|add|clear> # engagement scope management
313
+ clai privacy <status|on|off|clear-all> # privacy and history clearing
666
314
  ```
667
315
 
668
- ### One-shot output and exit status
669
-
670
- `clai "prompt"` and piped prompts use the noninteractive stream renderer. The final
671
- assistant answer is written to **stdout**; progress, tool cards, diffs, thinking (when
672
- `--show-thinking` is enabled), confirmations, and errors are written to **stderr**. `--quiet`
673
- suppresses progress while retaining the final answer on stdout, and `--verbose` expands tool
674
- output and diff hunks. `--no-history` disables persistence for that run.
675
-
676
- A completed, partially completed, blocked, or failed turn returns exit code `0`; an aborted
677
- turn returns `130`; an unhandled or loader error returns `1`. While one-shot work is running,
678
- press `Esc` or `Ctrl+C` to abort it, including commands launched through `bun run dev`.
679
- `--show-thinking` can also be enabled with `CLAI_SHOW_THINKING=1`.
680
-
681
316
  ---
682
317
 
683
318
  ## Model Context Protocol (MCP)
684
319
 
685
- clai can discover and call tools from local or remote MCP servers. MCP tools are **off by default** and are not sent to the model until a prompt mentions a server or the session is switched on with `/mcp all`. Run `/mcp` to discover configured servers and open the shared picker in Classic or OpenTUI. Picking a server inserts an aqua `@mcp:<server>` token into the composer text, exactly like a `skill:` mention: it is ordinary editable text, so backspace removes it, and several tokens select several servers for that prompt. `all` and `off` insert no token.
320
+ `clai` can discover and call tools from local or remote MCP servers. MCP tools are **off by default**; they are activated when a prompt mentions `@mcp:<server>` or via `/mcp all`.
686
321
 
687
322
  ### Project configuration
688
323
 
689
- The native project file is `.clai/mcp.json`. It accepts JSON or JSONC and the common `servers` / `mcpServers` shapes. A minimal configuration can mix stdio and Streamable HTTP servers:
324
+ Define MCP servers in `.clai/mcp.json` (or standard `.vscode/mcp.json` / `.cursor/mcp.json` locations):
690
325
 
691
326
  ```json
692
327
  {
@@ -708,38 +343,7 @@ The native project file is `.clai/mcp.json`. It accepts JSON or JSONC and the co
708
343
  }
709
344
  ```
710
345
 
711
- A `command` entry uses stdio. A `url` entry uses Streamable HTTP by default; set `"type": "sse"` for a legacy SSE endpoint. Remote HTTP/SSE entries with no `auth` block use OAuth discovery when the server requests authorization; set `"auth": {"kind": "none"}` to opt out explicitly for a public server. `${workspaceFolder}`, `${env:NAME}` / `${env.NAME}`, and compatible `${input:name}` substitutions are resolved at discovery time, and resolved secret values are redacted from diagnostics.
712
-
713
- The picker can add one server without hand-editing the file: choose **+ add MCP server** and a full multiline editor opens — visible caret, arrow-key and word/line navigation, mid-text edits, selection, and multi-line paste. `Enter` inserts a newline, `Ctrl+S` saves, `Esc` cancels. If the JSON does not validate, the editor reopens with your text intact and the parse error in the header, so a long paste is never retyped. You can also supply the fragment inline:
714
-
715
- ```text
716
- /mcp add {"name":"docs","command":"docs-server","args":[]}
717
- ```
718
-
719
- The add flow accepts one named object or a one-entry `servers` / `mcpServers` fragment, validates it, then atomically merges it into the current project `.clai/mcp.json` with deterministic server ordering and restrictive permissions. Existing unrelated metadata is preserved.
720
-
721
- clai also inherits compatible configuration from `CLAI_MCP_CONFIG` (an OS-delimited file list), ancestor `.clai/mcp.json` and `.mcp.json` files, repository `.github/mcp.json` and `.vscode/mcp.json`, clai user config, and supported Copilot, Claude, and VS Code user locations. Higher-precedence definitions deterministically shadow inherited servers with the same name. `/mcp locations` shows the exact project target and inherited paths for the current workspace.
722
-
723
- ### Select and inspect servers
724
-
725
- ```text
726
- /mcp # picker: add, off, all, or one server
727
- /mcp docs # drop @mcp:docs into the prompt (repeat for more servers)
728
- /mcp all # expose tools from every live server
729
- /mcp off # default: expose no MCP tools
730
- /mcp list # sources, transports, state, selection, and catalog
731
- /mcp status # live, failed, and invalid server details
732
- /mcp tools [server] # canonical mcp.<server>.<tool> names
733
- /mcp locations # project and inherited configuration paths
734
- /mcp refresh # rediscover configs and live tools
735
- /mcp reconnect docs # restart one server connection
736
- /mcp stop docs # shut the server down and drop its tools from every request
737
- /mcp start docs # bring a stopped server back
738
- /mcp login docs # run OAuth browser sign-in, store the token, reconnect
739
- /mcp add notion # add https://mcp.notion.com/mcp and sign in
740
- ```
741
-
742
- Static built-in tools always remain first; selected MCP definitions are appended in deterministic order as `mcp.<server>.<tool>`. Each schema is normalised once — documentation-only keywords dropped, descriptions bounded, keywords canonically ordered — and sent exactly once per request in the native function payload rather than repeated as prompt text, so a large server such as GitHub MCP costs a stable, cacheable prefix instead of tens of thousands of duplicated tokens per turn. Selection is never silently widened or dropped mid-refresh, and `/mcp stop <server>` closes the connection and removes its tools from every subsequent request until `/mcp start <server>`. A tool explicitly annotated read-only can run under the normal safe/parallel policy. Unmarked, mutating, or destructive MCP tools require the usual confirmation, and ask mode exposes only safe tools. Server descriptions and results are treated as untrusted data, secrets are redacted, response sizes and lifecycles are bounded, and HTTP credentials are never forwarded through redirects.
346
+ Use `/mcp` inside the interactive console to browse servers, inspect available tools, view logs, restart connections, or add new servers interactively.
743
347
 
744
348
  ---
745
349
 
@@ -756,31 +360,15 @@ Static built-in tools always remain first; selected MCP definitions are appended
756
360
  | **Plan** | `plan.create` · `task.update` · `agent.handoff` |
757
361
  | **Context** | `sysinfo` · `image.ocr` · `pdf.read` |
758
362
 
759
- ### tool.batch fail policy
760
-
761
- Default is **continue** — one failed lookup never kills the rest (ideal for recon). Opt into fail-fast or selective cancellation when later work depends on earlier success:
762
-
763
- ```json
764
- {"name":"tool.batch","args":{
765
- "on_fail":"cancel_pending",
766
- "calls":[
767
- {"name":"net.scan","args":{"target":"lab.example"}},
768
- {"name":"http.fetch","args":{"url":"https://lab.example/"}}
769
- ]
770
- }}
771
- ```
772
-
773
- Top-level messages with several separate tool blocks never cancel siblings — use `tool.batch` when you need a fail policy.
774
-
775
363
  ---
776
364
 
777
365
  ## Web search / OSINT
778
366
 
779
- | Provider | Key | Env var |
780
- |----------|-----|---------|
781
- | DuckDuckGo | none (default) | — |
782
- | Brave | required | `BRAVE_SEARCH_API_KEY` |
783
- | Tavily | required | `TAVILY_API_KEY` |
367
+ | Provider | Key | Environment Variable |
368
+ |----------|-----|----------------------|
369
+ | **DuckDuckGo** | None (default) | — |
370
+ | **Brave** | Required | `BRAVE_SEARCH_API_KEY` |
371
+ | **Tavily** | Required | `TAVILY_API_KEY` |
784
372
 
785
373
  ```sh
786
374
  clai set brave bsx-...
@@ -788,41 +376,37 @@ clai set tavily tvly-...
788
376
  clai search-provider tavily
789
377
  ```
790
378
 
791
- Search-provider keys are multi-key and rotate just like model providers.
792
-
793
379
  ---
794
380
 
795
381
  ## Per-project context
796
382
 
797
- Drop a `.clai/context.md` in a repo and its contents are injected every turn — lab topology, in-scope hosts, stack assumptions, coding conventions, or anything the agent should always know for that project. Project-scoped MCP servers belong in `.clai/mcp.json`; use `/mcp locations` to see that target plus every inherited compatible source.
383
+ Drop a `.clai/context.md` in any project root, and its content is injected automatically on every turn — repo architecture, stack conventions, testing instructions, or scope rules.
798
384
 
799
385
  ---
800
386
 
801
387
  ## Interactive REPLs and terminals
802
388
 
803
- Ask clai to use an interactive terminal whenever a program needs more than one input step. It keeps the process attached to the current conversation instead of treating it like a one-line shell command:
389
+ When a task requires multi-step interactive terminal interactions, `clai` maintains a persistent terminal session attached to the conversation:
804
390
 
805
391
  ```text
806
- Start a Python REPL, inspect the failing parser, make a small change, and show me the result.
392
+ Start a Python REPL, test the regular expression against our test cases, and show me the output.
807
393
  ```
808
394
 
809
- The agent uses `terminal.start`, `terminal.send`, `terminal.read`, `terminal.status`, `terminal.list`, `terminal.resize`, and `terminal.close` internally. Sessions are isolated per conversation, capture output as cursor-addressed pages, redact prompted secret input before it reaches transcripts or artifacts, and reliably terminate their process trees on close. In plan mode, starting or sending terminal input waits for plan approval; read, status, list, and close operations remain available.
810
-
811
- For authorized security work, interactive effects are re-evaluated against the active engagement scope at the time they are delivered. That includes the target, port, phase, and expiry accumulated from prior REPL commands, so a split sequence cannot bypass scope controls.
395
+ The agent runs interactive commands in persistent PTY sessions, reads incremental output, sends follow-up commands, and safely terminates processes upon completion.
812
396
 
813
397
  ---
814
398
 
815
399
  ## Configuration & privacy
816
400
 
817
401
  ```sh
818
- clai config # view config
819
- clai mode agent # default mode
820
- clai model <name> # default model for the active provider
402
+ clai config # view current config
403
+ clai mode agent # set default mode
404
+ clai model <name> # set default model
821
405
  /privacy on # private mode: don't persist this session
822
406
  /privacy clear-all # wipe history, logs, and artifacts
823
407
  ```
824
408
 
825
- Config lives under your OS user config dir (e.g. `~/.config/clai/`). Keys are stored locally and shown only masked. Inside an interactive console, `/provider`, `/model`, and `/models` change only that session and are restored with it; the explicit `clai use`, `clai provider`, and `clai model` CLI commands continue to set global defaults.
409
+ Config is stored locally in your OS user directory (e.g. `~/.config/clai/`). Keys are stored locally and never exposed in plain text.
826
410
 
827
411
  ---
828
412
 
@@ -833,31 +417,12 @@ npm install
833
417
  npm run dev # run from source
834
418
  npm run typecheck
835
419
  npm run build
836
- npm test # full Vitest suite
837
- npm run test:classic:pty # provider-independent POSIX PTY smoke
838
- npm run compile # native binaries (Bun)
839
- npm run release:verify
840
- npm pack --dry-run
420
+ npm test # full test suite
421
+ npm run compile # compile native binaries with Bun
841
422
  ```
842
423
 
843
424
  ---
844
425
 
845
- ## Releasing
846
-
847
- Tag-driven CI (`.github/workflows/release.yml`): validate (typecheck + tests + release checks) → multi-platform binaries → GitHub Release → npm `@pentoshi/clai` → Homebrew tap.
848
-
849
- ```sh
850
- # package.json "version" is the single source of truth.
851
- npm version 3.8.0 --no-git-tag-version
852
- npm run sync-version # refreshes version.generated.ts + install manifests + lockfile
853
- git commit -am "v3.8.0" && git push origin main
854
- git tag -a v3.8.0 -m "clai v3.8.0" && git push origin v3.8.0
855
- ```
856
-
857
- Secrets: `NPM_TOKEN`, `TAP_GITHUB_TOKEN`. Optional: `NPM_PROVENANCE=true`.
858
-
859
- ---
860
-
861
426
  ## Architecture
862
427
 
863
428
  ```
@@ -865,7 +430,7 @@ clai/
865
430
  ├─ src/
866
431
  │ ├─ index.ts # CLI entry + subcommands
867
432
  │ ├─ agent/ # loop, plans, compaction, resume orientation, tool parsing
868
- │ ├─ llm/ # 19 providers, streaming, native tools, key rotation + fallback
433
+ │ ├─ llm/ # providers, streaming, native tools, key rotation + fallback
869
434
  │ ├─ mcp/ # discovery, validation, transports, lifecycle, and tool dispatch
870
435
  │ ├─ tools/ # fs, shell, net, http, web, pentest, batch, plan
871
436
  │ ├─ safety/ # risk classifier + engagement (scope) policy