Skip to content

Repository files navigation

🌙 LocalCode

A local-first, Claude-Code-class AI coding assistant for your terminal — and your browser.

Website · English · Русский

Bun ≥ 1.1TypeScript strictLicense MITTests 3196 passingLocal-first


What is LocalCode?

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)

Table of contents


Highlights

  • 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 stdio or http; tools auto-namespaced as mcp__<server>__<tool>.
  • Cost tracking — per-message cost chip using real OpenRouter / static pricing. /usage dashboard, /cost per-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 through sandbox-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 turn chip before sending; cumulative session + today totals in the footer.
  • Smarter context compression — semantic dedup of repeated read_file results, 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 triggers regex, 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-code imports 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.

Supported providers

ProviderTypeSetup
OllamaLocalInstall Ollama, run ollama serve
LM StudioLocalInstall LM Studio, enable the local server
Unsloth StudioLocalunsloth run --model <repo> --disable-tools -p 8888needs an API key (UNSLOTH_API_KEY)
OpenAICloudAPI key via OPENAI_API_KEY or /provider
AnthropicCloudAPI key via ANTHROPIC_API_KEY or /provider
OpenRouterCloudAPI key via OPENROUTER_API_KEY or /provider
Google GeminiCloudAPI key via GEMINI_API_KEY or /provider
CustomCloudAny 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: two things that will bite you

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
  1. --disable-tools is 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.
  2. 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 run prints it too) and set UNSLOTH_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.


Requirements

  • 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).

Install

Pick whichever channel fits your platform — all paths land at the same binary.

Debian / Ubuntu (.deb)

# 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`).

Fedora / RHEL (.rpm)

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.

Homebrew (macOS & Linux)

brew install grosa787/tap/localcode

Auto-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 (Windows)

scoop bucket add localcode https://github.com/grosa787/scoop-bucket
scoop install localcode

See docs/SCOOP.md for bucket-bootstrap and maintainer setup.

One-command install script

# 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 bash

The 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 dir

Manual install (existing clone):

git clone https://github.com/grosa787/localcode.git
cd localcode
./install.sh

To run without installing:

bun install
bun run dev # alias for: bun run src/cli.tsx

Docker

LocalCode 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-open

For 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.


Quick start

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 --version

First launch triggers onboarding: pick a backend, confirm the URL, choose a model, and you're in the chat.


CLI flags

localcode [projectRoot] [flags]
FlagWhat 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-allDEPRECATED. 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).
--reconfigureRe-run onboarding, overwriting config.
--no-refresh-modelsSkip startup model-list refresh.
--webLaunch 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-openDo not auto-open the browser when --web starts. URL still printed to stdout.
--lanEnable LAN P2P session sharing via mDNS (off by default).
--help, -hShow usage and exit.
--version, -vPrint version and exit.

Subcommands

SubcommandDescription
localcode plugin <action>Manage plugins: install <path> · uninstall <id> · list · enable <id> · disable <id>
localcode daemonRun 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 demoReplay 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).

Web mode

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.

Slash commands

Every slash command runs locally — none are sent to the LLM.

Show all commands
CommandWhat it does
/helpList every registered command.
/initScan 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.
/statuslineConfigure the status line template.
/resume [id]Pick or load a session.
/clearPersist a summary, start a fresh chat.
/contextShow token usage, active skills, LOCALCODE.md status.
/ctxsize [n]Tune num_ctx and keep-alive.
/compressCompact context into one summary message.
/settingsEdit generation params (temperature, top_p, repeat_penalty, max_tokens).
/permissionsToggle 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.
/reviewOne-shot LLM code review.
/planTwo-phase plan generation.
/skillsManage skills.
/new-skillPaste or supply a path for a new skill.
/memoryOpen memory entries list.
/memory-save <id>Save a staged feedback proposal.
/todosOpen the Tasks panel.
/usageCumulative usage dashboard with cost per model.
/costCurrent-session cost breakdown per turn.
/perf/tokensToken visualizer with sparkline charts.
/agentRun 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 BCompare two branches.
/recordRecord / replay session: start · stop · save · list.
/replay <file>Replay a recording at chosen speed.
/cronPersistent cron schedules.
/wakeupsIn-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.
/archArchitecture rules: check · rules · init · ignore <pattern>.
/ontologyCodebase knowledge graph: status · refresh · graph <symbol>.
/secretsSecret scanner: scan · scan-all · allow <pattern>.
/sensitiveSensitive-files gating: list · add <pattern> · check <path>.
/worktreesWorktree management for sub-agents.
/pluginPlugin management.
/shareLAN P2P session sharing: start · stop · peers · accept.
/whiteboardOpen the web whiteboard.
/filterHide/show thinking / tool calls / system notes in chat.
/suggestToggle proactive suggestions panel.
/exitQuit and persist the session summary.

Tools for the model

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
ToolPurpose
read_fileRead file under projectRoot; auto-paginate >1 MB.
list_dirTree listing with .gitignore honoured.
glob_searchfast-glob lookup, gitignore-aware, symlink-safe.
find_symbolSymbol search via tsserver.
find_call_sitesOntology query: all callers of a function/method.
impacts_ofOntology query: transitive impact graph.
type_hierarchyOntology query: ancestors/descendants/siblings.
lint_fileNative syntax check (tsc / ruff / go vet / rustc).
fetch_imageDownload HTTPS or data:image/* URL; attach as multimodal.
web_fetchURL → markdown.
web_searchDuckDuckGo top results.
notebook_readRead a .ipynb.
pdf_readParse PDF pages to text.
monitorStatus of a background bash task.
process_statusStatus of watched processes.
git_status/diff/log/branchRead-only Git ops.
Mutating (approval required by default)
ToolPurpose
write_fileReplace file (two-phase preview + commit).
edit_fileUnique-string search/replace.
multi_editAtomic batch edits on one file.
notebook_editReplace / insert / delete a notebook cell.
run_commandsh -c … (optionally runInBackground: true).
git_commitMake a commit (also runs secret-scanner hook).
todo_writeUpdate in-session task list.
schedule_wakeupDefer continuation 60–3600 s.
spawn_agentSpawn a sub-agent worker.
team_send / team_readInter-agent communication via TeamBus.

Permission profiles

Profileread-onlywrite_file / edit_filerun_command / git_commit / browser_evaluate
defaultrunapprovalapproval
acceptEditsrunautoapproval
planrunblockedblocked
dontAskrunautoauto
bypassPermissionsrunauto + ⚠ bannerauto + ⚠ banner

Switch at any time with /profile <name> or Ctrl+P (TUI). Sensitive-files gating overrides every profile.


Memory & skills

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 & sensitive files

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.


Sub-agents

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.


MCP servers

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.


Configuration

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 = 32768keepAliveSeconds = 1800autoCompressPercent = 0.80maxRecentMessages = 20
[sound]
enabled = falseoutputStyle = "concise"
[statusline]
enabled = truetemplate = "{provider} · {model} · {tokens}/{maxTokens} ({pct}%) · {profile}"

Full schema: docs/CONFIG.md.


Architecture

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.


Testing

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.jscd 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/.


License

MIT — the LocalCode binary you install is free to use, modify, and redistribute.

Source code

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 requestsopen 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.md and BUILD_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.

About

Local-first, Claude-Code-class AI coding assistant for your terminal and your browser. Bring any LLM (local or cloud), real tool calling with approval gates, sub-agents, memory, hooks, MCP, branching sessions, voice, PDF, whiteboard, LAN sharing. Bun + ink + Vite/React.

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages