Skip to content
letmoa.run

CLI reference

FlagDefaultDescription
-pPrompt text, or @file to read from file. Without it, the prompt is read from piped stdin
-modelsonnetModel alias or provider/model-id
-thinkingmediumoff, low, medium, high, xhigh — see Thinking levels
-max-turns0Max agent turns per run. 0 uses max_turns from config (unlimited if unset)
-max-budgetfrom configMax USD spend per run, subagents included (-1 sentinel = use config.json; an explicit 0 means unlimited)
-yolofalseDisable sandbox and all permissions
-permissionsfrom configyolo, ask, auto. A one-shot run has nobody to approve, so a call that would ask is denied: cover what the run needs with -allow
-permissions-modelModel for auto mode evaluator
-path-scopederivedworkspace or unrestricted
-allowPermission pattern (repeatable), e.g. "Bash(go:*)"
-allow-pathAllow extra directory outside workspace (repeatable)
-outputtexttext or json (JSON-lines)
-loginanthropic, openai, xai (SuperGrok/X OAuth device login), meta (Muse OAuth device login), openai-transcribe
-logoutRemove stored credentials for provider

-output json emits one JSON object per line: lifecycle, message updates, tool execution, progress and errors.

It also reports what the run cost, so a benchmark or a cost ledger does not have to reconstruct spending from session files:

  • message_usage — one line per completed assistant message, with role (main or subagent), subagent_id for a child, provider, model, the token fields (input, output, cache_read, cache_write) and cost_usd.
  • subagent_end — the child’s terminal cost_usd. A resumed child’s figure covers its whole history, so when it differs from what the run itself spent the line also carries run_cost_usd; only that enters the summary total.
  • summary — the run total cost_usd, the aggregate usage, and by_model entries split by provider, model and role, each with its messages count, tokens and cost.
{"type":"message_usage","role":"main","provider":"anthropic","model":"claude-sonnet-5","input":1204,"output":318,"cache_read":8192,"cache_write":0,"cost_usd":0.008}
{"type":"summary","turns":2,"tools_completed":3,"files_touched":["main.go"],"elapsed_seconds":41,"cost_usd":0.0143,"usage":{"input":1600,"output":420,"cache_read":8192,"cache_write":0},"by_model":[{"provider":"anthropic","model":"claude-sonnet-5","role":"main","messages":2,"input":1600,"output":420,"cache_read":8192,"cache_write":0,"cost_usd":0.0143}]}

The main run’s cost comes from the run’s own total and children’s from their terminal run cost, so no dollar is counted twice. Costs are rounded to the micro-dollar, well below any provider’s billing granularity.

Terminal window
moa version # or: moa --version, moa -v

Prints the version, commit, and build date.

Terminal window
moa update # download, verify, and install the latest release
moa update --check # only report current vs latest, install nothing
FlagDefaultDescription
--checkfalseReport whether an update is available without installing it

Downloads the release archive for your platform, verifies its SHA-256 against the release checksums.txt, and replaces the running binary in place. It never restarts anything: restart Moa yourself afterwards.

Binaries installed through Homebrew or Nix are refused with a pointer to the package manager (brew upgrade moa). If the binary’s directory is not writable, the command fails with a clear message rather than escalating privileges. Unlike the passive update notice, moa update ignores MOA_NO_UPDATE_CHECK: it is an explicit request.

Terminal window
moa serve [--host 127.0.0.1] [--port 8080] [--model sonnet] [--token <secret>] [--allowed-hosts <names>]
FlagDefaultDescription
--host127.0.0.1Bind address (use 0.0.0.0 for remote access)
--port8080HTTP port
--modelsonnetDefault model for new sessions
--tokenShared secret for opt-in auth (or MOA_SERVE_TOKEN). When set, requests need a valid session cookie or ?token=<secret>
--automation-tokenShared secret enabling the Automation API (or MOA_AUTOMATION_TOKEN), presented as Authorization: Bearer <secret>. Separate from --token; without it those routes do not exist
--allowed-hostsComma-separated extra Host names accepted by the anti DNS-rebinding check (localhost/IP literals always allowed; e.g. a Tailscale MagicDNS name)
--preview-port0Initial local port for the Live Preview proxy. With 0, Moa suggests its own port plus one. The listener binds 127.0.0.1 on first use and closes when the preview does
--preview-public-urlLegacy fallback for API clients that do not send their own address. The web UI derives the address per browser from Moa’s host and the listener port; this flag is not required

See Web UI for details.

Terminal window
moa hooks add <source> --project DIR [--when-none inbox|create] [--when-many inbox|latest] [--model M --thinking T --yolo] [--autorun]
moa hooks add <source> --session ID
moa hooks add <source> --owner ID_OR_NAME
moa hooks add <source> --inbox
moa hooks list [--show-secrets]
moa hooks rm <source>
FlagDefaultDescription
--projectTarget a project directory ({project: DIR})
--sessionTarget a live session id ({session: ID})
--ownerTarget a project owner by id or name ({owner: ID_OR_NAME}): events go to the owner’s conversation
--inboxfalseLeave events in the inbox
--when-noneinboxWhen the project has no live session: inbox or create
--when-manyinboxWhen the project has several live sessions: inbox or latest
--modelModel for when-none=create
--thinkingThinking level for when-none=create
--yolofalseCreate sessions in yolo permission mode
--autorunfalseStart a turn on delivery to an idle session. Off by default: the event is recorded without running
--show-secretsfalselist only: print the full /hooks/<source>/<secret> path

add needs exactly one of --project, --session, --owner or --inbox.

add generates a 32-byte url-safe secret, writes it to the global config, and prints the hook path on its own line, preceded by Hook URL path (contains the secret; store it in the provider now):. See Event hooks.

AliasResolves to
sonnetclaude-sonnet-5-5
opusclaude-opus-5-5
haikuclaude-haiku-4-5-20251001 (Claude Haiku 5.5 is claude-haiku-5-5, by ID only)
fableclaude-fable-5-1
codexgpt-5.3-codex
codex-sparkgpt-5.3-codex-spark
codex-5.2gpt-5.2-codex
astragpt-6-astra
gpt-6gpt-6-astra
grokgrok-4.7 (xAI)
grok-4.7-buildgrok-4.7 (the subscription backend’s name for it)
grok-4.6-buildgrok-4.6 (the subscription backend’s name for it)
grok-4.5-buildgrok-4.5 (the subscription backend’s name for it)
musemuse-spark-1.3 (Meta)
solgpt-6.1-sol
daybreakgpt-daybreak-blue-latest (moving alias for the current flagship, with safeguards for defensive cybersecurity work; needs Daybreak provisioning)
terragpt-5.6-terra
lunagpt-6-luna
gpt-5.6gpt-5.6-sol
gpt5gpt-5.5
gpt5.5gpt-5.5
gpt5-minigpt-5.4-mini

You can also use canonical IDs (claude-sonnet-5) or provider-prefixed IDs (anthropic/claude-sonnet-5). Some known models have no alias and are reachable only by ID: claude-fable-5, claude-opus-5, claude-sonnet-5, claude-opus-4-8, claude-haiku-5-5, gpt-5.6-luna, grok-4.5, grok-4.6, muse-spark-1.3-contributor (cheaper, but Meta trains on its prompts). Provider-prefixed custom IDs, including xai/<model-id>, are accepted, but context-window management and any unverified pricing metadata are disabled for them.

off, low, medium, high, xhigh are the canonical levels, but what a model does with them differs:

  • xAI Grok requires reasoning: off/low collapse to low, xhigh to high. Only low, medium, high are distinct there.
  • Meta Muse Spark always reasons: off becomes low.
  • Claude Fable 5, Fable 5.1 and Opus 5.5 think on every turn. off is not a real setting for them: it runs at the model’s default effort (high on Fable, medium on Opus 5.5), and the web selector hides the option.
  • Claude Sonnet 5.5 cannot turn thinking fully off. off sends its lowest setting, between_tools: no up-front thinking, only short progress notes between tool calls, at the default high effort.
  • Claude Haiku 5.5 (claude-haiku-5-5, 1M-token context, up to 128K tokens of output) thinks adaptively at medium effort by default and accepts xhigh; off sends disabled, which the API accepts up to high effort. It costs $0.10 / $0.50 per million input / output tokens for prompts up to 100K tokens; above that the whole request is priced at $0.50 / $2.50.
  • xhigh is its own effort level on every Anthropic model except Haiku 4.5, which caps it at high. OpenAI models accept xhigh as its own effort level. On GPT-6 Astra, the five UI positions map to low, medium, high, xhigh, and max respectively, so off is Astra’s lowest reasoning effort rather than disabled reasoning.

Fast mode buys premium speed at a premium price on the same model. It is a per-session switch in the web UI (GET/PATCH /api/sessions/{id}/fast), not a CLI flag, and only some models can serve it:

ProviderModels that support itWhat it costs
AnthropicOpus models only2.5× faster, billed as separate usage credits
OpenAIgpt-6-astra, gpt-6.1-sol, gpt-6-sol, gpt-6-lunaFast mode, 2× the token rate
OpenAIgpt-5.6-solup to 2.5× faster, burns credits 2×
OpenAIgpt-5.6-terra, gpt-5.6-luna, gpt-5.4-mini, gpt-5.3-codexFast mode, burns credits 2×
OpenAIgpt-5.5Fast mode, burns credits 2.5×
xAIthe whole cataloguepriority queue, 2× the token rate

Turning it on for a model that cannot serve it is not an error: the setting is not stored, and the session stays at standard speed.

The session cost (cost_usd, the budget guardrail) charges a fast request at the provider’s premium: 2× the standard rate on Anthropic (for example $8/$40 per MTok on Opus 5.5 and $10/$50 on Opus 5 and 4.8, cache multipliers on top), 2.5× on gpt-5.5 and 2× on every other supported OpenAI model, and 2× on xAI. The multiplier applies only to turns the provider actually served at the premium tier — Anthropic reports usage.speed, OpenAI and xAI echo service_tier — so a turn that fell back to standard speed is billed as standard.

Terminal window
# one-shot prompt
moa -p "fix flaky tests"
# explicit provider/model
moa -model openai/gpt-5.3-codex -p "optimize this query"
# Grok 4.7 with its supported thinking levels
moa -model grok -thinking high -p "review this change"
# budget-limited run
moa -max-budget 0.50 -p "refactor auth module"
# permissions with allow patterns
moa -permissions ask -allow "Bash(go:*)" -allow "Write(*.go)" -p "fix the build"
# allow access to extra directory
moa -allow-path /tmp/shared-data -p "summarize the reports in /tmp/shared-data"
# web UI on the network
moa serve --host 0.0.0.0 --port 8080