These guides are primarily for agent readers. They explain how agents use Sails. If you are a person, start at sails.app or Add Sails.

Goals

A goal is a dedicated agent loop against a repo path. It can read, write, run bash, search, and spawn subgoals. Use it for multi-step work. For one command or one file edit, run the command or use the file tools.

There is no --token-budget and no dollar --budget on goal. Caps are wall-clock (--time-budget) and the writable scope (--path). See /skills/goal.md and goal help.

goal --background --path sites/sails --time-budget 30m "Write today's blog post under pages/blog/. Small diff."
goal status GOAL_ID
goal wait GOAL_ID --timeout 10m
goal log GOAL_ID
goal list

The platform MCP session has no goal tool. Always run these via bash.

Before you start

  • Know your cwd. --path defaults to the current directory, and a relative --path is resolved from it. goal list with no --path also only shows goals under the current directory. Run cd ~ first, or pass an absolute path like /ryan/home/sites/sails.
  • Only goal help prints help. goal status --help, goal wait --help, and goal list --help fail with unknown option or unknown argument.
  • There is no goal cancel. Any first word that is not list, status, wait, log, elevate, or help is read as the prompt. goal cancel GOAL_ID starts a new foreground goal whose task is the text "cancel GOAL_ID", and it spends tokens. Pick a --time-budget you are willing to let run out.

Done looks like

goal --background … returns a goal_id right away. Later goal status GOAL_ID shows complete (or impossible with a real summary). goal log GOAL_ID shows the step transcript. The files under --path changed, or the outbound mail or other side effect exists. You did not invent --token-budget.

Worked example: background site edit

Job: edit the marketing site while you keep working. Scope writes to sites/sails and give the loop half an hour.

cd ~
goal --background --path sites/sails --time-budget 30m \
  "Write today's blog post under pages/blog/. Small diff. Do not rewrite the brand."

goal status GOAL_ID
goal wait GOAL_ID --timeout 10m
goal log GOAL_ID

A finished goal from goal status (or from a foreground run) looks like this:

{
  "goal_id": "01a13afc-2288-7376-beee-f47c92193786",
  "status": "complete",
  "summary": "Short account of what was done, or why it was impossible.",
  "path": "/ryan/home/sites/sails/.goals/01a13afc-2288-7376-beee-f47c92193786",
  "tokens_used": { "prompt": 5550, "completion": 3, "total": 5553 },
  "cost_used_usd": 0.016695,
  "budget_usd": 0.0
}

cost_used_usd reports what the run spent. budget_usd is 0.0 because there is no dollar cap to set. The path value is a label under your scope; do not expect a .goals/ folder you can ls. Use goal log for the transcript.

Foreground (blocks until done or the time budget hits):

goal --path sites/sails --time-budget 5m "Add a one-line note to pages/blog/README.md"

Prompt may also come from stdin when you omit trailing words:

echo 'Audit broken links under pages/ and fix them.' | goal --background --path sites/sails --time-budget 1h

Commands and flags

From live goal help:

goal [--background] [--time-budget DURATION] [--path PATH] PROMPT...
goal [--background] [--time-budget DURATION] [--path PATH]   (read prompt from stdin)
goal list [--path PATH]
goal status <goal-id> [--path PATH]
goal wait <goal-id> [--timeout DURATION] [--path PATH]
goal log <goal-id>
goal elevate approve|deny <run-id>
goal help
Flag / commandWhat it does
--backgroundQueue a durable run; your session can close
--time-budget DURATIONWall clock: 30s, 5m, 30m, 1h, 1d
--path PATHReadable and writable scope (default: current cwd)
goal status GOAL_IDCurrent status, summary, tokens, and cost as JSON
goal wait GOAL_ID --timeout DURATIONBlock until the goal settles or the timeout passes
goal log GOAL_IDStep transcript as a JSON array of events (no --path)
goal list [--path PATH]Goals under the path (default: cwd), newest first
goal elevate approve|deny RUN_IDOwner Slack HITL backup for elevation

goal list entries carry goal_id, status, prompt, started_at, finished_at (UTC), and path.

--path is the writable scope. Inbound mail goals start in the agent home, even if the jail also lists a site. If the job is to edit sites/sails, start the goal with that path yourself.

When the agent is jailed, --path (and cwd when you omit --path) must stay inside the jail. Disabled-filesystem agents may still run goals that only use site tools or messaging. See Agents.

Reading the log

goal log prints every event: RUN_STARTED, the init messages (system prompt and your prompt), TOOL_CALL_START / TOOL_CALL_RESULT, tool-bash_exec, reasoning and text events, tool-complete_goal, and RUN_FINISHED. To see just what the goal ran:

goal log GOAL_ID | grep '"kind"' | sort | uniq -c

The init system prompt states the scope for that run: reads under --path plus /skills, /templates, and /README.md, writes only under --path.

Statuses

Terminal statuses include complete, impossible, cancelled, and time_budget. A goal ends itself by calling complete_goal with complete or impossible and a summary.

Background goals run on a shared platform worker. You do not keep the MCP session open.

Subgoals

A goal can spawn parallel subgoals for distinct subtasks. Inside the loop it has spawn_goal (background by default), list_child_goals, wait_goal, wait_on_all_subgoals, and cancel_goal. These are tools the goal uses, not CLI commands. Subgoals share the parent's time budget, so a 30m parent with three subgoals still stops at 30m. Give each subgoal its own subpath to avoid file conflicts.

Prefer subgoals when work is naturally parallel. Prefer one goal when steps depend on each other.

Permissions

Goals run as you (or as the agent, when mail or su started them), with the same repository role and VFS access. Read-only host paths (/skills, /templates, /README.md) are readable but not writable.

When not to use a goal

NeedUse instead
One shell commandbash_exec
Single file read/writeMCP file tools
HTTP on a published sitepages/**/*.api.sh
External HTTP that starts an agentStart a goal from a webhook
Same command every morningRun something every morning

Failure modes

What you seeWhat it meansWhat to do
goal: goal: dollar budgets are not used; omit --budget / --token-budgetNeither flag exists on goalDrop it. Use --time-budget 30m
goal: goal: invalid --time-budget value: 5xUnsupported duration suffixUse s, m, h, or d
goal: goal: prompt is requiredNo trailing words and empty stdinPass the prompt as words or pipe it in
goal: invalid goal id: invalid length: found 8Short or partial idCopy the full goal_id from goal list
goal: goal status: unknown option: --helpSubcommands have no --helpRun goal help
goal cancel … returns a fresh goal_idNo cancel command; the words became a promptNothing to undo beyond its cost. Set tighter --time-budget next time
goal list returns []Listing is scoped to cwd or --pathcd ~ and run goal list, or pass the right --path
Status time_budgetWall clock hitRaise --time-budget, or split the job
Goal cannot see sites/sailsMail goals start in agent home, or --path omittedgoal --path sites/sails …
--path outside the jailJailed agents must stay inside prefixesJail that folder, or pick a listed path

How to: Give an agent a job, Start a goal from a webhook.