---
meta:
title: Extending and automating
description: Scale Muse Code beyond a single interactive session — parallel subagents, reusable skills, lifecycle hooks, MCP servers, and headless runs for CI.
keywords: multi-agent, subagents, worktree isolation, skills, hooks, MCP, headless, CI, muse exec
cms:
alias: /model-api/docs/muse-code/extending
target: aidmc
---
# Extending and automating
Take Muse Code past a single interactive session: distribute work across parallel agents, package reusable workflows as skills, wire your own commands into the lifecycle with hooks, connect external tools through MCP, and run the agent non-interactively in CI.
## Subagents and multi-agent {#multi-agent}
Distribute a large job across a team of agents that work in parallel, and steer them from one place. A lead session spawns child agents, hands each a bounded task, and keeps managing the group while they run.
A **subagent** is a child agent that the lead spawns for one bounded task. **By default, children share the lead's workspace**, so parallel writes can collide. Turn on worktree isolation, and each child works in its own git worktree checked out from the lead's commit:
```bash
cd /path/to/your/repo
muse --subagent-worktree-isolation
```
With isolation on, the runtime creates and owns one git worktree per child, including read-only children. Each child edits only inside its worktree, and the lead's working copy stays untouched. Isolation is opt-in and needs a git repository. In a non-git workspace, Muse Code ignores the flag without an error, and children share the lead's workspace instead.
Use subagents when a job splits into tasks that are each bounded, independently verifiable, and would otherwise contend for the same files. Keep the work on one agent when the steps are strictly sequential.
**Steer the agents.** From the interactive session, `/agent` (or `/subagents`) opens the subagents view, and the `/agent-*` commands steer a child by id: `/agent-note`, `/agent-followup`, `/agent-interrupt`, `/agent-stop`, `/agent-resume`, `/agent-reopen`, `/agent-close`. Internally, the lead manages the parallel run through native tools: `subagent_spawn`, `subagent_status`, `subagent_send_message`, `subagent_cancel`, `subagent_wait`, and `subagent_read_result`.
A few properties worth knowing:
- The runtime runs several children at once. The default cap scales with the machine: roughly core count minus two, clamped between 2 and 16. Extra spawns queue until slots free up.
- Children run one level deep: a child cannot spawn its own children.
- Cancellation is cooperative: a cancelled child that never reaches a checkpoint keeps running, and one mid-write finishes that write.
- The runtime journals every spawn, status change, and control action, so you can answer "which child did what, and when" after the run. Each child commits inside its own worktree, and the lead reviews or merges each commit.
### Background observer agents {#observer-agents}
Alongside the main session, Muse Code runs a team of background observer agents. Each one watches a single axis of quality, and can insert a short advisory into the main agent's next turn without an interruption. An observer never answers for you: it proposes, a reconciler decides, and only an accepted proposal reaches the main agent.
- **Memory recall**: surface a note from local project [memory](/docs/muse-code/configuration#local-memory) relevant to the next reply.
- **Skill recall**: surface a project [skill](#skills) the task should load first.
- **Goal tracking**: hold the agent to a declared [goal](/docs/muse-code/interactive#goals-loops) and decline to close the turn until the work is done.
- **Verification**: check that the agent ran the work it claims it finished.
Memory, skill, and goal recall are on by default. Verification is off by default. Toggle any of them in your [settings file](/docs/muse-code/configuration#settings-file).
> [!NOTE]
> Three of the four observers run by default, and each makes its own model calls, so they add token usage in addition to the main session.
## Skills {#skills}
A skill packages a repeatable workflow the agent can load on demand: a set of instructions, and optionally tools and files, that turn "explain how we do X" into a single invocation. Muse Code ships built-in skills, and you can add your own or import them from other agents.
Skills load from three sources:
- **Built-in**: skills that ship with Muse Code.
- **User**: your account-wide skills, in `$XDG_CONFIG_HOME/muse/skills` (and `~/.agents/skills`), available in every project.
- **Project**: skills committed to a repo under `<repo>/.agents/skills/<skill-id>/SKILL.md`, shared with anyone who clones it. Muse Code also scans repo-local `.codex/skills` and `.claude/skills`.
Manage and invoke skills:
```bash
muse skills list # every skill, all sources
muse skills inspect <skill-id>
muse skills enable <skill-id> --scope project
muse skills install ./my-skill --scope user
muse skills validate ./my-skill # check it before installing
muse skills import --from claude # or: --from codex
```
In an interactive session, invoke a skill with a slash command. Built-in skills include `/plan` (turn a task into a grounded, decision-complete plan, then stop for approval), `/grilling` (stress-test a plan or design before you build), and `/taste` (a design-quality gate for frontend work). A related command, `/grill-with-docs`, also records the settled decisions into your project docs. The agent also loads a relevant skill on its own when a [background observer](#observer-agents) surfaces one.
## Hooks {#hooks}
Wire your own shell commands into Muse Code's lifecycle. A hook binds a shell command to a lifecycle event. When the event fires, Muse Code runs the command and acts on its result: enforce a check, format code, or block an action before it happens, without a change to the agent itself.
Hooks come from three sources:
- **Project**: committed to the repo at `<project-root>/.muse/hooks.json`.
- **User**: your machine-wide hooks, defined in your [settings](/docs/muse-code/configuration#settings-file).
- **Managed**: a file that the `managed_hooks_path` setting points to, for centrally administered hooks.
You must **trust** User and Project hooks before they run (`muse hooks trust <key>`). Muse Code treats managed hooks as pre-approved, and runs them without a trust step. So whoever controls the managed hooks file controls what executes.
> [!WARNING]
> Only add hooks whose commands you've read. A hook's command runs directly through your shell, **outside** the [sandbox and approval](/docs/muse-code/permissions) that govern the agent's own tools. The only hardening is a cleared environment with a small allowlist.
### Lifecycle events {#hook-events}
A hook binds to exactly one event. The available events are `SessionStart`, `UserPromptSubmit`, `PreToolUse`, `PermissionRequest`, `PostToolUse`, `PreLLMCall`, `PostLLMCall`, `PreCompact`, `PostCompact`, `SubagentStart`, `SubagentStop`, and `Stop`.
Manage and test hooks:
```bash
muse hooks list # show configured hooks and their keys
muse hooks validate # check hook configuration
muse hooks trust <key> # trust a hook by key
muse hooks run <key> --fixture ./fixture.json
```
`muse hooks run` fixture-tests a hook, but only after you trust and enable it. The fixture is a JSON file with a required `event` (that matches the hook's event) and a required `stdin` object that holds the payload the hook receives:
```json
{ "event": "PreToolUse", "stdin": {} }
```
## MCP servers {#mcp}
Connect external tools through the Model Context Protocol (MCP). Declare servers in the `mcp_servers` block of your [settings file](/docs/muse-code/configuration#settings-file). Each server takes a `transport`: either `stdio` (with `command`, `args`, `env`) or `streamable_http` (with `url`, `headers`). It also takes `enabled`, `mode`, and an optional `framing`:
```json
{ "mcp_servers": {
"my-tools": { "transport": "stdio", "command": "my-mcp-server", "args": [] }
} }
```
A server's `mode` defaults to `required`. If a required server fails to start, the whole run aborts. Set `mode` to `optional` for a server that Muse Code should skip with a warning when it's unavailable.
> [!WARNING]
> Only connect servers you trust. **MCP tools are not sandboxed.** Unlike shell commands, an MCP server runs as an ordinary child process (stdio) or a direct network connection, outside the filesystem and network [sandbox](/docs/muse-code/permissions#sandbox). Approval still applies, but containment does not.
## Headless and CI {#headless}
Run Muse Code without a terminal UI. `muse exec` takes one prompt, runs it to completion, and exits, so you can drive the agent from a script, a job, or a CI pipeline:
```bash
muse exec "Update the changelog for the latest release and run the tests."
muse exec --prompt-file ./task.txt
muse exec --json "Refactor the auth module and run the tests." # JSONL events on stdout
```
`muse exec` prints the agent's output to stdout and returns a process exit code. The code reflects **how the run ended, not whether the work is correct**: `0` when the turn completes, `1` when it fails or is cancelled (including a `--max-model-steps` limit), `2` for a usage error, and `130` or `143` on SIGINT or SIGTERM. An agent can finish its turn and still report that the tests fail, and it exits `0`. So gate on your own test command, not on Muse Code's exit code alone.
**Control approvals for automation.** A non-interactive run has no one to answer an approval prompt, so choose a posture in advance:
- **`--disable-approval`**: skip approval prompts, but keep the [sandbox](/docs/muse-code/permissions#sandbox) on to contain what runs.
- **`--yolo`**: disable approval and the sandbox for a fully unattended run. It **also trusts the workspace**, and loads the checkout's `AGENTS.md`, rules, and skills. On a fork or pull-request checkout, those are attacker-controlled, so use `--yolo` only on trusted code in a disposable, isolated container.
Cap a run's work with `--max-model-steps` so a stuck task can't loop indefinitely.
> [!NOTE]
> The sandbox in CI requires the OS sandbox to work on the runner. On Linux, that means a working bubblewrap and a non-musl build, because musl artifacts ship without the sandbox helper. On a host without it, every sandboxed shell command aborts as an environment failure. See [permissions](/docs/muse-code/permissions#sandbox).
**Resume and audit non-interactively.** Headless runs are [sessions](/docs/muse-code/interactive#session-model) like any other. To continue an interrupted job non-interactively, use `exec` with the session id. `muse resume` opens the interactive UI and is not a headless surface:
```bash
muse exec --session-id <uuid> "Continue the task."
muse export --session <uuid> --out run.json
```
Muse Code refuses a workspace mismatch unless you pass `--allow-workspace-switch`. The export document embeds the CLI version that produced it, so pin the version if you gate on its hash.
## Next steps
- Keep the whole team inside the same [permissions and sandbox boundary](/docs/muse-code/permissions).
- Give skills and subagents the project facts they rely on in [configuration and context](/docs/muse-code/configuration).
- Connect external agent tooling to the model from the [coding agents](/docs/coding-agents) guide.
Extending and automating
Take Muse Code past a single interactive session: distribute work across parallel agents, package reusable workflows as skills, wire your own commands into the lifecycle with hooks, connect external tools through MCP, and run the agent non-interactively in CI.Subagents and multi-agent
Distribute a large job across a team of agents that work in parallel, and steer them from one place. A lead session spawns child agents, hands each a bounded task, and keeps managing the group while they run.A subagent is a child agent that the lead spawns for one bounded task. By default, children share the lead's workspace, so parallel writes can collide. Turn on worktree isolation, and each child works in its own git worktree checked out from the lead's commit:With isolation on, the runtime creates and owns one git worktree per child, including read-only children. Each child edits only inside its worktree, and the lead's working copy stays untouched. Isolation is opt-in and needs a git repository. In a non-git workspace, Muse Code ignores the flag without an error, and children share the lead's workspace instead.Use subagents when a job splits into tasks that are each bounded, independently verifiable, and would otherwise contend for the same files. Keep the work on one agent when the steps are strictly sequential.Steer the agents. From the interactive session, /agent (or /subagents) opens the subagents view, and the /agent-* commands steer a child by id: /agent-note, /agent-followup, /agent-interrupt, /agent-stop, /agent-resume, /agent-reopen, /agent-close. Internally, the lead manages the parallel run through native tools: subagent_spawn, subagent_status, subagent_send_message, subagent_cancel, subagent_wait, and subagent_read_result.A few properties worth knowing:- •The runtime runs several children at once. The default cap scales with the machine: roughly core count minus two, clamped between 2 and 16. Extra spawns queue until slots free up.
- •Children run one level deep: a child cannot spawn its own children.
- •Cancellation is cooperative: a cancelled child that never reaches a checkpoint keeps running, and one mid-write finishes that write.
- •The runtime journals every spawn, status change, and control action, so you can answer "which child did what, and when" after the run. Each child commits inside its own worktree, and the lead reviews or merges each commit.
Background observer agents
Alongside the main session, Muse Code runs a team of background observer agents. Each one watches a single axis of quality, and can insert a short advisory into the main agent's next turn without an interruption. An observer never answers for you: it proposes, a reconciler decides, and only an accepted proposal reaches the main agent.- •Memory recall: surface a note from local project memory relevant to the next reply.
- •Skill recall: surface a project skill the task should load first.
- •Goal tracking: hold the agent to a declared goal and decline to close the turn until the work is done.
- •Verification: check that the agent ran the work it claims it finished.
Memory, skill, and goal recall are on by default. Verification is off by default. Toggle any of them in your settings file.Three of the four observers run by default, and each makes its own model calls, so they add token usage in addition to the main session.
A skill packages a repeatable workflow the agent can load on demand: a set of instructions, and optionally tools and files, that turn "explain how we do X" into a single invocation. Muse Code ships built-in skills, and you can add your own or import them from other agents.Skills load from three sources:- •Built-in: skills that ship with Muse Code.
- •User: your account-wide skills, in
$XDG_CONFIG_HOME/muse/skills (and ~/.agents/skills), available in every project. - •Project: skills committed to a repo under
<repo>/.agents/skills/<skill-id>/SKILL.md, shared with anyone who clones it. Muse Code also scans repo-local .codex/skills and .claude/skills.
Manage and invoke skills:In an interactive session, invoke a skill with a slash command. Built-in skills include /plan (turn a task into a grounded, decision-complete plan, then stop for approval), /grilling (stress-test a plan or design before you build), and /taste (a design-quality gate for frontend work). A related command, /grill-with-docs, also records the settled decisions into your project docs. The agent also loads a relevant skill on its own when a background observer surfaces one.Wire your own shell commands into Muse Code's lifecycle. A hook binds a shell command to a lifecycle event. When the event fires, Muse Code runs the command and acts on its result: enforce a check, format code, or block an action before it happens, without a change to the agent itself.Hooks come from three sources:- •Project: committed to the repo at
<project-root>/.muse/hooks.json. - •User: your machine-wide hooks, defined in your settings.
- •Managed: a file that the
managed_hooks_path setting points to, for centrally administered hooks.
You must trust User and Project hooks before they run (muse hooks trust <key>). Muse Code treats managed hooks as pre-approved, and runs them without a trust step. So whoever controls the managed hooks file controls what executes.Only add hooks whose commands you've read. A hook's command runs directly through your shell, outside the sandbox and approval that govern the agent's own tools. The only hardening is a cleared environment with a small allowlist. A hook binds to exactly one event. The available events are SessionStart, UserPromptSubmit, PreToolUse, PermissionRequest, PostToolUse, PreLLMCall, PostLLMCall, PreCompact, PostCompact, SubagentStart, SubagentStop, and Stop.Manage and test hooks:muse hooks run fixture-tests a hook, but only after you trust and enable it. The fixture is a JSON file with a required event (that matches the hook's event) and a required stdin object that holds the payload the hook receives:Connect external tools through the Model Context Protocol (MCP). Declare servers in the mcp_servers block of your settings file. Each server takes a transport: either stdio (with command, args, env) or streamable_http (with url, headers). It also takes enabled, mode, and an optional framing:A server's mode defaults to required. If a required server fails to start, the whole run aborts. Set mode to optional for a server that Muse Code should skip with a warning when it's unavailable.Only connect servers you trust. MCP tools are not sandboxed. Unlike shell commands, an MCP server runs as an ordinary child process (stdio) or a direct network connection, outside the filesystem and network sandbox. Approval still applies, but containment does not. Run Muse Code without a terminal UI. muse exec takes one prompt, runs it to completion, and exits, so you can drive the agent from a script, a job, or a CI pipeline:muse exec prints the agent's output to stdout and returns a process exit code. The code reflects how the run ended, not whether the work is correct: 0 when the turn completes, 1 when it fails or is cancelled (including a --max-model-steps limit), 2 for a usage error, and 130 or 143 on SIGINT or SIGTERM. An agent can finish its turn and still report that the tests fail, and it exits 0. So gate on your own test command, not on Muse Code's exit code alone.Control approvals for automation. A non-interactive run has no one to answer an approval prompt, so choose a posture in advance:- •
--disable-approval: skip approval prompts, but keep the sandbox on to contain what runs. - •
--yolo: disable approval and the sandbox for a fully unattended run. It also trusts the workspace, and loads the checkout's AGENTS.md, rules, and skills. On a fork or pull-request checkout, those are attacker-controlled, so use --yolo only on trusted code in a disposable, isolated container.
Cap a run's work with --max-model-steps so a stuck task can't loop indefinitely.The sandbox in CI requires the OS sandbox to work on the runner. On Linux, that means a working bubblewrap and a non-musl build, because musl artifacts ship without the sandbox helper. On a host without it, every sandboxed shell command aborts as an environment failure. See permissions. Resume and audit non-interactively. Headless runs are sessions like any other. To continue an interrupted job non-interactively, use exec with the session id. muse resume opens the interactive UI and is not a headless surface:Muse Code refuses a workspace mismatch unless you pass --allow-workspace-switch. The export document embeds the CLI version that produced it, so pin the version if you gate on its hash.