Using the CLI
The bun run main.ts assistant is a local coding and personal assistant. Its commands and keys follow Claude Code and the Codex CLI, and it works with Ollama, Docker Model Runner, or llama.cpp.
Launching
bun run main.ts # full-screen TUI; the current directory is the workspace
bun run main.ts --plain # line REPL (automatic when stdin/stdout isn't a terminal)
bun run main.ts --model qwen3:8b
bun run main.ts --add-dir ~/notes
bun run main.ts --listen # start in hands-free voice mode
bun run main.ts whatsapp link # chat from WhatsApp (see below)
From another directory, run bun ~/path/to/andromeda-js/main.ts (or an alias for it) so that directory becomes the workspace. Every option is listed under Command-line flags.
MCP servers, the knowledge graph and other slow services connect in the background after the UI opens, so you can start typing straight away. /doctor checks the configured model server and reports model capabilities.
The terminal UI
The TUI is a scrolling transcript with • replies and tool calls, a Working (12s • esc to interrupt) status line and a › composer. Tool calls show what they do: a red/green diff with line numbers for edits, the start of a new file, the command for shell calls. When they finish, a one-line summary replaces the raw output, such as "Read 120 lines", "Updated app.py · +3 −1" or "Found 14 matches".
| Key | Action |
|---|---|
| Enter | Send. Prompts typed while a turn runs are queued. |
| ↑ / ↓ | Recall earlier prompts, or move through the / command popup |
| / then Tab | Complete a slash command |
| Esc | Interrupt the running turn (or stop a spoken reply) |
| Shift+Tab | Cycle permission mode: auto → plan → ask |
| ← on an empty prompt | Live view of subagents and background tasks |
| Ctrl+T | Full transcript: complete tool output and reasoning |
| Ctrl+R | Toggle hands-free voice mode |
| Ctrl+C / Ctrl+D | Quit |
Set OLLAMA_CONTEXT_WINDOW to your server's context size to see context-used percentages. At 90% the conversation is summarized automatically and the turn continues; see Context compaction.
Permission modes
The mode decides which tool calls need your approval. Switch with Shift+Tab or /permissions auto|plan|ask, and set the default with agent.permission_mode.
| Mode | Behavior |
|---|---|
auto | Tools run freely, except the always-ask calls below. |
plan | Read-only. Edits, commands, downloads, browser clicks and generation are refused. The agent investigates and writes a plan for you to review. |
ask | Every tool that changes something (writes, commands, non-read-only MCP tools, browser actions) asks first. |
In every mode, the CLI asks before a destructive or outward-facing shell command (rm, sudo, git push, git reset --hard, curl … | sh, DROP TABLE, …) and before creating or running a tool the model wrote. Answering always approves only that exact command or tool for the rest of the session. Turn this off with agent.confirm_destructive: false.
Plan, review, then build
- Switch to
planand describe the change. The agent reads and searches, writes a plan withupdate_planand summarizes it, leaving the steps open. - Read it with
/plan(or open the Markdown file under.andromeda/plans/), edit it if you like, and ask for changes until it's right. - Switch back to
autoand say "go ahead". The agent carries the saved plan out step by step, and a turn can't end with steps left undone.
See Planning & verification for how plans and the verifier work.
Slash commands
Read-only commands (/status, /context, /usage, /diff, /plan, …) run immediately, even while a turn is in progress. Others queue behind it.
Session and model
| Command | Action |
|---|---|
/help | Commands and keyboard shortcuts |
/status | Model, context window, token usage, MCP and session status |
/model [name] | List installed models, or switch. The switch reaches every running Andromeda process (TUI, REPL, background service, voice server) within a fraction of a second, moves subagents with it, unloads the old model and warms the new one |
/config [key value] | Show settings, or change reasoning and UI preferences (thinking on|off|auto, reasoning low|medium|high|…, show-thinking, recaps, tips, recap-after <seconds>) |
/doctor | Check the configured model server, tool support, and context size |
/permissions [auto|plan|ask] | Show or change the permission mode (also /approvals) |
/shortcuts | Keyboard shortcuts |
/exit, /quit | Quit |
Conversations and context
| Command | Action |
|---|---|
/clear (/new) | Start a new conversation; the old one stays in /resume |
/resume [id] | List saved conversations, or resume one (also /threads) |
/forget | Permanently delete the current conversation |
/compact [focus] | Summarize the conversation to free context. With memory, continue in a new thread seeded with the summary. |
/context | What is filling the context window |
/usage (/cost) | Session token totals |
/plan [clear] | Show this conversation's saved plan, or delete it |
/memory | Show working memory |
/reindex | Rebuild semantic-recall vectors from saved history |
/export [file] | Save the conversation as Markdown in the workspace |
/steer <context> | Add information to the running task at its next model step |
/btw <question> | A side answer that doesn't enter the main conversation |
Working on code
| Command | Action |
|---|---|
/init | Have the agent create or improve AGENTS.md |
/review [focus] | Have the agent review uncommitted changes (read-only) |
/diff | Show uncommitted changes, including new files |
/mention <path> | Attach a workspace file to your next message |
/add-dir [path] | Let tools read and write another folder this session |
Tools, agents and media
| Command | Action |
|---|---|
/tools | Core tools and on-demand tool groups |
/skills [query] | List or search skills |
/agents | Subagent profiles and this session's subagents |
/tasks [cancel <id>] | Running and recent background tasks, or cancel one |
/mcp | MCP servers, their status and tools |
/voice [on|off] | Listen continuously and speak replies (also Ctrl+R) |
/voice kokoro, /voice qwen [VOICE] | Pick the speaking engine and Qwen voice |
/plugin (/plugins) | Loaded plugins and any that failed |
/hooks | Configured shell hooks |
/image <prompt> | Generate an image without going through the model (also /imagine) |
/image edit <change> | Edit the last image; each edit becomes the new last image |
/image qwen|ollama | Switch image provider; /image alone shows status |
Claude Code-style commands
Commands from Claude Code work too: /effort, /fast, /output-style, /rename, /fork (/branch), /rewind (/checkpoint), /copy, /security-review, /simplify, /pr-comments, /loop, /schedule, /artifacts, /insights, /theme, /vim, /statusline and more, with aliases such as /todos, /stats, /code-review and /settings. Ones that need an Anthropic account (/install-github-app, /mobile, …) say what to use instead.
Learning
| Command | Action |
|---|---|
/good, /bad | Rate the last answer, so its recipe is reused or dropped |
/learned [delete <id>] | List learned recipes and stats, or delete one |
/teach … | Record a task you demonstrate and turn it into a skill (below) |
Shell & teach mode
Start a line with ! to run a shell command in the workspace yourself. The output is shown and sent along with your next message, so you can say "fix this" after a failing test run:
› !bun test tests/calc.test.ts
› the divide test fails, fix it
Teach mode turns a task you do yourself into a reusable skill:
› /teach release-notes write release notes from git
› !git log --oneline v1.2..HEAD
› /teach note fixes go before features
… edit files in your editor …
› /teach stop # shows the drafted SKILL.md
› /teach save # keeps it under skills/learned/ (or /teach discard)
Only ! commands and file changes inside the workspace are recorded; nothing watches in the background. /teach cancel stops without drafting.
Command-line flags
| Flag | Effect |
|---|---|
--model NAME | Override OLLAMA_MODEL |
--config PATH | YAML settings file (default ANDROMEDA_CONFIG or ./andromeda.yml) |
--workspace PATH | Workspace for tools, skills, personality and outputs (default: current directory) |
--add-dir DIR | Also let tools use DIR (repeatable) |
--plain | Line REPL instead of the TUI |
--no-stream | Wait for each complete model response |
--think / --no-think | Enable or disable model reasoning |
--quiet | Hide progress and metrics |
--memory none|local|qdrant | Memory provider (default MEMORY_PROVIDER) |
--resource-id ID | Identity that scopes long-term memory (default local-user) |
--thread-id ID | Conversation identity (default default) |
--listen | Start in hands-free voice mode |
--no-audio, --no-images, --no-videos | Leave out voice, image generation or video generation |
--browser / --no-browser | Let the assistant drive a real browser |
--browser-visible | Show the Playwright browser window (the default here) |
--heartbeat / --no-heartbeat | Self-started check-ins |
Managing MCP servers
Add Model Context Protocol servers the same way as claude mcp or codex mcp. Servers connect the next time Andromeda starts.
# stdio: everything after -- is the command line
bun run main.ts mcp add playwright -- npx -y @playwright/mcp@latest
bun run main.ts mcp add -e API_KEY='${MY_KEY}' myserver -- bunx my-mcp-server
# remote (Streamable HTTP by default; --transport sse for legacy servers)
bun run main.ts mcp add --transport http postman https://mcp.postman.com/minimal \
--header 'Authorization: Bearer ${POSTMAN_API_KEY}'
bun run main.ts mcp list # connects to each server and reports ✓/✗
bun run main.ts mcp get postman
bun run main.ts mcp remove postman
Project servers are written to mcp.json; without one, the JSON files under mcp/ are the project list (see bundled MCP servers).
Put single quotes around ${VAR} so your shell doesn't expand it; the config then stores the reference, not the secret. Other options: --scope project|user, --description, --always-loaded and --force. See MCP servers for the config files and per-server options.
Agent API and web chat
The TypeScript app can serve its configured agent over an OpenAI Chat Completions-compatible HTTP API. The API uses the same tools, memory and permission rules as the terminal app, and it supports streamed replies, conversation IDs, per-turn model and sampling settings, tool selection, and retrieval controls.
API_HOST=127.0.0.1 API_PORT=8791 bun run main.ts api serve
# For a network interface, set a private token:
API_HOST=0.0.0.0 API_TOKEN=your-long-random-token bun run main.ts api serve
Clients send POST /v1/chat/completions with a Bearer token and a messages array. Use stream: true for server-sent events and pass the returned conversation_id on later turns. GET /v1/models lists available models; GET /v1/settings reports active defaults. Requests can set model, system_prompt, sampling values, tools, permission_mode (auto, ask, plan) and retrieval (auto, on, off). Configured server permissions remain in force.
When an action needs approval, the stream sends a andromeda.approval event. Clients can also check GET /v1/approvals?conversation_id=… and answer with POST /v1/approvals/:id using approve, always or deny. An unanswered approval expires after five minutes.
Attachments, audio and browser UI
Chat requests can use multipart form data with a JSON payload and repeated attachments fields: up to eight files, 12 MiB each and 24 MiB total. Uploaded files are saved in the API workspace under .andromeda/web-uploads/ for the agent to inspect. Audio endpoints list configured voices at GET /v1/audio/voices, synthesize speech at POST /v1/audio/speech and transcribe multipart audio at POST /v1/audio/transcriptions.
The standalone React chat in web/ connects to this API, stores conversations and connection settings in browser storage, and supports attachments, microphone input, spoken replies, approvals, and inline weather, image, audio, YouTube and map cards. Run it with cd web && bun install && bun run dev; allow its exact origin in api.cors_origins (or API_CORS_ORIGINS). Keep the API on loopback for local use. Binding to a network interface requires api.token; use HTTPS when exposing it beyond a trusted local network.
Configure api.host, api.port, api.token and api.cors_origins in andromeda.yml, or use API_HOST, API_PORT, API_TOKEN and API_CORS_ORIGINS.
One turn path for terminal and web
The TUI, the REPL and both API chat endpoints run a turn through the same code (runTurn in src/app/turn.ts). Web chats now get what the terminal always had: the workspace refresh, UserPromptSubmit and Stop hooks, file checkpoints for /rewind, and the "Keep going?" question when a turn reaches the tool-call limit, answered through the approval broker. Attachments are read the same way everywhere: text, PDF and Office files go inline, images go to vision models (or to view_image for models without vision), and other files are passed by path. Each web conversation keeps its own checkpoint turn, so concurrent chats no longer share one. reasoning_effort (none to max) picks a thinking level, and GET /v1/models reports the levels each model accepts.
Web app pages and accounts
Besides chat, the web app has a Command Center (every live run and subagent shown as a crew member at a station, with what it is doing and who it reports to), Code Review (review picked paths, local changes or a pull request; findings come with a suggested fix you approve or reject, plus optional Snyk and SonarQube scans), Library, Memory (browse, search and prune what is stored in Qdrant), Skills (install from a repository or folder, including every skill in it at once, and switch skills on or off), Scheduled, Images and Settings.
The API supports password accounts for the web app: POST /v1/auth/signup, /v1/auth/login and /v1/auth/logout, GET /v1/auth/me, and GET/PATCH /v1/auth/users for admins. The first account becomes an admin and owns conversations from before accounts existed. Passwords are hashed with scrypt and only a hash of each 30-day session token is stored, in .andromeda/web-auth.db (see Database). Users see only their own memories; admins can see everyone's.
Chat from WhatsApp
Message Andromeda from your phone. It links to your own account like WhatsApp Web, using an unofficial client (Baileys).
Unofficial clients are against WhatsApp's terms, and the number could be banned.
bun run main.ts whatsapp link # scan the QR code: WhatsApp → Settings → Linked devices
bun run main.ts whatsapp status
bun run main.ts whatsapp unlink
Then set whatsapp.enabled: true in andromeda.yml, start Andromeda, and message yourself:
whatsapp:
enabled: true
primary_user: "+27810000000" # only this identity receives owner memory and private tools
allowed: [] # other numbers, digits with country code, e.g. ["447700900123"]
notify: true # also send schedule results and check-ins to your own chat
reply_prefix: "🤖 "
- Who it answers: your own "Message yourself" chat and the numbers in
allowed. Everyone else is ignored. Setprimary_userto your exact phone number; allowed contacts get separate conversations and do not receive owner memory or private tools. - Conversations: each chat is its own thread, sharing long-term memory with the terminal. Replies start with the prefix so you can tell them apart in your own chat.
- Approvals: anything that needs approval is asked in the chat; reply yes, always or no (no answer in five minutes means no).
- Media: images, documents and voice notes are supported; voice notes can receive spoken replies. Contacts and outgoing messages are available through tools, with approval required before sending.
Catching up: messages that arrive while the PC sleeps or Andromeda is stopped are answered when it reconnects, if they are within whatsapp.catchup_hours (default 24). A backlog in one chat gets a single reply that says how late it is, and a reply cut off by a restart is answered again. Ask Andromeda to "catch up on WhatsApp" to run the whatsapp_catch_up tool by hand. Images now reach the model directly, without a tool call.
Voice calls
Live calls use Meta's WhatsApp Business Calling API on a separate business number; linked-device chat cannot carry call audio. Configure whatsapp.calls, with the Meta app secrets in .env or under whatsapp.calls in andromeda.yml. Andromeda only answers calls you start; it refuses requests that could place a billed call and hangs up if a call is reported as business-initiated. Calls that need tool approval are declined because nobody can approve them by voice.
Always-on service and triggers
Schedules, event triggers and heartbeat check-ins only run while Andromeda is active. The background daemon keeps them running after you close the terminal:
./start.sh background # start detached
./start.sh status
./start.sh logs 50
./start.sh stop
bun run main.ts daemon install # start at login
bun run main.ts daemon uninstall
The installed service starts with your user session on macOS, Ubuntu or Windows. It owns WhatsApp, schedules, triggers, webhooks and heartbeat while it runs; a TUI or REPL opened alongside it leaves those services to the daemon. Use /daemon to inspect its status.
Event triggers
Ask Andromeda to run a prompt when new mail arrives, an event is approaching, files change, or a webhook fires. Manage configured triggers with /triggers. Gmail and calendar triggers use the Google MCP login; file triggers watch workspace paths; webhook triggers accept a secret token on the local endpoint. The first polling check records existing state, so old mail or files do not fire as new events.
Trigger runs are unattended: event content is treated as untrusted input, tool approvals are enforced, and each trigger is rate limited. The daemon log records results without saving prompts or event contents. Plugins can add trigger kinds of their own.
Web app and usage dashboard
Run the web client in development with bun run main.ts:web and the API with bun run main.ts:api. After bun run build:web, the API serves the built web app itself. The API starts the usage dashboard at http://127.0.0.1:8790 by default; run it separately with bun run main.ts:dashboard, or pass --no-dashboard to api serve to suppress it.
See Web app & dashboard for the current navigation, live-session and history views, Model Hub, memory and library, automations, dashboard Playground and Flows, traces, connected devices, and safe remote access.