hilla
v0.6.12
Published
AI co-founder in your terminal. Plan on canvas, execute in CLI.
Maintainers
Readme
Hilla CLI
AI co-founder in your terminal. Plan on canvas at hilla.ai, execute in CLI.
▄▄▄ ▄▄▄▄ ▄▄▄▄▄
▄█████▄ ▄██████▄ ▄████████▄
████████▄▄▄█████████████████████
▀█████████████████████▀▀ ▀██▀
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀hilla.ai is the brain — you sketch your project there: goals, stages, features, tech stack. hilla CLI is the hands — it takes that plan, sets up a sandbox, and writes real code into it.
Chat and execution are bound to a pulled Hilla project. Load work with /pull before asking questions or running tasks. When the agent calls task_done, the matching canvas task is marked complete back on hilla.ai.
Install
Pick whichever fits your setup. All three install the same hilla binary.
npm (recommended for Node devs):
npm install -g hillaHomebrew (macOS / Linux):
brew install hillaai/hilla/hillacurl (Mac/Linux, brings its own Node 20 LTS):
curl -fsSL https://hilla.ai/install.sh | shThe curl installer lands the binary at ~/.hilla/bin/hilla and prints the
one-line PATH update for your shell. It will install a private Node 20 LTS into
~/.hilla/runtime/ if your system Node is older than 18.
Requires Node 18+ (provided automatically by the Homebrew formula and the curl installer).
Windows isn't supported in v1. Use WSL2 + Linux for now.
Quick start
# 0. New here? Take the 5-min walkthrough (no API calls, runs in a tmpdir)
hilla tutorial
# 1. Check your environment
hilla doctor
# 2. Launch the TUI
hilla
# 3. Connect your Hilla account (create a token at hilla.ai/settings/tokens)
/login
# 4. Pull a project from your canvas
/pull # opens the project picker
/pull 7 # direct pull still works
# First pull asks: where should I create this project?
# ▸ ~/code/<project-slug> (default — your ~/code dir)
# ~/Desktop/<project-slug>
# ~/.hilla/sandboxes/<id> (hidden, sandboxed)
# Other — paste a path
# The chosen path is saved in ~/.hilla/projects.json;
# later pulls reuse it silently. Use /cd-move <new-path>
# to relocate.
# 5. Let the agent start building it
/run # next open task — agent will mark it done on canvas
/run @brand # match a specific task by id-prefix or keyword
/run write a README # freeform instruction
# Plain text is chat only, even after a project is loaded
What should I build first?
# Or launch directly in autonomous mode:
hilla --all-in
# Slash commands can also be launched from your shell:
hilla pull 7
hilla run
hilla doctor
hilla --versionEvery project lives in a folder you pick on the first /pull (defaults to ~/code/<project-slug>/). The agent only reads and writes inside that folder. Each task gets its own tasks/<task-name>/inputs/ (drop assets here) and tasks/<task-name>/outputs/ (agent writes deliverables here). Hidden metadata — canvas context, history, plan, per-task state — lives in <folder>/.hilla/.
The map of project_id → folder path lives in ~/.hilla/projects.json (chmod 600). Use /cd-move <new-path> to relocate a project later.
Upgrading from v0.4? Existing sandboxes at ~/.hilla/sandboxes/<project-id>/ keep working. On first launch, the CLI lists any legacy sandboxes it sees so you can decide whether to relocate them with /cd-move.
Agent modes
Execution starts only from /run. Plain text messages are chat, and can answer questions about the loaded project without editing files.
Three autonomy modes, set via --mode <name> at launch, the /mode slash command at runtime, or persisted in ~/.hilla/config.json:
| Tool | ask-all (default) | safe-auto | yolo |
| ----------------------------------------------------------------------------- | -------------------------- | ----------- | -------------------- |
| read_file, list_dir | auto | auto | auto |
| write_file, edit_file | ask | auto | auto |
| bash (safe) | ask | ask | auto |
| bash (dangerous: rm -rf /, sudo, fork bombs, piped curl-to-shell, etc.) | ask | ask | ask (never auto) |
| task_done | auto | auto | auto |
| ask_user | always pauses for the user | | |
--all-in is preserved as a back-compat alias for --mode=safe-auto.
hilla --mode=yolo # everything auto, except deploy + dangerous bash
hilla --mode=safe-auto # file writes auto; bash still asks
hilla --all-in # legacy alias for --mode=safe-auto
hilla # ask-all (safe default)
# At runtime:
/mode # show current mode + the matrix
/mode yolo # switch and persist to ~/.hilla/config.jsonRecovery when the agent gets stuck. After every tool call the CLI checks for three trip-wires: 3 consecutive failures on the same file, the same edit_file search string failing twice, or > 5 minutes since the last successful write. Any ask_user call also counts as stuck.
ask-all— surfaces the diagnostic to you viaask_user.safe-auto— auto-escalates toopus-4.6for one retry. If already on opus, falls through toask_user.yolo— marks the canvas taskblockedwith a reason, logs it to history, and moves on.
Ctrl+C cancels at any time. When the agent calls task_done, hilla CLI first syncs at least one real file from tasks/<task>/outputs/ to the canvas, then pushes the matching canvas task to done. If there is no deliverable to upload, the upload fails, or your token is missing write scope, task_done is rejected and the agent has to fix the output before the board is marked complete.
Per-task locks
Each /run acquires a per-task lock at <project-folder>/.hilla/lock.json. Two CLI sessions can run different tasks on the same project simultaneously — locks are keyed by task id, not project. Running the same task twice in parallel is refused with a diagnostic:
Task locked by PID 12345 on hilla-laptop (held 4m).
Stale? Use `/unlock task-abc-123` to force-release.Stale lock recovery:
- Same machine: if the holder PID is no longer running, the next acquire auto-releases it.
- Cross-machine: locks older than 30 minutes are treated as stale (we can't probe a remote PID).
Useful commands:
/locks— list all locks held on the current project, flagging stale ones./unlock <task-id>— force-release a lock. The CLI asks for confirmation when the holder is on another machine.
Lock auto-releases and force-releases are appended to <project-folder>/.hilla/history.jsonl so you can audit who unstuck what.
Multi-machine
Run hilla CLI on a laptop and a desktop against the same canvas project. Each /pull records this machine's hostname + folder path into projects.cli_metadata.cli_locations[] on canvas, and the status bar shows the most recently seen other machine:
● ready · Also on hilla-desktop · ~/Work/foo (2h ago) · live sonnet-4.6 · 18crThe trailing · live appears when this CLI has an open Supabase Realtime subscription on the project. If Realtime fails to attach (firewall, offline), fall back to manual /pull — task status flowing through canvas keeps working either way. Disable with HILLA_REALTIME_DISABLED=1.
Code sync is not the CLI's job — use git. Each machine gets its own folder path because ~/.hilla/projects.json is per-machine. Canvas just helps you remember where the project lives on the other machine.
Commands
| Command | Description |
| ---------------------------------------- | ------------------------------------------------------------------------------------------ |
| /help | Show available commands |
| /doctor | Diagnose auth / network / sandbox / version |
| /login | Connect your Hilla account |
| /logout | Sign out, remove local credentials |
| /whoami | Show current user |
| /projects [n\|keyword] | Browse projects or pull a selected project |
| /pull [n\|keyword] | Open the project picker or pull directly into sandbox |
| /run | Execute the next open canvas task |
| /run @<id\|keyword> | Execute a specific task by id-prefix or substring match |
| /run <text> | Freeform instruction (treated as one-off agent prompt) |
| /ask <question> | One-off chat question (no tools) |
| /cd | Print the current project's folder path |
| /cd-move <new-path> | Relocate the current project to a new folder |
| /status | Current project summary |
| /tasks | Show tasks of loaded project |
| /locks | List per-task locks held in the current project |
| /unlock <task-id> | Force-release a stuck task lock |
| /model [list\|n\|name] | Show or switch AI model |
| /mode [ask-all\|safe-auto\|yolo] | Show or switch autonomy mode |
| /mcp list | List MCP server connections + auth status |
| /mcp connect <name> | Open OAuth flow in browser to connect an MCP server |
| /mcp disconnect <name> | Revoke a connected MCP server |
| /proposals [all] | List CLI-originated proposals awaiting canvas review |
| /deploy [targets] [--auto-create-repo] | Deploy current project to Vercel / Supabase / GitHub (opens a picker when no targets pass) |
| /credits | Credit balance and plan |
| /history | Last 20 commands |
| /config | Show current settings |
| /exec <cmd> | Run a safe read-only command inside the current sandbox |
| /tutorial | Replay the 5-min synthetic walkthrough |
| /clear | Clear the chat |
| /quit | Exit |
Deploy
/deploy (or hilla deploy from your shell) ships the current project to
the three targets we wire up out of the box: GitHub (source), Supabase
(db + edge functions), and Vercel (production). Each target is optional —
the picker opens with detected targets pre-selected, or you can pass them
inline:
# In-TUI:
/deploy # open the picker
/deploy vercel,github # skip the picker; deploy two targets
/deploy github --auto-create-repo
# Out-of-TUI (pipeable, no Ink):
hilla deploy # default: every detected target
hilla deploy --target=vercel,github
hilla deploy --project=my-app --auto-create-repoThe orchestrator runs targets in a fixed order — github → supabase → vercel — so source is up before infra and infra is up before the app. A failure in any one target is logged but does not abort the others; the final report shows OK/FAIL per target with URLs and durations.
Requirements per target:
- Vercel —
vercelCLI on PATH (npm i -g vercel) andvercel loginonce. - Supabase —
supabaseCLI on PATH (brew install supabase/tap/supabase) and a project-ref. Canvas-declaredcli_metadata.supabase_project_reffills this in automatically. - GitHub —
giton PATH. The first-time--auto-create-repoflow uses theghCLI when present.
Agent tools
Inside /run, the model has access to:
read_file(path)— read inside the sandboxwrite_file(path, content)— create/overwrite (asks approval)edit_file(path, search, replace)— surgical diff editslist_dir(path)— list filesbash(command, reason)— run shell inside sandbox with a scrubbed environment (always asks approval).${cwd}/node_modules/.binis on PATH so devDependency binaries work withoutnpx.task_done(summary)— mark the task complete after at least one real output syncs to canvasask_user(question)— pause and ask for clarificationgenerate_placeholder(kind, name, task_name, brand_color?, brand_font?)— write a text-styled SVG placeholder totasks/<task-name>/outputs/<kind>-placeholder.svg. Kinds:logo,hero-image,illustration,icon,photo. Used when no real asset has been uploaded yet.swap_placeholder(placeholder_path, real_asset_path)— rewrite every reference to a placeholder asset across the sandbox (skippingtasks/and.hilla/) and delete the placeholder. Always asks approval; treated as destructive.
Brand assets & placeholders
Each task gets tasks/<task-name>/inputs/ (drop real files here) and tasks/<task-name>/outputs/ (agent deliverables). Before the agent starts a visual task — logo, hero image, illustration, icon, photo — the CLI prints a manifest listing the expected inputs/ folders and opens them in your file manager.
- Drop a real file in
tasks/<task>/inputs/and the chokidar-backed watcher picks it up. The CLI prints📥 New asset receivedand queues a re-run of the affected task as soon as the agent is idle. Multiple writes to the same file inside a 500 ms window collapse to a single event. - If no asset has landed by the time the agent needs one, it can call
generate_placeholderto produce a text-styled SVG (wordmark, monogram, banner — never an AI-generated image). Each placeholder carries ahilla-placeholder: truemarker so it can be safely swapped later. - When a real asset shows up later, the agent runs
swap_placeholderto rewrite every reference across the project, then deletes the placeholder file.tasks/and.hilla/are skipped during the rewrite to avoid recursive self-replacement.
The watcher is started by /pull and torn down by /quit or /logout. While the canvas-side output upload op lands separately (HILLA-525), the watcher mirrors only locally for now.
Paths are relative to the project folder. Any attempt to read or write outside that folder, including symlink escapes and .. traversal, is rejected — whether the folder is a user-chosen path (~/code/foo/) or the legacy hidden sandbox (~/.hilla/sandboxes/<id>/). The backend verifies the loaded project and owns the execution tool schema; custom tools and generic no-project agent requests are rejected server-side.
MCP (Model Context Protocol) servers
You can connect Hilla to external MCP servers (GitHub, Linear, Figma, Notion, Vercel) so the AI can pull richer context from those services. The CLI re-uses the same connections as the web app.
/mcp list # current connections + auth status
/mcp connect github # opens the OAuth flow in your browser
/mcp disconnect github # revoke an existing connectionConnections live on your Hilla account, not on disk — /logout and re-login keep your MCP setup intact. Tool execution through these servers happens server-side; the CLI itself never sees the access token.
Keyboard
Tab— switch view, or complete slash commands↑/↓— command historyCtrl+S— toggle sidebarCtrl+L— clear the chatCtrl+C— cancel agent turn / approval promptCtrl+X— dismiss the "update available" banner
AI models
/model list to see available models. Switch with /model <n|name>.
- sonnet-4.6 — coding
- opus-4.6 — heavy reasoning
- kimi-k2.6 — writing / Turkish
- gemini-3.1-pro — planning
- glm-5-turbo — fast coding
- deepseek-r1 — research
All model labels route to the matching OpenRouter model ID through Hilla's backend and are metered server-side — your account token is the only credential.
Upgrading from < 0.4? Pre-
0.4.0shipped a stalekimi-k2.5alias that the backend stopped accepting once the model was rev'd. Your saved config is auto-migrated tokimi-k2.6on first launch.
Config
~/.hilla/config.json:
{
"defaultModel": "sonnet-4.6",
"mode": "ask-all"
}~/.hilla/auth.json holds your PAT (chmod 600). ~/.hilla/config.json stores non-secret preferences only.
Environment overrides: HILLA_API_URL, HILLA_PUBLISHABLE_KEY, HILLA_TELEMETRY_DISABLED=1 to opt out of anonymous telemetry, HILLA_POSTHOG_KEY to point telemetry at a different PostHog project.
Telemetry
Hilla CLI sends anonymous usage events (launch, login, project pull, run start/done/cancelled, model change, doctor run) to PostHog so we can measure adoption and find rough edges. A random ID is stored at ~/.hilla/telemetry-id.json — no tokens or sandbox contents are ever sent.
Opt out with HILLA_TELEMETRY_DISABLED=1.
Develop
git clone https://github.com/hillaai/hilla-cli.git
cd hilla-cli
npm install
npm run build
npm link
hillaLicense
MIT © hillaai
