A fast, lightweight AI coding agent for your terminal.
Pure Rust, no Node.js, tiny binary, built for Termux.
Laudacode is an AI agent that lives in your terminal. It reads your project, edits files, runs commands and fetches web docs β all under your approval β using any OpenAI-compatible API: OpenAI, OpenRouter, Groq, DeepSeek, Together, Ollama, LM Studio, llama.cpp server, vLLMβ¦
- β‘ Pure Rust + Tokio + reqwest (rustls) β one small, nearly static binary, built for Android/Termux
- π Any OpenAI-compatible endpoint β custom
base_url/api_key/model - π§ Agentic tool loop β
list_dir,read_file,write_file,edit_file,apply_patch,run_command,fetch_url,web_search,grep,glob,git,update_plan - π Regex search β
grepis literal by default, pass"regex": truefor a pattern; honorsignore_case,context, and has a compiled-size cap - π Local git β one
gittool, fixed local allowlist, direct argv, no shell. Read:status,log,diff,show,blame. Write:add,commit,branch,checkout,stash. Nopush/fetch/resetβ userun_commandfor those, so the dangerous-command detector still sees them - π§© MCP tool servers β
[mcp_servers.<name>]spawns any stdio MCP server and exposes its tools asmcp__<server>__<tool>; paged listings, per-request timeouts, name-collision rejection, and/mcpfor status. Third-party tools ask for consent in BUILD/PLAN unless FULL AUTO - π Parallel tool calls β independent reads (
read_file,grep,glob,fetch_url,web_search, git reads) in one assistant turn run concurrently; results stay in call order, and every write still runs sequentially behind its approval - πͺ Post-edit hooks β
[hooks] post_editruns formatters/linters/tests after every file mutation; exit status and output go back to the model - πΈ Token & cost guardrails β
[limits] max_tokens/max_cost_usd;/statusshows what's left. Cost is priced per model (input and output separately) from a built-in table, overridable with[pricing]; sub-agent spend counts too - π§Ύ Structured logs β optional JSONL trail of tool calls, usage, hook results, budget blocks; never prompts or file contents
- π°οΈ Proxy & custom CAs β
[network] proxy(http/https/socks5),ca_bundle, explicitinsecure - π¦ Patient about flaky networks β up to 5 retries by default on dropped connections and 408/409/429/5xx, jittered exponential backoff honoring
Retry-After. Waits show in the status line,Esccancels instantly, tune with[limits] max_retries - βοΈ Managed processes β
start_process/poll_process/write_process/stop_processfor dev servers, watchers, REPLs; 16 per agent, 8 KiB per stream, process-group cleanup - π Web built in β
fetch_urlplusweb_search(DuckDuckGo) - π‘οΈ Approval modes β
suggest,auto-edit,full-auto, with confirmation for dangerous commands - π Per-tool permissions β
[permission.bash|edit|read|webfetch|mcp]maps a glob to a rule, last match wins:"*" = "ask","git *" = "allow","rm *" = "deny". Unmatched tools fall back to the danger heuristics. A prompt is always a no: Enter and EOF deny - π€ Sub-agents β
delegatefans a task out to specialists (planner,researcher,coder,reviewer,tester) running concurrently with their own contexts and reduced toolsets βrevieweris read-only./agentslists them - π Skills β markdown playbooks in
~/.config/laudacode/skills/<name>/SKILL.mdor.laudacode/skills/<name>/SKILL.md;/skillsopens a searchable picker and stages the pick into the composer - π Custom slash commands β drop a markdown file in
.laudacode/commands/;$ARGUMENTSand$1β¦$9expand at call time - πΌοΈ Image input β screenshots and photos for vision models (
-i,/image) - π‘ Streaming responses with reasoning-model support
- π¨ 17 color themes β lauda, cherry, midnight, nord, dracula, monokai, solarized, gruvbox, tokyo, everforest, ember, ice, hacker, catppuccin, rosepine, ayu, onedark (
/theme) - β¨ Ambient effects β petals, rain, snow, matrix rain, lightning, stars, fireflies, bubbles, embers, confetti, comets, aurora, leaves, drizzle, rainbow, pulse (
/effect, banner only) - π Syntax highlighting β code blocks and diffs, per language (rust, python, js/ts, go, c/cpp, java, sh, toml, yaml, json)
- β¨οΈ Prompt history β β/β recall with draft restore, persisted
- π¬ Slash commands & input sugar β
/provider,/model,/diff,/review,/undo,/compact,/init,/status,/export,/resume,/retryand more, plus@file,#noteand!<cmd> - β©οΈ Multi-turn undo β
/undo Nreverts the last N agent turns - π° Session cost tracking β cumulative tokens and estimated USD in
/status - π± Adaptive mobile UI β banner art collapses to a one-line header on small screens
- π Touch-native β swipe to scroll, tap picker rows, tap the composer to move the caret
- π οΈ Live activity feedback β footer shows
reasoning/streaming/running <tool>plus elapsed time - π AGENTS.md support β project instructions auto-loaded (
/initgenerates one) - π€ Profiles β named presets in
[profiles.<name>], via--profile - πΎ Session persistence β
--continue,--resume <id>,/resume; ids, unique prefixes and names all work - πΏ Checkpoints & branching β
/checkpoint [label],/checkpoints,/branch <n|id> - π₯οΈ One-shot mode β
laudacode exec "fix the failing test" - π¦ JSON output β
--jsonemits machine-readable event lines
curl -fsSL https://raw.githubusercontent.com/Anon4You/Laudacode/main/install.sh | shInstalls the latest GitHub release (auto-detected), builds it on-device and
puts laudacode in $PREFIX/bin β no sudo inside Termux. Needs curl, tar,
rust (Termux: pkg install curl tar rust). Pin an older release with
LAUDACODE_VERSION=v0.2.0.
pkg update && pkg install rust git -y
git clone https://github.com/Anon4You/Laudacode.git
cd Laudacode
cargo build --release
cp target/release/laudacode $PREFIX/bin/Low-RAM phone?
CARGO_PROFILE_RELEASE_LTO=off cargo build --release
cargo install --locked --git https://github.com/Anon4You/Laudacode
# or from a clone:
cargo install --locked --path .export OPENAI_API_KEY="sk-or-v1-..." # any OpenAI-compatible key
export OPENAI_BASE_URL="https://openrouter.ai/api/v1"
export OPENAI_MODEL="stealth/ox-alpha"
laudacode # interactive REPLOr skip env vars and configure from inside the TUI: first run has no wizard,
just type /provider. It opens an interactive menu (add Β· use Β· edit Β· list):
pick add, choose a preset (openrouter, openai, groq, deepseek, together,
cerebras, mistral, xai, fireworks, sambanova, deepinfra, nvidia, upstage,
minimax, huggingface, githubmodels, ollama, lmstudioβ¦), paste your key, pick a
model from the live catalog. Nothing is saved until a live test request proves key and model
work β same rule for the CLI flow.
Keyless free providers β powerbrain and aitopia need no API key and no
model picker: picking one saves immediately with a built-in default model.
aitopia is the built-in default, so with nothing configured Laudacode works
out of the box, chat-first.
laudacode provider add # guided setup (name, url, key, model)
laudacode provider list
laudacode provider use tokenrouter
laudacodeOne-shot tasks:
laudacode exec "explain what this repo does"
laudacode exec "add input validation to src/main.rs" --mode full-autoPrecedence: CLI flags > profile (--profile) > environment variables > config file.
| Variable | Meaning |
|---|---|
OPENAI_API_KEY |
API key |
OPENAI_BASE_URL |
e.g. https://api.groq.com/openai/v1 |
OPENAI_MODEL |
model name |
Config file at ~/.config/laudacode/config.toml
(or .json; override location with LAUDACODE_CONFIG):
active_provider = "openrouter"
approval_mode = "suggest"
[providers.openrouter]
base_url = "https://openrouter.ai/api/v1"
api_key = "sk-or-v1-..."
model = "stealth/ox-alpha"
[providers.openrouter.headers] # optional custom headers
"HTTP-Referer" = "https://github.com/Anon4You/Laudacode"
"X-Title" = "Laudacode"
[profiles.fast] # optional presets β laudacode --profile fast
provider = "groq"
model = "llama-3.3-70b-versatile"
[limits] # stop runaway loops
max_tokens = 500000
max_cost_usd = 5.0
[pricing."anthropic/claude-sonnet-4"] # USD per 1M tokens; beats the built-in table
input = 3.0
output = 15.0
[hooks] # run after every file mutation
post_edit = ["cargo fmt", "cargo clippy --quiet -- -D warnings"]
post_edit_timeout_secs = 30
[logging] # JSONL trail, no prompts or file contents
enabled = true # no `file` β stderr
file = "~/.local/share/laudacode/session.jsonl"
[network] # applies to every outgoing request
proxy = "http://127.0.0.1:8080" # http / https / socks5
ca_bundle = "~/.config/laudacode/corp.pem"
# insecure = true # skip TLS verification (risky)Post-edit hooks get the touched paths in $LAUDACODE_CHANGED_FILES
(space-separated). Each hook's exit status and first output line go back to the
model, so a failing test run is something it can see and fix. Only
file-mutating tools trigger them.
Provider presets (OpenAI, OpenRouter, Groq, DeepSeek, Cerebras, Mistral, xAI,
Fireworks, SambaNova, DeepInfra, NVIDIA, Upstage, MiniMax, Hugging Face, Ollama,
LM Studio) and annotated versions of every block above live in
config.example.toml.
Laudacode can host tools from any MCP
server that speaks stdio. Each configured server is spawned at startup and its
tools are exposed to the model as mcp__<server>__<tool>:
[mcp_servers.files]
command = "mcp-server-filesystem"
args = ["--root", "."]
timeout_secs = 60| Key | Default | Meaning |
|---|---|---|
command |
β | executable to spawn (required) |
args |
[] |
arguments |
env |
{} |
extra environment variables |
cwd |
agent's | working directory for the child |
enabled |
true |
false keeps the config but skips startup |
timeout_secs |
60 |
per-request budget; 0 uses the default |
plan |
false |
opt in to using these tools in PLAN mode |
A server that fails to start is reported and skipped β the rest of the
session works normally, and /mcp shows what went wrong. Tool names are
sanitized for the model but the original names are what get called, and two
servers claiming the same name are rejected rather than silently shadowing
each other.
MCP tools are third-party code whose effects Laudacode cannot inspect, so they get no danger-based auto-approval: they ask for consent in BUILD and PLAN mode, and stay silent only in FULL AUTO. Override per tool with permission rules:
[permission.mcp]
"mcp__files__delete" = "deny"
"mcp__*" = "ask"Any stdio language server works, so the model gets real compiler-grade
feedback instead of guessing. Each server claims file extensions and is
mapped to the LSP languageId it expects:
[lsp_servers.rust]
command = "rust-analyzer"
filetypes = { rs = "rust" }
[lsp_servers.c]
command = "clangd"
filetypes = { c = "c", h = "c" }| Key | Default | Meaning |
|---|---|---|
command |
β | executable to spawn (required) |
args |
[] |
arguments |
env |
{} |
extra environment variables |
filetypes |
{} |
extension (no dot) β LSP languageId |
enabled |
true |
false keeps the config but skips startup |
root |
agent's | workspace root |
timeout_secs |
90 |
per-request budget |
init_timeout_secs |
120 |
initialize handshake budget |
diagnostics_secs |
5 |
how long to wait for diagnostics to settle |
Check what is running with /lsp. Servers start at launch, so indexes are
warm before the first question; a server that fails to start is reported and
skipped without affecting anything else.
filetypes is an explicit mapping because languageId is server-specific β
pygls servers reject a wrong id β and two servers claiming the same
extension is rejected at load time rather than resolved by guesswork.
Diagnostics are waited on for at most diagnostics_secs. Analysis is often
asynchronous: rust-analyzer republishes a file's problems as its index
settles, so a first batch of zero diagnostics does not mean the file is fine.
If the budget runs out mid-analysis the result says it is still analysing
rather than reporting a clean file.
After any edit the server's diagnostics for the touched file are appended to
the tool result β a real compiler front end instead of an LLM round trip on
cargo check. Only extensions a server claims are checked.
The lsp tool exposes the same server to the model on demand for
diagnostics, definition, references, workspace symbols, and hover, gated by
your existing read rules:
{ "action": "diagnostics", "path": "src/main.rs" }
{ "action": "definition", "path": "src/main.rs", "line": 12, "column": 5 }
{ "action": "references", "path": "src/lib.rs", "line": 30, "column": 9 }
{ "action": "symbols" }
{ "action": "hover", "path": "src/main.rs", "line": 5, "column": 1 }Positions are 1-based like read_file line numbers, and are converted to the
protocol's 0-based UTF-16 units internally, so an emoji earlier in the line
does not shift a column.
Default is BUILD (auto-edit).
--mode value |
TUI label | File edits | Shell commands | Dangerous commands |
|---|---|---|---|---|
suggest (alias ask) |
PLAN | ask | ask | ask |
auto-edit (default) |
BUILD | β auto | ask | ask |
full-auto (alias yolo) |
FULL AUTO | β auto | β auto | ask (always) |
Answer [a]lways on any prompt to auto-approve the rest of the session. In the
TUI, switch modes any time with /approvals or Tab.
laudacode # interactive session
laudacode "quick question" # one-shot prompt
laudacode exec "<task>" # same as above
laudacode exec "<task>" --json # emit JSON event lines instead of prose
laudacode exec "<task>" --quiet # suppress the banner; results only
laudacode -P groq -m llama-3.3-70b-versatile
laudacode --profile fast # activate [profiles.fast] from your config
laudacode -i screenshot.png "what's wrong with this UI?"
laudacode --base-url http://localhost:11434/v1 --api-key ollama --model qwen2.5-coder:7b
laudacode -c # continue last session
laudacode -y # shorthand for --mode full-auto
laudacode provider add|list|use|edit|remove <name>
laudacode session checkpoints <id> # list a session's checkpoints
laudacode session branch <id> <checkpoint> # fork a new session from one
Exit codes: 0 success, 1 any failure (bad args, provider error, budget
exhausted, tool denial). Use --json or --quiet when capturing stdout β the
banner is suppressed automatically under --json.
| Command | Description |
|---|---|
/help |
command overview |
/model |
pick a model from the provider's live list |
/approvals |
switch approval mode (plan/build/full-auto) |
/agents |
list the sub-agents and their roles |
/reasoning |
thinking depth: low Β· normal Β· medium Β· high Β· max |
/provider β¦ |
manage providers (add list show use <name>) |
/theme |
switch color theme (live preview) |
/banner |
roll a different banner logo |
/effect |
ambient effects (petals Β· rain Β· lightningβ¦) |
/status |
provider/model/session + token, cost, budget, hook, log & network state |
/update |
check GitHub releases and install a newer binary |
/doctor |
environment diagnostics (no network, no tokens) |
/usage |
tokens & cost this session |
/archive |
park a session aside (hidden from /resume) |
/mcp |
external MCP tool servers Β· connected, failed, or disabled |
/session β¦ |
rename Β· search Β· list Β· delete sessions |
/checkpoint [label] |
snapshot the conversation as a branch point |
/checkpoints |
list this session's checkpoints |
/branch <n|id> |
fork a new session from a checkpoint |
/skills |
searchable picker β pick a skill to stage it in the composer |
/lsp |
language servers Β· running, failed, or disabled |
/diff |
git diff of working tree |
/review |
AI review of the current git diff |
/undo [N] |
revert file changes from the last N turns |
/init |
generate AGENTS.md for this project |
/compact |
summarize history to free context window |
/clear |
fresh conversation |
/retry |
re-run the previous task |
/resume |
restore a previous session by id |
/image <path> |
attach an image to your next message |
/export |
save transcript as markdown |
/quit |
exit |
A skill is a directory with a SKILL.md. The frontmatter is optional; name
defaults to the directory name and description falls back to the first body
line. Both a one-line value and a YAML block scalar are understood:
---
name: my-skill
description: > # `|` keeps the line breaks, `>` folds them to spaces
Shown in the picker and
injected into the system prompt.
---
Full instructions. Only name + description reach the model; it reads this file
with `read_file` when the skill is relevant.Input prefixes:
| Prefix | Effect |
|---|---|
@file |
attach a file β its contents are inlined into the prompt |
#note |
save a memory into AGENTS.md |
!<command> |
run a shell command locally (no agent) |
Keys: type / for autocomplete, β/β + Tab/Enter to complete,
Ctrl+O expands recent tool output, Esc interrupts the agent.
Touch / mouse β swipe or wheel to scroll the transcript, tap a picker row
to choose it (models, themes, sessions, providersβ¦), and tap inside the
composer to move the caret to that position. Ctrl+B toggles the banner, and
the hint strip under the composer adapts to narrow windows automatically.
MIT β see LICENSE.