A local-first, Claude-Code-class AI coding assistant for your terminal — and your browser.
LocalCode is a terminal + web AI pair-programming assistant. It speaks to any LLM (local or cloud), gives the model a curated toolbox (read/write files, run commands, browse the web, view images, manage Jupyter notebooks, etc.), keeps everything you do in a local SQLite session store, and ships extensive controls so the assistant is fast, safe, and inspectable.
Built on Bun + ink (TUI) + Vite/React (Web). Single statically-compiled binary, no Electron, no cloud calls unless you opt in.
localcode # terminal UI (default)
localcode --web # browser UI (opens automatically)- Highlights
- Supported providers
- Requirements
- Install
- Docker
- Quick start
- CLI flags
- Web mode
- Slash commands
- Tools for the model
- Permission profiles
- Memory & skills
- Hooks & sensitive files
- Sub-agents
- MCP servers
- Configuration
- Architecture
- Testing
- License
- Bring your own LLM — Ollama, LM Studio, Unsloth Studio, OpenAI, Anthropic, OpenRouter, Google Gemini, and any OpenAI-compatible URL (Groq, Together, Fireworks, Mistral, vLLM, llama.cpp…). One UI, eight backends.
- Two surfaces, same brain — gorgeous ink TUI and a polished web UI (tabs, dock, voice in/out, drag-drop, PDF, whiteboard).
- Real tool calling with approval gates — diff previews, command previews, per-tool auto-approve, five permission profiles (
default,acceptEdits,plan,dontAsk,bypassPermissions). - Sub-agents on-demand — spawn specialist workers (
architect,debugger,security-reviewer, language reviewers, etc.) from a curated catalog. Switch between them, send extra context, see live progress. - Memory — persistent file-based per-project memory (
user/feedback/project/reference) injected into every system prompt. - Hooks — shell scripts at
PreToolUse/PostToolUse/UserPromptSubmit/SessionStart/Stop/PreCompact/SessionEnd. - MCP support — Model Context Protocol over
stdioorhttp; tools auto-namespaced asmcp__<server>__<tool>. - Cost tracking — per-message cost chip using real OpenRouter / static pricing.
/usagedashboard,/costper-session, token visualizer with sparklines. - Smart inputs — paste an image path → auto-attached as multimodal. Drag-drop files in the web. Voice in/out via Web Speech API. PDF parsing.
- Diagnostics + recovery — process introspection (
/watch <cmd>+/diagnose), conversation branching,/undo, full diff viewer, health watchdog, error banner with retry. - Codebase ontology — TypeScript LSP-powered knowledge graph; new tools
find_call_sites,impacts_of,type_hierarchy. - Architecture rules — declare layering rules in
.localcode/arch.toml; PreToolUse validator blocks forbidden imports. - Built-in security — secret scanner pre-commit hook (AWS / GitHub / OpenAI / Anthropic / Stripe / private-key patterns + entropy heuristic), sensitive-files gating, redaction.
- LAN sharing — mDNS discovery + HMAC pairing + AES-GCM session sync; share a session with a colleague on the same network without any cloud round-trip.
- Multi-user web — two browser tabs on the same session see each other's messages live, with typing presence and colored peer dots.
- Sandboxed
run_command— every shell command runs throughsandbox-exec(macOS),firejail(Linux), or Docker — fully configurable per-platform. - Plan mode — visible 🔒 banner + Ctrl+P hotkey; write & run tools blocked so the model can plan before touching anything.
- Batch approval — when the model emits 3+ mutating tool calls, a single 2-pane dialog reviews them all at once.
- Crash journal + resume — synchronous JSONL per chunk; on next start, offer to resume any session interrupted by SIGKILL / power loss.
- Cost forecast —
~$0.02 next turnchip before sending; cumulative session + today totals in the footer. - Smarter context compression — semantic dedup of repeated
read_fileresults, write/run preserved verbatim, middle summarised via a cheap model. - Image paste TUI — Ctrl+V pastes the clipboard image straight into the conversation (mac / linux / win).
- Skill auto-suggest — when your message matches a skill's
triggersregex, a toast offers Tab-to-activate. - Marketplace browse —
/skills browse(anthropics/skills) and/mcp browse(modelcontextprotocol/servers) install with one keypress. - Migration from Claude Code —
/import claude-codeimports your~/.claude/projects/*/history into the local SQLite. - /metrics dashboard — opt-in tool success rate, cache hit %, cost per model, top expensive sessions — all local.
- i18n — full UI in English and Russian, switchable live; all 6 main overlays fully translated.
| Provider | Type | Setup |
|---|---|---|
| Ollama | Local | Install Ollama, run ollama serve |
| LM Studio | Local | Install LM Studio, enable the local server |
| Unsloth Studio | Local | unsloth run --model <repo> --disable-tools -p 8888 — needs an API key (UNSLOTH_API_KEY) |
| OpenAI | Cloud | API key via OPENAI_API_KEY or /provider |
| Anthropic | Cloud | API key via ANTHROPIC_API_KEY or /provider |
| OpenRouter | Cloud | API key via OPENROUTER_API_KEY or /provider |
| Google Gemini | Cloud | API key via GEMINI_API_KEY or /provider |
| Custom | Cloud | Any OpenAI-compatible base URL (Groq, Together, Fireworks, Mistral, …) |
Explicit apiKey in ~/.localcode/config.toml wins; environment variables are the fallback. See docs/PROVIDERS.md for per-provider examples.
Unsloth Studio is a local GGUF/MLX server (llama.cpp + MLX) on
http://localhost:8888/v1, spoken to over the plain OpenAI-compatible
API. Install with curl -fsSL https://unsloth.ai/install.sh | sh, then:
unsloth run --model unsloth/gemma-4-26B-A4B-it-GGUF --disable-tools -p 8888--disable-toolsis mandatory. Without it Unsloth runs its own server-side tool loop and intercepts tool calls instead of handing them back. LocalCode does all its work through tool calls, so the agent replies with prose and never touches a file — with no error message anywhere. If LocalCode looks inert against Unsloth, this is why.- It needs an API key even on localhost. Ollama and LM Studio take
no key; Unsloth 401s without
Authorization: Bearer sk-unsloth-…. Grab it from Settings → API in Unsloth Studio (unsloth runprints it too) and setUNSLOTH_API_KEY, or paste it into/provider.
macOS on Apple Silicon is supported with no GPU and no CUDA (macOS 12+, Python >=3.11 <3.14). Windows and Linux inherit Unsloth's own prerequisite of an NVIDIA GPU with CUDA. Full walkthrough: docs/PROVIDERS.md.
- Bun ≥ 1.1 — runtime, package manager, and bundler.
- macOS or Linux. Windows is supported via WSL.
- At least one reachable LLM backend (local server or cloud API key).
Pick whichever channel fits your platform — all paths land at the same binary.
# Resolve the newest published release, so this snippet can never go stale.
VER=$(curl -fsSL https://api.github.com/repos/grosa787/localcode/releases/latest \
| sed -n 's/.*"tag_name": *"v\{0,1\}\([^"]*\)".*/\1/p' | head -1)
curl -fsSL -o localcode.deb \
https://github.com/grosa787/localcode/releases/download/v${VER}/localcode_${VER}_amd64.deb
sudo dpkg -i localcode.deb
# arm64: replace amd64 with arm64 in the URL above.
# To pin an older release instead, set VER by hand (e.g. `VER=0.24.0`).VER=$(curl -fsSL https://api.github.com/repos/grosa787/localcode/releases/latest \
| sed -n 's/.*"tag_name": *"v\{0,1\}\([^"]*\)".*/\1/p' | head -1)
sudo dnf install \
https://github.com/grosa787/localcode/releases/download/v${VER}/localcode-${VER}-1.x86_64.rpm
# arm64: replace x86_64 with aarch64.brew install grosa787/tap/localcodeAuto-resolves the right prebuilt binary for darwin-arm64, darwin-x64,
linux-arm64, or linux-x64. Upgrade later with brew upgrade localcode.
See docs/HOMEBREW.md for tap-bootstrap and maintainer
setup details.
scoop bucket add localcode https://github.com/grosa787/scoop-bucket
scoop install localcodeSee docs/SCOOP.md for bucket-bootstrap and maintainer setup.
# Always installs the latest release — nothing to bump by hand
curl -fsSL https://raw.githubusercontent.com/grosa787/localcode/main/install.sh | bash
# Or pin an exact release tag (browse them at /releases)
curl -fsSL https://raw.githubusercontent.com/grosa787/localcode/main/install.sh | LOCALCODE_REF=vX.Y.Z bashThe installer detects your OS/arch and, by default, downloads the prebuilt binary for your platform from the latest GitHub release, verifying it against the release SHA256SUMS before installing. If no usable prebuilt asset exists it falls back to a source build: it installs Bun if missing (via the official bun.sh/install), clones LocalCode into ~/.local/share/localcode, and runs bun install && bun run build. Either way it symlinks the result into ~/.local/bin (no sudo); only if that directory isn't writable does it fall back to /usr/local/bin with sudo.
An existing install stays on the channel it was installed with — re-running the installer over a source checkout keeps building from source instead of dropping a prebuilt binary beside the now-orphaned clone. Run --uninstall first to switch channels.
Flags & env overrides. When piping from curl, flags must follow -s --, otherwise bash consumes them:
curl -fsSL .../install.sh | bash -s -- --update # refresh in place (same channel)
curl -fsSL .../install.sh | bash -s -- --uninstall # remove symlink + install dir
curl -fsSL .../install.sh | bash -s -- --from-source # force clone + Bun build
curl -fsSL .../install.sh | bash -s -- --force # re-download + re-verify the binary
curl -fsSL .../install.sh | bash -s -- --verbose # show every step's output
curl -fsSL .../install.sh | bash -s -- --help # full usage text
LOCALCODE_HOME=/opt/localcode bash install.sh # custom install dir
LOCALCODE_BIN_DIR=$HOME/bin bash install.sh # custom PATH dirManual install (existing clone):
git clone https://github.com/grosa787/localcode.git
cd localcode
./install.shTo run without installing:
bun install
bun run dev # alias for: bun run src/cli.tsxLocalCode ships as a multi-arch image (linux/amd64 + linux/arm64) to GitHub
Container Registry: ghcr.io/grosa787/localcode. Tags: latest, vX.Y.Z,
and X.Y.Z per release.
# TUI (needs an interactive TTY)
docker run -it --rm \
-v "$(pwd):/workspace" \
-v "$HOME/.localcode:/root/.localcode" \
-e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \
ghcr.io/grosa787/localcode:latest
# Web UI (port-mapped, no TTY)
docker run --rm -p 7777:7777 \
-v "$(pwd):/workspace" \
-v "$HOME/.localcode:/root/.localcode" \
-e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \
ghcr.io/grosa787/localcode:latest \
--web --web-host 0.0.0.0 --no-openFor local backends (Ollama / LM Studio / Unsloth Studio) running on the host, use
host.docker.internal (Docker Desktop) or add
--add-host=host.docker.internal:host-gateway on Linux. Full usage,
auth, persistence, and compose recipes: docs/DOCKER.md.
localcode # open the current directory
localcode ~/path/to/project # open a specific project
localcode --resume ab12cd34 # resume a session by id prefix
localcode --model claude-3-5-sonnet # override the model
localcode --profile plan # start in Plan Mode (no mutations)
localcode --web # browser UI
localcode --help # full flag list
localcode --versionFirst launch triggers onboarding: pick a backend, confirm the URL, choose a model, and you're in the chat.
localcode [projectRoot] [flags]
| Flag | What it does |
|---|---|
[projectRoot] |
Positional. Path to the project. Defaults to process.cwd(). |
--profile <name> |
Permission profile: default · acceptEdits · plan · dontAsk · bypassPermissions. Overrides persisted config. |
--dangerously-allow-all |
DEPRECATED. Equivalent to --profile dontAsk. Skips approvals. |
--resume <sessionId> |
Resume a session. Accepts full UUID or sufficiently-unique prefix. |
--model <name> |
Override the active model for this run only (does NOT modify persisted config). |
--reconfigure |
Re-run onboarding, overwriting config. |
--no-refresh-models |
Skip startup model-list refresh. |
--web |
Launch the browser-based UI instead of the terminal. |
--web-host <host> |
Bind host for --web. Default 127.0.0.1. Pass 0.0.0.0 to expose on the LAN. |
--web-port <port> |
First port to try for --web. Default 7777. Probes subsequent ports if busy. |
--no-open |
Do not auto-open the browser when --web starts. URL still printed to stdout. |
--lan |
Enable LAN P2P session sharing via mDNS (off by default). |
--help, -h |
Show usage and exit. |
--version, -v |
Print version and exit. |
| Subcommand | Description |
|---|---|
localcode plugin <action> |
Manage plugins: install <path> · uninstall <id> · list · enable <id> · disable <id> |
localcode daemon |
Run the persistent cron daemon (background scheduled wakeups). |
localcode doctor [--json] |
Diagnose installation health: config, backend, models, disk, hooks, MCP, git. |
localcode completion <sh> |
Print a shell-completion script (bash, zsh, fish) to stdout. |
localcode demo |
Replay a short bundled tour that demonstrates what LocalCode can do. |
localcode update <action> |
Auto-update from GitHub Releases (supports delta patches — typical update is ~200 KB). |
localcode --web boots a local server on 127.0.0.1:7777 (configurable), prints the URL with a one-shot CSRF token in the fragment, and auto-opens your default browser.
The web UI features:
- Tabbed sessions (Cmd/Ctrl + 1…9 to switch, Cmd/Ctrl + T new, Cmd/Ctrl + W close).
- Resizable / dockable panels.
- Top-bar icon panels: Tasks · Agents · Browser · Memory · Files · Usage · Notifications · Settings.
- Voice input (push-to-talk) + voice output (TTS) via the Web Speech API.
- Drag-drop files from OS (images become multimodal attachments, text becomes
@path). - PDF parsing with page-by-page preview.
- High-quality whiteboard via
tldraw— sketch a diagram → send to chat as multimodal image. - Mermaid diagrams rendered as SVG, full-screen zoom/pan.
- Light & dark themes.
- Live cost meter, queued-message indicator, error banner with Retry last.
Every slash command runs locally — none are sent to the LLM.
Show all commands
| Command | What it does |
|---|---|
/help |
List every registered command. |
/init |
Scan project & write .localcode/LOCALCODE.md. |
/model [name] |
Open picker or switch directly. |
/provider [...] |
Switch backend (Ollama / LM Studio / Unsloth / OpenAI / Anthropic / OpenRouter / Google / custom). |
/profile [name] |
Switch permission profile: default · acceptEdits · plan · dontAsk · bypassPermissions. |
/style [name] |
Output style: concise · explanatory · verbose. |
/statusline |
Configure the status line template. |
/resume [id] |
Pick or load a session. |
/clear |
Persist a summary, start a fresh chat. |
/context |
Show token usage, active skills, LOCALCODE.md status. |
/ctxsize [n] |
Tune num_ctx and keep-alive. |
/compress |
Compact context into one summary message. |
/settings |
Edit generation params (temperature, top_p, repeat_penalty, max_tokens). |
/permissions |
Toggle per-tool auto-approval. |
/diff [ref] |
Open the full-screen diff viewer. |
/undo [N|list] |
Roll back the last N file mutations from the in-memory snapshot stack. |
/review |
One-shot LLM code review. |
/plan |
Two-phase plan generation. |
/skills |
Manage skills. |
/new-skill |
Paste or supply a path for a new skill. |
/memory |
Open memory entries list. |
/memory-save <id> |
Save a staged feedback proposal. |
/todos |
Open the Tasks panel. |
/usage |
Cumulative usage dashboard with cost per model. |
/cost |
Current-session cost breakdown per turn. |
/perf /tokens |
Token visualizer with sparkline charts. |
/agent |
Run the agentic loop on a task. |
/spawn [id task] |
Spawn a specialist worker from the catalog. |
/agents diff <id> |
Show worktree diff for a sub-agent. |
/branch [...] |
Branching sessions: list · <name> · switch <name> · delete <name>. |
/conv diff A B |
Compare two branches. |
/record |
Record / replay session: start · stop · save · list. |
/replay <file> |
Replay a recording at chosen speed. |
/cron |
Persistent cron schedules. |
/wakeups |
In-session deferred-continuation list. |
/watch <cmd> |
Watch a long-running process (dev server, test runner) for diagnostic signals. |
/diagnose [id] |
Surface compile / test errors from watched processes. |
/arch |
Architecture rules: check · rules · init · ignore <pattern>. |
/ontology |
Codebase knowledge graph: status · refresh · graph <symbol>. |
/secrets |
Secret scanner: scan · scan-all · allow <pattern>. |
/sensitive |
Sensitive-files gating: list · add <pattern> · check <path>. |
/worktrees |
Worktree management for sub-agents. |
/plugin |
Plugin management. |
/share |
LAN P2P session sharing: start · stop · peers · accept. |
/whiteboard |
Open the web whiteboard. |
/filter |
Hide/show thinking / tool calls / system notes in chat. |
/suggest |
Toggle proactive suggestions panel. |
/exit |
Quit and persist the session summary. |
The model gets a typed toolbox. Read-only tools auto-run; mutating tools go through approval (unless pre-approved or under dontAsk).
Read-only / inspect
| Tool | Purpose |
|---|---|
read_file |
Read file under projectRoot; auto-paginate >1 MB. |
list_dir |
Tree listing with .gitignore honoured. |
glob_search |
fast-glob lookup, gitignore-aware, symlink-safe. |
find_symbol |
Symbol search via tsserver. |
find_call_sites |
Ontology query: all callers of a function/method. |
impacts_of |
Ontology query: transitive impact graph. |
type_hierarchy |
Ontology query: ancestors/descendants/siblings. |
lint_file |
Native syntax check (tsc / ruff / go vet / rustc). |
fetch_image |
Download HTTPS or data:image/* URL; attach as multimodal. |
web_fetch |
URL → markdown. |
web_search |
DuckDuckGo top results. |
notebook_read |
Read a .ipynb. |
pdf_read |
Parse PDF pages to text. |
monitor |
Status of a background bash task. |
process_status |
Status of watched processes. |
git_status/diff/log/branch |
Read-only Git ops. |
Mutating (approval required by default)
| Tool | Purpose |
|---|---|
write_file |
Replace file (two-phase preview + commit). |
edit_file |
Unique-string search/replace. |
multi_edit |
Atomic batch edits on one file. |
notebook_edit |
Replace / insert / delete a notebook cell. |
run_command |
sh -c … (optionally runInBackground: true). |
git_commit |
Make a commit (also runs secret-scanner hook). |
todo_write |
Update in-session task list. |
schedule_wakeup |
Defer continuation 60–3600 s. |
spawn_agent |
Spawn a sub-agent worker. |
team_send / team_read |
Inter-agent communication via TeamBus. |
| Profile | read-only | write_file / edit_file |
run_command / git_commit / browser_evaluate |
|---|---|---|---|
default |
run | approval | approval |
acceptEdits |
run | auto | approval |
plan |
run | blocked | blocked |
dontAsk |
run | auto | auto |
bypassPermissions |
run | auto + ⚠ banner | auto + ⚠ banner |
Switch at any time with /profile <name> or Ctrl+P (TUI). Sensitive-files gating overrides every profile.
Memory lives at <projectRoot>/.localcode/memory/*.md. Four types: user / feedback / project / reference. Each entry is a markdown file with YAML frontmatter. The full memory section is injected into the system prompt byte-stably so prompt-prefix caching stays hot. Indexed in MEMORY.md at the root.
Skills are markdown files in .localcode/skills/ (project-local) + ~/.localcode/skills/ (global). Project wins on id collision. Hot-reloaded via chokidar. Manage via /skills and /new-skill.
Hooks are shell scripts wired through [[hooks]] blocks in ~/.localcode/config.toml. Triggers:
PreToolUse— can BLOCK a tool call (non-zero exit → tool fails).PostToolUse— synthetic system note only.UserPromptSubmit— fires before context commit; blocking aborts the turn.SessionStart/SessionEnd/Stop/PreCompact— lifecycle.
Built-in secret-scanner hook auto-registers on PreToolUse:git_commit — refuses to commit AWS keys, GitHub PATs, OpenAI/Anthropic/Stripe/Google keys, private keys, or high-entropy assignments.
Sensitive files at ~/.localcode/sensitive-files.toml (or .localcode/sensitive-files.toml) declare path globs (e.g. .env*, **/secrets/**, *.pem, **/.ssh/**) that always require approval, even under dontAsk.
Spawn specialist workers from the curated catalog (10 templates):
architect · debugger · security-reviewer · typescript-reviewer ·
python-reviewer · rust-reviewer · go-reviewer · test-engineer ·
performance-optimizer · doc-writer
/spawn debugger "find why the migration timed out"Workers run in isolated git worktrees, communicate via TeamBus. In TUI press Tab to enter agent-focus mode, ↑/↓ to select, Enter to attach and chat with a worker; Esc to return to the lead. In the web UI click a worker in the Agents panel to enter reply mode.
Completed workers automatically move to history; toggle visibility per panel.
LocalCode is a Model Context Protocol client. Configure via TOML:
[[mcpServers.my-server]]
type = "stdio"
command = "uvx"
args = ["mcp-server-time"]
[[mcpServers.docs]]
type = "http"
url = "https://example.com/mcp"
headers = { Authorization = "Bearer …" }Tools are auto-namespaced as mcp__<server>__<tool>. Status panel at GET /api/mcp.
All config lives at ~/.localcode/config.toml (global) + per-project overrides at <projectRoot>/.localcode/settings.json. The first run scaffolds defaults; subsequent edits are atomic (temp + rename) and Zod-validated.
Sample:
[backend]
type = "openrouter"
baseUrl = "https://openrouter.ai/api/v1"
[model]
current = "anthropic/claude-3.5-sonnet"
[permissions]
profile = "acceptEdits"
autoApprove = ["read_file", "list_dir"]
[context]
maxTokens = 32768
keepAliveSeconds = 1800
autoCompressPercent = 0.80
maxRecentMessages = 20
[sound]
enabled = false
outputStyle = "concise"
[statusline]
enabled = true
template = "{provider} · {model} · {tokens}/{maxTokens} ({pct}%) · {profile}"Full schema: docs/CONFIG.md.
src/
├── cli.tsx argv + ink mount
├── app.tsx composition root
├── llm/ adapter (OpenAI-compat + Anthropic), context, executor, pricing
├── tools/ 30+ tool implementations
├── sessions/ bun:sqlite, FTS5 search, branching
├── commands/ slash-command factories
├── config/ Zod-validated TOML
├── skills/ chokidar-watched markdown skills
├── memory/ persistent memory entries
├── hooks/ shell-hook engine (7 triggers)
├── mcp/ Model Context Protocol client
├── agents/ orchestrator, TeamBus, worker pool, worktree GC, catalog
├── ontology/ TS LSP knowledge graph
├── architecture/ arch.toml validator
├── security/ secret scanner, sensitive files
├── process-monitor/ watched-process diagnoser
├── networking/ LAN P2P (mDNS + HMAC + AES-GCM)
├── recordings/ session record/replay
├── scheduling/ wakeups + persistent crons
├── web/ REST + WS server, runtime pool, approval bridge
└── ui/ ink TUI (screens, components, overlays)
web-frontend/ Vite + React SPA (Zustand, CSS Modules, tldraw)
Single-process Bun, no daemons (except optional localcode daemon for crons). Web SPA is embedded as base64 in the CLI binary by scripts/embed-web.ts.
bun test # full suite (3196 passing)
bun test tests/llm/adapter.test.ts # single file
bunx tsc --noEmit # type-check
bun run build # bundle to dist/cli.js
cd web-frontend && bunx vitest run # web tests (435 passing)CI: bunx tsc --noEmit, bun test, bun build, and a lint job that fails the build on any : any / <any> / as any / @ts-ignore in src/.
MIT — the LocalCode binary you install is free to use, modify, and redistribute.
LocalCode's distribution lives here: installer, docs, packaging, landing page, and the issue tracker. Active development happens in a separate, private repository.
- Binaries are MIT. Anything you download from Releases is yours to use, fork, and redistribute under those terms.
- Bug reports + feature requests — open an issue. This is where we triage everything.
- Want to contribute code? Open an issue describing the change. We accept code contributions via a short CLA + invite to the private source repo. See
CONTRIBUTING.mdandBUILD_FROM_SOURCE.md.
We picked this layout to keep the public surface focused on real users (downloads, issues, docs) while shipping fast. If you've used LocalCode under MIT, you keep MIT — closing future development doesn't retroactively close past releases.
Made with ☕, late nights, and a lot of /undos.