A coding assistant that lives in your terminal. It reads your files and runs your commands — and waits for you before every one. Connects to any OpenAI-compatible LLM endpoint.
tuisample-codeis nowboxcode. Reinstall with the command below —~/.tuisample-code/andTUISAMPLE_*env vars still migrate/work automatically, but are deprecated in favor of~/.boxcode//BOXCODE_*.
◈
▟█▙ ▟█▙ boxcode v1.11.0
▜███████████▛ a terminal coding assistant
██ █████ ██
▜███████████▛ Welcome back, you!
▜█▛ ▜█▛
────────────────────────────────────────────────────────────────
model deepseek-chat
endpoint https://api.deepseek.com
cwd ~/Desktop/HolboxAI/boxcode
/provider switch provider or endpoint
/model switch model
Ask about this project — it can read files and run commands.
Destructive commands wait for your approval — deleting, force-pushing,
publishing.
╭──────────────────────────────────────────────────────────────────────╮
│❯ add a health check endpoint │
╰──────────────────────────────────────────────────────────────────────╯
↵ send · ⌥↵ newline · ↑↓ history · ^c exit
Install — macOS/Linux: curl -fsSL https://boxcode.sh/install.sh | bash
Windows (PowerShell): irm https://boxcode.sh/install.ps1 | iex
Downloads a prebuilt binary (no Rust toolchain needed) and verifies it against
a published checksum. No prebuilt for your platform? It falls back to
installing Rust and building from source (macOS/Linux only — on Windows,
install Rust yourself and run cargo build --release, or use WSL). Or build
from source directly:
git clone https://github.com/HolboxAI/boxcode && cd boxcode
cargo build --release && ./target/release/boxcodeConfigure — launch boxcode and run /provider: pick a provider, then a
model. An existing {PROVIDER}_API_KEY env var (e.g. DEEPSEEK_API_KEY) is
picked up automatically; otherwise you're prompted for it. Not on the list?
Pick "Custom endpoint..." for any OpenAI-compatible endpoint. Or skip the
picker and edit ~/.boxcode/config.toml / set BOXCODE_ENDPOINT,
BOXCODE_MODEL, BOXCODE_API_KEY directly.
Run — boxcode
Update — it checks on every startup and asks before installing anything
(default answer is no), so a pending update is mentioned every time rather than
once and then not again until tomorrow. Reinstall by hand any time with
boxcode --upgrade (always a force-install, so it also fixes a broken
install). Turn the automatic check off with BOXCODE_NO_UPDATE_CHECK=1 or
check_on_start = false under [update].
Everything below happens because you asked in plain English — there's no command for most of these, on purpose: a sentence carries the intent and the details in one message, where a flag-heavy command would need several screens to ask for the same thing.
- Reads, writes and edits your files, and runs commands in the directory
you launched from. Only destructive actions stop to ask. Building
something is dozens of ordinary steps —
mkdir,npm install,npm run build, a file written for each one — and prompting for every one of those never made the dangerous ones safer, it buried them: twenty identical prompts get answeredyby reflex, and so does the twenty-first. So deleting, force-pushing, discarding uncommitted work, killing processes, uninstalling, running as root, publishing a package and putting anything on the internet all still wait for youry/n, shown in full — and everything else just runs. Genuinely catastrophic commands (rm -rf /, disk formatting, fork bombs,curl | sh, ...) are never even offered as a yes/no — seedanger.rs; that tier is deliberately narrow, and covers destruction with no way back rather than anything merely unexpected, so writing a log to/tmpand reading it back works the way it does in any shell. Setapproval = "always"under[tools]to be asked about every write and command instead. Upgrading from an earlier version loosens your existing install: the retiredrequire_approvalkey is dropped rather than translated, because the app used to write it into every config itself, so its presence never meant anyone had chosen it. - Every file change is a diff, before and after — a write or an edit is
approved by looking at red
-and green+lines with real line numbers, against the file as it is on disk right now, not by reading a whole new file or a "replace this / with this" pair with no idea where it lands. The approved change then leaves that same diff in the transcript, so "wrote 4kb" becomes "changed these four lines". The preview is produced by the very code that applies the edit, so what you approve and what happens cannot differ. /plan— research first, get an editable plan, then approve the approach before any file changes. The plan lands asplan.mdin your project, so it's a file you own, not hidden state. Full details: docs/plan-mode.md.- Publishes a live URL, with real auth and a real database, in the same
conversation — say "publish this" and get a shareable link; say "add
sign-up and sign-in" and get real email/password auth (hosted, no account
needed); say "add a database" and the model can run SQL against a real
per-project SQLite file, scoped to the signed-in user on request. The
published page also live-reloads — a tab already open picks up your next
publish automatically. A visitor can leave a change request from the live
page itself (e.g. from their phone) that your next session picks up.
/pullswitches between any project this machine has published before. - Deploys to Vercel or Netlify — say "deploy this to vercel" and it detects your framework, builds, and hands back a live URL (or the build log, to fix and retry) — all in one approval-gated tool call. Full details: docs/deploying.md.
BOXCODE.md(orAGENTS.md) at your project root rides along with every request — run/initto have the model write one. Sessions persist to~/.boxcode/sessions/per directory;--resume//resumepicks up where you left off, even after a crash./compactsummarizes a long conversation (automatically, past a token threshold, or on demand) instead of losing it with/new.
- You can see it thinking — a reasoning model streams its chain of thought
on a separate field before a word of the answer appears. That field is now
read: the spinner says
Thinking…and the latest line of it sits underneath, where before there was a blank screen and a frozen counter for minutes at a time. The spinner also separates the round in flight from the whole turn (Responding… (12s · 152s this turn)), because a turn that rannpm installand four round trips was reporting all of it as time spent responding. - The transcript is marks, not emoji — a tool line that is still running
wears a spinner and a finished one wears a
·, so you can see which of four commands is the slow one rather than only that four are running. No pictographs anywhere: they are double-width in some terminals, single in others, and replacement boxes without an emoji font — the verb after the mark saysread/write/grepmore precisely than a picture of a page did. - Type prompt, Enter to send — Alt/Shift-Enter for a newline, Esc to
cancel, ↑/↓ to recall previous prompts, Ctrl-A/E/W/U/K for line editing,
Ctrl-C twice to exit (the first press arms it, any other key disarms).
Paste works, and while the session is running your terminal's own scrollback,
search and selection all work on the transcript. Quitting then clears it
off the terminal, leaving the shell as you found it — the conversation is
still on disk for
--resume, andclear_on_exit = falseunder[ui]keeps it on screen instead. endpointacceptshttps://host,https://host/v1, or the full chat- completions URL — all three resolve. Env vars overrideconfig.toml.
| Command | Does |
|---|---|
/plan |
Toggle plan mode (research → approve a plan → then it can write) |
/provider |
Pick a provider, then chains into a model picker |
/model |
Re-pick just the model for the current provider |
/init |
Explore the project and write/update BOXCODE.md |
/resume |
Reload this directory's last session into a fresh conversation |
/pull |
Switch to a different project this machine has published |
/new |
Start a fresh conversation (old one stays on disk for /resume) |
/compact |
Summarize the conversation now, freeing context |
/usage |
Local token usage — today, last 7 days, all time |
/rollback |
Undo every file the model wrote this session (asks first) |
/provider and /model write to ~/.boxcode/config.toml and apply
immediately, no restart needed.
/rollback undoes files, not history. Every write_file and edit_file
records what the file held first, and /rollback puts those states back —
files the model created are deleted, files it changed are restored to what it
found, and files you edited yourself are never touched. It shows the full list
and waits for a yes. What it cannot undo, it says so about: shell commands
(a build, an npm install, an rm) change the disk in ways no snapshot
covers, so the confirmation names the commands that ran instead of pretending.
The window opens when boxcode starts, survives /compact, and closes on
/new.
[tools]
enabled = true # false sends no tool schema at all
workspace = "." # "." = the directory you launched from
approval = "destructive" # what stops to ask:
# "destructive" (default) — only actions that destroy
# something or put something on the public internet
# "always" — every write and every command, sparing reads
[deploy]
enabled = true # false removes the deploy_project tool entirely
[update]
check_on_start = true
[ui]
theme = "auto" # auto | dark | light — auto-detects your terminal, falls back to a palette legible on both
clear_on_exit = true # wipe the conversation off the terminal when you quit.
# Clears the scrollback, not just the visible screen — the
# same thing `clear` does, with the same consequence:
# anything there before boxcode started goes too. The
# session is still on disk for `--resume` either way.Neither approval value reaches the two deliberately-uncapped tiers:
destructive-but-legitimate actions always stop for a decision in both, and the
catastrophic tier can't be configured off at all — see danger.rs for the
exact rules. Nothing here is a sandbox: these prompts are the only thing
limiting what the model can do with your own permissions, and a looser default
means the destructive tier is now carrying more of that weight on its own.
No login, no way to attribute usage to a person. Two anonymous pings only
({anon_id, event, version, os, date}, no prompts/paths/content): one on
install, one per active day. /usage (local-only, never sent) is separate.
Opt out entirely with BOXCODE_TELEMETRY_URL="" (or =off on Windows) before
installing. Aggregate counts are public: see src/telemetry.rs.
Rust + Ratatui (terminal UI) + tokio (async), talking to any OpenAI-compatible chat-completions endpoint.
src/app.rs— state machine ·src/ui.rs— rendering ·src/llm.rs— the streaming clientsrc/tools.rs— the model's tools: schemas, execution, timeoutssrc/config.rs,src/providers.rs— configuration and the built-in provider/model registrysrc/session.rs,src/usage.rs,src/telemetry.rs— local session log, local usage log, anonymous pings (all separate, see Privacy above)src/plan.rs—plan.md's format and progress trackingsrc/diff.rs— the line diff behind every file-change preview and transcript entrysrc/rollback.rs— the undo journal behind/rollback: what each write replaced, and what putting it back meanssrc/artifacts.rs,src/auth.rs,src/db.rs,src/requests.rs— publishing, hosted auth, hosted per-project SQLite, the change-request mailbox;infra/holds the control-planes these talk tosrc/deploy/— one file per hosting provider behind a sharedDeploymentProvidertrait (see docs/deploying.md for how to add one)
- A diff preview when a command (not a write) is about to modify tracked files
- Remembering per-command approvals across a session
- GitHub integration (VPC-only)
- Test generation
- More deployment providers — AWS Amplify, Cloudflare Pages, GitHub Pages, Render
cargo build --release # build
RUST_LOG=debug boxcode # run with debug logging
cargo test # testMIT